/**
 * Tests for Multi-Provider Settings Store
 * Story 4.4: Provider Switching
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import { useSettingsStore } from './use-settings';
import type { ProviderProfile } from '@/types/settings';

describe('Multi-Provider Settings Store', () => {
  beforeEach(() => {
    // Reset store state before each test
    useSettingsStore.getState().actions.clearSettings();
    // Clear migration state for testing
    useSettingsStore.setState({
      providerMigrationState: {
        hasMigrated: false,
      },
      savedProviders: [],
      activeProviderId: null,
    });
  });

  describe('Initial state', () => {
    it('should have empty savedProviders array', () => {
      const { savedProviders } = useSettingsStore.getState();
      expect(savedProviders).toEqual([]);
    });

    it('should have null activeProviderId', () => {
      const { activeProviderId } = useSettingsStore.getState();
      expect(activeProviderId).toBeNull();
    });

    it('should have migration state initialized to not migrated', () => {
      const { providerMigrationState } = useSettingsStore.getState();
      expect(providerMigrationState.hasMigrated).toBe(false);
    });
  });

  describe('addProvider action', () => {
    it('should add a new provider profile', () => {
      const { actions } = useSettingsStore.getState();

      const newProfile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Test Provider',
        baseUrl: 'https://api.test.com/v1',
        apiKey: 'sk-test-key',
        modelName: 'test-model',
      };

      act(() => {
        const id = actions.addProvider(newProfile);
        expect(id).toBeTruthy();
        expect(id).toMatch(/^provider-\d+-[a-z0-9]+$/);
      });

      const { savedProviders } = useSettingsStore.getState();
      expect(savedProviders).toHaveLength(1);
      expect(savedProviders[0].name).toBe('Test Provider');
      expect(savedProviders[0].baseUrl).toBe('https://api.test.com/v1');
      expect(savedProviders[0].apiKey).toBe('sk-test-key');
      expect(savedProviders[0].modelName).toBe('test-model');
      expect(savedProviders[0].id).toBeTruthy();
      expect(savedProviders[0].createdAt).toBeTruthy();
      expect(savedProviders[0].updatedAt).toBeTruthy();
    });

    it('should auto-select first provider as active', () => {
      const { actions } = useSettingsStore.getState();

      const newProfile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'First Provider',
        baseUrl: 'https://api.first.com/v1',
        apiKey: 'sk-first',
        modelName: 'first-model',
      };

      act(() => {
        actions.addProvider(newProfile);
      });

      const { activeProviderId, savedProviders } = useSettingsStore.getState();
      expect(activeProviderId).toBe(savedProviders[0].id);
    });

    it('should NOT auto-select when adding subsequent providers', () => {
      const { actions } = useSettingsStore.getState();

      const firstProfile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'First Provider',
        baseUrl: 'https://api.first.com/v1',
        apiKey: 'sk-first',
        modelName: 'first-model',
      };

      const secondProfile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Second Provider',
        baseUrl: 'https://api.second.com/v1',
        apiKey: 'sk-second',
        modelName: 'second-model',
      };

      let firstId: string;
      let secondId: string;

      act(() => {
        firstId = actions.addProvider(firstProfile);
        secondId = actions.addProvider(secondProfile);
      });

      const { activeProviderId, savedProviders } = useSettingsStore.getState();
      expect(activeProviderId).toBe(firstId);
      expect(activeProviderId).not.toBe(secondId);
      expect(savedProviders).toHaveLength(2);
    });

    it('should generate unique IDs for each provider', () => {
      const { actions } = useSettingsStore.getState();

      const profile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Test Provider',
        baseUrl: 'https://api.test.com/v1',
        apiKey: 'sk-test',
        modelName: 'test-model',
      };

      let id1: string;
      let id2: string;

      act(() => {
        id1 = actions.addProvider(profile);
        id2 = actions.addProvider({ ...profile, name: 'Test Provider 2' });
      });

      expect(id1).not.toBe(id2);
    });
  });

  describe('removeProvider action', () => {
    it('should remove a provider by ID', () => {
      const { actions } = useSettingsStore.getState();

      const profile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Test Provider',
        baseUrl: 'https://api.test.com/v1',
        apiKey: 'sk-test',
        modelName: 'test-model',
      };

      let id: string;

      act(() => {
        id = actions.addProvider(profile);
        expect(useSettingsStore.getState().savedProviders).toHaveLength(1);
      });

      act(() => {
        actions.removeProvider(id);
      });

      const { savedProviders } = useSettingsStore.getState();
      expect(savedProviders).toHaveLength(0);
    });

    it('should auto-select another provider when removing active provider', () => {
      const { actions } = useSettingsStore.getState();

      const profile1: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Provider 1',
        baseUrl: 'https://api.p1.com/v1',
        apiKey: 'sk-p1',
        modelName: 'model1',
      };

      const profile2: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Provider 2',
        baseUrl: 'https://api.p2.com/v1',
        apiKey: 'sk-p2',
        modelName: 'model2',
      };

      let id1: string;
      let id2: string;

      act(() => {
        id1 = actions.addProvider(profile1);
        id2 = actions.addProvider(profile2);
        expect(useSettingsStore.getState().activeProviderId).toBe(id1);
      });

      act(() => {
        actions.removeProvider(id1);
      });

      const { activeProviderId, savedProviders } = useSettingsStore.getState();
      expect(activeProviderId).toBe(id2);
      expect(savedProviders).toHaveLength(1);
    });

    it('should set activeProviderId to null when removing last provider', () => {
      const { actions } = useSettingsStore.getState();

      const profile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Only Provider',
        baseUrl: 'https://api.only.com/v1',
        apiKey: 'sk-only',
        modelName: 'only-model',
      };

      let id: string;

      act(() => {
        id = actions.addProvider(profile);
        expect(useSettingsStore.getState().activeProviderId).toBe(id);
      });

      act(() => {
        actions.removeProvider(id);
      });

      const { activeProviderId, savedProviders } = useSettingsStore.getState();
      expect(activeProviderId).toBeNull();
      expect(savedProviders).toHaveLength(0);
    });

    it('should not change activeProviderId when removing non-active provider', () => {
      const { actions } = useSettingsStore.getState();

      const profile1: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Active Provider',
        baseUrl: 'https://api.active.com/v1',
        apiKey: 'sk-active',
        modelName: 'active-model',
      };

      const profile2: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Other Provider',
        baseUrl: 'https://api.other.com/v1',
        apiKey: 'sk-other',
        modelName: 'other-model',
      };

      let id1: string;
      let id2: string;

      act(() => {
        id1 = actions.addProvider(profile1);
        id2 = actions.addProvider(profile2);
      });

      const activeIdBefore = useSettingsStore.getState().activeProviderId;

      act(() => {
        actions.removeProvider(id2);
      });

      const { activeProviderId } = useSettingsStore.getState();
      expect(activeProviderId).toBe(activeIdBefore);
      expect(activeProviderId).toBe(id1);
    });
  });

  describe('setActiveProvider action', () => {
    it('should set the active provider by ID', () => {
      const { actions } = useSettingsStore.getState();

      const profile1: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Provider 1',
        baseUrl: 'https://api.p1.com/v1',
        apiKey: 'sk-p1',
        modelName: 'model1',
      };

      const profile2: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Provider 2',
        baseUrl: 'https://api.p2.com/v1',
        apiKey: 'sk-p2',
        modelName: 'model2',
      };

      let id1: string;
      let id2: string;

      act(() => {
        id1 = actions.addProvider(profile1);
        id2 = actions.addProvider(profile2);
      });

      act(() => {
        actions.setActiveProvider(id2);
      });

      const { activeProviderId } = useSettingsStore.getState();
      expect(activeProviderId).toBe(id2);
    });

    it('should allow switching between providers', () => {
      const { actions } = useSettingsStore.getState();

      const profile1: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Provider 1',
        baseUrl: 'https://api.p1.com/v1',
        apiKey: 'sk-p1',
        modelName: 'model1',
      };

      const profile2: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Provider 2',
        baseUrl: 'https://api.p2.com/v1',
        apiKey: 'sk-p2',
        modelName: 'model2',
      };

      let id1: string;
      let id2: string;

      act(() => {
        id1 = actions.addProvider(profile1);
        id2 = actions.addProvider(profile2);
      });

      // Start with provider 1 active
      expect(useSettingsStore.getState().activeProviderId).toBe(id1);

      // Switch to provider 2
      act(() => {
        actions.setActiveProvider(id2);
      });
      expect(useSettingsStore.getState().activeProviderId).toBe(id2);

      // Switch back to provider 1
      act(() => {
        actions.setActiveProvider(id1);
      });
      expect(useSettingsStore.getState().activeProviderId).toBe(id1);
    });
  });

  describe('updateProvider action', () => {
    it('should update provider fields', () => {
      const { actions } = useSettingsStore.getState();

      const profile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Original Name',
        baseUrl: 'https://api.original.com/v1',
        apiKey: 'sk-original',
        modelName: 'original-model',
      };

      let id: string;

      act(() => {
        id = actions.addProvider(profile);
      });

      act(() => {
        actions.updateProvider(id, {
          name: 'Updated Name',
          modelName: 'updated-model',
        });
      });

      const { savedProviders } = useSettingsStore.getState();
      const updated = savedProviders.find(p => p.id === id);
      expect(updated?.name).toBe('Updated Name');
      expect(updated?.modelName).toBe('updated-model');
      expect(updated?.baseUrl).toBe('https://api.original.com/v1'); // Unchanged
      expect(updated?.apiKey).toBe('sk-original'); // Unchanged
      expect(updated?.createdAt).toBeTruthy(); // Original createdAt preserved
      expect(updated?.updatedAt).toBeTruthy(); // updatedAt updated
      // Note: updatedAt should be different from createdAt IF we waited between add and update
      // In fast execution, they might be the same - that's fine
    });

    it('should update updatedAt timestamp', async () => {
      const { actions } = useSettingsStore.getState();

      const profile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Test Provider',
        baseUrl: 'https://api.test.com/v1',
        apiKey: 'sk-test',
        modelName: 'test-model',
      };

      let id: string;
      let originalUpdatedAt: string;

      act(() => {
        id = actions.addProvider(profile);
        originalUpdatedAt = useSettingsStore.getState().savedProviders[0].updatedAt;
        // Small delay to ensure timestamp difference
        return new Promise(resolve => setTimeout(resolve, 10));
      });

      act(() => {
        actions.updateProvider(id, { name: 'Updated' });
      });

      const { savedProviders } = useSettingsStore.getState();
      const updated = savedProviders.find(p => p.id === id);
      expect(updated?.updatedAt).toBeTruthy();
      // Note: In fast execution, updatedAt might be the same if not enough time passed
      // The important thing is that updatedAt was set (truthy check above)
    });

    it('should handle updating non-existent provider gracefully', () => {
      const { actions } = useSettingsStore.getState();

      expect(() => {
        act(() => {
          actions.updateProvider('non-existent-id', { name: 'Test' });
        });
      }).not.toThrow();

      const { savedProviders } = useSettingsStore.getState();
      expect(savedProviders).toHaveLength(0);
    });
  });

  describe('completeMigration action', () => {
    it('should mark migration as complete with timestamp', () => {
      const { actions } = useSettingsStore.getState();

      act(() => {
        actions.completeMigration();
      });

      const { providerMigrationState } = useSettingsStore.getState();
      expect(providerMigrationState.hasMigrated).toBe(true);
      expect(providerMigrationState.migratedAt).toBeTruthy();
      expect(providerMigrationState.migratedAt).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);
    });
  });

  describe('getActiveProvider computed', () => {
    it('should return the active provider profile', () => {
      const { actions, getActiveProvider } = useSettingsStore.getState();

      const profile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Active Provider',
        baseUrl: 'https://api.active.com/v1',
        apiKey: 'sk-active',
        modelName: 'active-model',
      };

      let id: string;

      act(() => {
        id = actions.addProvider(profile);
      });

      const active = getActiveProvider();
      expect(active).toBeTruthy();
      expect(active?.id).toBe(id);
      expect(active?.name).toBe('Active Provider');
      expect(active?.baseUrl).toBe('https://api.active.com/v1');
      expect(active?.apiKey).toBe('sk-active');
      expect(active?.modelName).toBe('active-model');
    });

    it('should return null when no provider is active', () => {
      const { getActiveProvider } = useSettingsStore.getState();
      const active = getActiveProvider();
      expect(active).toBeNull();
    });

    it('should return null when activeProviderId points to non-existent provider', () => {
      const { actions, getActiveProvider } = useSettingsStore.getState();

      act(() => {
        actions.setActiveProvider('non-existent-id');
      });

      const active = getActiveProvider();
      expect(active).toBeNull();
    });
  });

  describe('API Key encoding/decoding with profiles', () => {
    it('should encode API keys before persisting', () => {
      const { actions } = useSettingsStore.getState();

      const profile: Omit<ProviderProfile, 'id' | 'createdAt' | 'updatedAt'> = {
        name: 'Test Provider',
        baseUrl: 'https://api.test.com/v1',
        apiKey: 'sk-test-key-12345',
        modelName: 'test-model',
      };

      let id: string;

      act(() => {
        id = actions.addProvider(profile);
      });

      // Check localStorage (simulated by checking store state)
      const { savedProviders } = useSettingsStore.getState();
      expect(savedProviders[0].apiKey).toBe('sk-test-key-12345'); // Decoded in memory
    });
  });
});
