/**
 * Tests for Settings Service validation enhancement (Story 4.2)
 *
 * Tests the enhanced connection validation with detailed results.
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { SettingsService, ProviderSettings } from './settings-service';
import { ApiErrorType } from '@/types/settings';
import { useSettingsStore } from '@/store/use-settings';

// Mock LLMService
vi.mock('./llm-service', () => ({
  LLMService: {
    validateConnection: vi.fn(),
  },
}));

import { LLMService } from './llm-service';

describe('Settings Service - Connection Validation (Story 4.2)', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('validateConnectionWithDetails', () => {
    const testSettings: ProviderSettings = {
      apiKey: 'sk-test-key',
      baseUrl: 'https://api.openai.com/v1',
      modelName: 'gpt-4o',
    };

    it('should return success for valid connection', async () => {
      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: true,
      });

      const result = await SettingsService.validateConnectionWithDetails(testSettings);

      expect(result.isValid).toBe(true);
      expect(result.errorType).toBeUndefined();
      expect(LLMService.validateConnection).toHaveBeenCalledWith(
        testSettings.baseUrl,
        testSettings.apiKey,
        testSettings.modelName
      );
    });

    it('should return detailed error for invalid API key', async () => {
      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: false,
        errorType: ApiErrorType.INVALID_KEY,
        errorMessage: 'Your API key appears to be invalid or expired.',
        suggestedAction: 'Please check your API key in your provider dashboard and try again.',
      });

      const result = await SettingsService.validateConnectionWithDetails(testSettings);

      expect(result.isValid).toBe(false);
      expect(result.errorType).toBe(ApiErrorType.INVALID_KEY);
      expect(result.errorMessage).toBeTruthy();
      expect(result.suggestedAction).toBeTruthy();
    });

    it('should validate structure before connection', async () => {
      const invalidSettings: ProviderSettings = {
        apiKey: '',
        baseUrl: 'not-a-url',
        modelName: '',
      };

      const result = await SettingsService.validateConnectionWithDetails(invalidSettings);

      expect(result.isValid).toBe(false);
      expect(result.errorMessage).toContain('API Key is required');
      expect(LLMService.validateConnection).not.toHaveBeenCalled();
    });

    it('should return network error details', async () => {
      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: false,
        errorType: ApiErrorType.NETWORK_ERROR,
        errorMessage: 'Unable to reach the API server.',
        suggestedAction: 'Please check your internet connection and try again.',
      });

      const result = await SettingsService.validateConnectionWithDetails(testSettings);

      expect(result.isValid).toBe(false);
      expect(result.errorType).toBe(ApiErrorType.NETWORK_ERROR);
    });

    it('should return model not found error', async () => {
      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: false,
        errorType: ApiErrorType.MODEL_NOT_FOUND,
        errorMessage: 'The specified model was not found.',
        suggestedAction: 'Please check the model name for typos.',
      });

      const result = await SettingsService.validateConnectionWithDetails(testSettings);

      expect(result.isValid).toBe(false);
      expect(result.errorType).toBe(ApiErrorType.MODEL_NOT_FOUND);
    });

    it('should return quota exceeded error', async () => {
      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: false,
        errorType: ApiErrorType.QUOTA_EXCEEDED,
        errorMessage: 'Your API quota has been exceeded.',
        suggestedAction: 'Please check your billing status.',
      });

      const result = await SettingsService.validateConnectionWithDetails(testSettings);

      expect(result.isValid).toBe(false);
      expect(result.errorType).toBe(ApiErrorType.QUOTA_EXCEEDED);
    });
  });

  describe('saveProviderSettingsWithValidation', () => {
    const mockActions = {
      setApiKey: vi.fn(),
      setBaseUrl: vi.fn(),
      setModelName: vi.fn(),
      clearSettings: vi.fn(),
    };

    beforeEach(() => {
      // Reset mock calls
      mockActions.setApiKey.mockClear();
      mockActions.setBaseUrl.mockClear();
      mockActions.setModelName.mockClear();
      mockActions.clearSettings.mockClear();

      // Mock useSettingsStore.getState to return our mock actions
      (useSettingsStore as any).getState = vi.fn(() => ({
        apiKey: '',
        baseUrl: '',
        modelName: '',
        isConfigured: false,
        actions: mockActions,
      }));
    });

    it('should save settings when validation passes', async () => {
      const validSettings: ProviderSettings = {
        apiKey: 'sk-test-key',
        baseUrl: 'https://api.openai.com/v1',
        modelName: 'gpt-4o',
      };

      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: true,
      });

      const result = await SettingsService.saveProviderSettingsWithValidation(validSettings);

      expect(result).toBeUndefined(); // Success returns undefined
      expect(mockActions.setApiKey).toHaveBeenCalledWith(validSettings.apiKey);
      expect(mockActions.setBaseUrl).toHaveBeenCalledWith(validSettings.baseUrl);
      expect(mockActions.setModelName).toHaveBeenCalledWith(validSettings.modelName);
    });

    it('should not save settings when structure validation fails', async () => {
      const invalidSettings: ProviderSettings = {
        apiKey: '',
        baseUrl: 'not-a-url',
        modelName: '',
      };

      const result = await SettingsService.saveProviderSettingsWithValidation(invalidSettings);

      expect(result).toBeDefined();
      expect(result?.isValid).toBe(false);
      expect(mockActions.setApiKey).not.toHaveBeenCalled();
      expect(mockActions.setBaseUrl).not.toHaveBeenCalled();
      expect(mockActions.setModelName).not.toHaveBeenCalled();
    });

    it('should not save settings when connection validation fails', async () => {
      const invalidConnectionSettings: ProviderSettings = {
        apiKey: 'sk-invalid-key',
        baseUrl: 'https://api.openai.com/v1',
        modelName: 'gpt-4o',
      };

      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: false,
        errorType: ApiErrorType.INVALID_KEY,
        errorMessage: 'Your API key appears to be invalid or expired.',
        suggestedAction: 'Please check your API key in your provider dashboard.',
      });

      const result = await SettingsService.saveProviderSettingsWithValidation(invalidConnectionSettings);

      expect(result).toBeDefined();
      expect(result?.isValid).toBe(false);
      expect(result?.errorType).toBe(ApiErrorType.INVALID_KEY);
      expect(mockActions.setApiKey).not.toHaveBeenCalled();
    });

    it('should return validation error with suggested action', async () => {
      const invalidSettings: ProviderSettings = {
        apiKey: 'sk-invalid',
        baseUrl: 'https://api.openai.com/v1',
        modelName: 'invalid-model',
      };

      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: false,
        errorType: ApiErrorType.MODEL_NOT_FOUND,
        errorMessage: 'The specified model was not found.',
        suggestedAction: 'Please check the model name for typos.',
      });

      const result = await SettingsService.saveProviderSettingsWithValidation(invalidSettings);

      expect(result?.isValid).toBe(false);
      expect(result?.errorType).toBe(ApiErrorType.MODEL_NOT_FOUND);
      expect(result?.suggestedAction).toBeTruthy();
    });
  });

  describe('validateProviderConnection - backward compatibility', () => {
    beforeEach(() => {
      // Mock useSettingsStore.getState for backward compatibility tests
      (useSettingsStore as any).getState = vi.fn(() => ({
        apiKey: 'sk-test-key',
        baseUrl: 'https://api.openai.com/v1',
        modelName: 'gpt-4o',
        isConfigured: true,
        actions: {
          setApiKey: vi.fn(),
          setBaseUrl: vi.fn(),
          setModelName: vi.fn(),
          clearSettings: vi.fn(),
        },
      }));
    });

    it('should still work with existing interface', async () => {
      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: true,
      });

      const result = await SettingsService.validateProviderConnection();

      expect(result.isValid).toBe(true);
    });

    it('should map detailed error to simple error string', async () => {
      vi.mocked(LLMService.validateConnection).mockResolvedValueOnce({
        isValid: false,
        errorType: ApiErrorType.INVALID_KEY,
        errorMessage: 'Your API key appears to be invalid or expired.',
        suggestedAction: 'Please check your API key.',
      });

      const result = await SettingsService.validateProviderConnection();

      expect(result.isValid).toBe(false);
      expect(result.error).toBeTruthy();
    });
  });
});
