import { describe, it, expect, beforeEach } from 'vitest';
import { useIdentityStore } from '../identity-store';
import type { Identity } from '@/lib/jmap/types';

const makeIdentity = (overrides: Partial<Identity> = {}): Identity => ({
  id: 'id-1',
  name: 'Test User',
  email: 'test@example.com',
  mayDelete: true,
  ...overrides,
});

describe('identity-store', () => {
  beforeEach(() => {
    useIdentityStore.setState({
      identities: [],
      selectedIdentityId: null,
      isLoading: false,
      error: null,
      subAddress: { recentTags: [], tagSuggestions: {} },
    });
  });

  describe('setIdentities', () => {
    it('should set identities list', () => {
      const identities = [makeIdentity(), makeIdentity({ id: 'id-2', email: 'other@test.com' })];
      useIdentityStore.getState().setIdentities(identities);
      expect(useIdentityStore.getState().identities).toHaveLength(2);
    });

    it('should replace existing identities', () => {
      useIdentityStore.getState().setIdentities([makeIdentity()]);
      useIdentityStore.getState().setIdentities([makeIdentity({ id: 'id-new' })]);
      expect(useIdentityStore.getState().identities).toHaveLength(1);
      expect(useIdentityStore.getState().identities[0].id).toBe('id-new');
    });
  });

  describe('addIdentity', () => {
    it('should append identity to list', () => {
      useIdentityStore.getState().addIdentity(makeIdentity());
      expect(useIdentityStore.getState().identities).toHaveLength(1);
    });

    it('should not remove existing identities', () => {
      useIdentityStore.getState().addIdentity(makeIdentity({ id: 'id-1' }));
      useIdentityStore.getState().addIdentity(makeIdentity({ id: 'id-2' }));
      expect(useIdentityStore.getState().identities).toHaveLength(2);
    });
  });

  describe('updateIdentityLocal', () => {
    it('should update matching identity', () => {
      useIdentityStore.getState().setIdentities([makeIdentity({ id: 'id-1', name: 'Old' })]);
      useIdentityStore.getState().updateIdentityLocal('id-1', { name: 'New' });
      expect(useIdentityStore.getState().identities[0].name).toBe('New');
    });

    it('should not modify other identities', () => {
      useIdentityStore.getState().setIdentities([
        makeIdentity({ id: 'id-1', name: 'First' }),
        makeIdentity({ id: 'id-2', name: 'Second' }),
      ]);
      useIdentityStore.getState().updateIdentityLocal('id-1', { name: 'Updated' });
      expect(useIdentityStore.getState().identities[1].name).toBe('Second');
    });

    it('should no-op for non-existent identity', () => {
      useIdentityStore.getState().setIdentities([makeIdentity()]);
      useIdentityStore.getState().updateIdentityLocal('nonexistent', { name: 'X' });
      expect(useIdentityStore.getState().identities[0].name).toBe('Test User');
    });
  });

  describe('removeIdentity', () => {
    it('should remove identity by id', () => {
      useIdentityStore.getState().setIdentities([makeIdentity({ id: 'id-1' }), makeIdentity({ id: 'id-2' })]);
      useIdentityStore.getState().removeIdentity('id-1');
      expect(useIdentityStore.getState().identities).toHaveLength(1);
      expect(useIdentityStore.getState().identities[0].id).toBe('id-2');
    });

    it('should clear selectedIdentityId when removing selected identity', () => {
      useIdentityStore.getState().setIdentities([makeIdentity({ id: 'id-1' })]);
      useIdentityStore.getState().selectIdentity('id-1');
      useIdentityStore.getState().removeIdentity('id-1');
      expect(useIdentityStore.getState().selectedIdentityId).toBeNull();
    });

    it('should preserve selectedIdentityId when removing different identity', () => {
      useIdentityStore.getState().setIdentities([makeIdentity({ id: 'id-1' }), makeIdentity({ id: 'id-2' })]);
      useIdentityStore.getState().selectIdentity('id-1');
      useIdentityStore.getState().removeIdentity('id-2');
      expect(useIdentityStore.getState().selectedIdentityId).toBe('id-1');
    });
  });

  describe('selectIdentity', () => {
    it('should set selectedIdentityId', () => {
      useIdentityStore.getState().selectIdentity('id-1');
      expect(useIdentityStore.getState().selectedIdentityId).toBe('id-1');
    });

    it('should allow null to deselect', () => {
      useIdentityStore.getState().selectIdentity('id-1');
      useIdentityStore.getState().selectIdentity(null);
      expect(useIdentityStore.getState().selectedIdentityId).toBeNull();
    });
  });

  describe('setLoading', () => {
    it('should set loading state', () => {
      useIdentityStore.getState().setLoading(true);
      expect(useIdentityStore.getState().isLoading).toBe(true);
    });

    it('should clear loading state', () => {
      useIdentityStore.getState().setLoading(true);
      useIdentityStore.getState().setLoading(false);
      expect(useIdentityStore.getState().isLoading).toBe(false);
    });
  });

  describe('setError', () => {
    it('should set error message', () => {
      useIdentityStore.getState().setError('Something went wrong');
      expect(useIdentityStore.getState().error).toBe('Something went wrong');
    });

    it('should clear error with null', () => {
      useIdentityStore.getState().setError('error');
      useIdentityStore.getState().setError(null);
      expect(useIdentityStore.getState().error).toBeNull();
    });
  });

  describe('clearIdentities', () => {
    it('should clear identities and selection', () => {
      useIdentityStore.getState().setIdentities([makeIdentity()]);
      useIdentityStore.getState().selectIdentity('id-1');
      useIdentityStore.getState().setError('old error');
      useIdentityStore.getState().clearIdentities();

      const state = useIdentityStore.getState();
      expect(state.identities).toEqual([]);
      expect(state.selectedIdentityId).toBeNull();
      expect(state.error).toBeNull();
    });

    it('should not clear sub-address state', () => {
      useIdentityStore.getState().addRecentTag('shopping');
      useIdentityStore.getState().clearIdentities();
      expect(useIdentityStore.getState().subAddress.recentTags).toContain('shopping');
    });
  });

  describe('addRecentTag', () => {
    it('should add tag to recent tags', () => {
      useIdentityStore.getState().addRecentTag('shopping');
      expect(useIdentityStore.getState().subAddress.recentTags).toEqual(['shopping']);
    });

    it('should prepend new tags', () => {
      useIdentityStore.getState().addRecentTag('first');
      useIdentityStore.getState().addRecentTag('second');
      expect(useIdentityStore.getState().subAddress.recentTags[0]).toBe('second');
    });

    it('should deduplicate tags by moving to front', () => {
      useIdentityStore.getState().addRecentTag('a');
      useIdentityStore.getState().addRecentTag('b');
      useIdentityStore.getState().addRecentTag('a');
      const tags = useIdentityStore.getState().subAddress.recentTags;
      expect(tags).toEqual(['a', 'b']);
    });

    it('should cap at 10 recent tags', () => {
      for (let i = 0; i < 15; i++) {
        useIdentityStore.getState().addRecentTag(`tag-${i}`);
      }
      expect(useIdentityStore.getState().subAddress.recentTags).toHaveLength(10);
      expect(useIdentityStore.getState().subAddress.recentTags[0]).toBe('tag-14');
    });
  });

  describe('addTagSuggestion', () => {
    it('should add suggestion for domain', () => {
      useIdentityStore.getState().addTagSuggestion('example.com', 'promo');
      expect(useIdentityStore.getState().subAddress.tagSuggestions['example.com']).toEqual(['promo']);
    });

    it('should not duplicate existing suggestion', () => {
      useIdentityStore.getState().addTagSuggestion('example.com', 'promo');
      useIdentityStore.getState().addTagSuggestion('example.com', 'promo');
      expect(useIdentityStore.getState().subAddress.tagSuggestions['example.com']).toEqual(['promo']);
    });

    it('should cap at 5 suggestions per domain', () => {
      for (let i = 0; i < 8; i++) {
        useIdentityStore.getState().addTagSuggestion('example.com', `tag-${i}`);
      }
      expect(useIdentityStore.getState().subAddress.tagSuggestions['example.com']).toHaveLength(5);
    });

    it('should keep suggestions separate per domain', () => {
      useIdentityStore.getState().addTagSuggestion('a.com', 'tag-a');
      useIdentityStore.getState().addTagSuggestion('b.com', 'tag-b');
      expect(useIdentityStore.getState().subAddress.tagSuggestions['a.com']).toEqual(['tag-a']);
      expect(useIdentityStore.getState().subAddress.tagSuggestions['b.com']).toEqual(['tag-b']);
    });
  });

  describe('getTagSuggestionsForDomain', () => {
    it('should return suggestions for known domain', () => {
      useIdentityStore.getState().addTagSuggestion('example.com', 'promo');
      expect(useIdentityStore.getState().getTagSuggestionsForDomain('example.com')).toEqual(['promo']);
    });

    it('should return empty array for unknown domain', () => {
      expect(useIdentityStore.getState().getTagSuggestionsForDomain('unknown.com')).toEqual([]);
    });
  });

  describe('clearRecentTags', () => {
    it('should clear recent tags', () => {
      useIdentityStore.getState().addRecentTag('a');
      useIdentityStore.getState().addRecentTag('b');
      useIdentityStore.getState().clearRecentTags();
      expect(useIdentityStore.getState().subAddress.recentTags).toEqual([]);
    });

    it('should not clear tag suggestions', () => {
      useIdentityStore.getState().addTagSuggestion('example.com', 'promo');
      useIdentityStore.getState().clearRecentTags();
      expect(useIdentityStore.getState().subAddress.tagSuggestions['example.com']).toEqual(['promo']);
    });
  });

  describe('persistence', () => {
    it('should only persist subAddress state', () => {
      const { partialize } = (useIdentityStore as unknown as { persist: { getOptions: () => { partialize: (state: Record<string, unknown>) => Record<string, unknown> } } }).persist.getOptions();
      const fullState = {
        identities: [makeIdentity()],
        selectedIdentityId: 'id-1',
        isLoading: true,
        error: 'err',
        subAddress: { recentTags: ['a'], tagSuggestions: {} },
      };
      const persisted = partialize(fullState);
      expect(persisted).toHaveProperty('subAddress');
      expect(persisted).not.toHaveProperty('identities');
      expect(persisted).not.toHaveProperty('selectedIdentityId');
      expect(persisted).not.toHaveProperty('isLoading');
      expect(persisted).not.toHaveProperty('error');
    });
  });
});
