import { describe, it, expect, vi, beforeEach } from 'vitest';

vi.mock('../client', () => ({
  default: {
    get: vi.fn(),
    post: vi.fn(),
    patch: vi.fn(),
    delete: vi.fn(),
  },
}));

import {
  getTicketEmailSettings,
  updateTicketEmailSettings,
  testImapConnection,
  testSmtpConnection,
  testEmailConnection,
  fetchEmailsNow,
  getIncomingEmails,
  reprocessIncomingEmail,
  detectEmailProvider,
  getOAuthStatus,
  initiateGoogleOAuth,
  initiateMicrosoftOAuth,
  getOAuthCredentials,
  deleteOAuthCredential,
  type TicketEmailSettings,
  type TicketEmailSettingsUpdate,
  type TestConnectionResult,
  type FetchNowResult,
  type IncomingTicketEmail,
  type EmailProviderDetectResult,
  type OAuthStatusResult,
  type OAuthInitiateResult,
  type OAuthCredential,
} from '../ticketEmailSettings';
import apiClient from '../client';

describe('ticketEmailSettings API', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('getTicketEmailSettings', () => {
    it('should call GET /tickets/email-settings/', async () => {
      const mockSettings: TicketEmailSettings = {
        imap_host: 'imap.gmail.com',
        imap_port: 993,
        imap_use_ssl: true,
        imap_username: 'support@example.com',
        imap_password_masked: '***',
        imap_folder: 'INBOX',
        smtp_host: 'smtp.gmail.com',
        smtp_port: 587,
        smtp_use_tls: true,
        smtp_use_ssl: false,
        smtp_username: 'support@example.com',
        smtp_password_masked: '***',
        smtp_from_email: 'support@example.com',
        smtp_from_name: 'Support Team',
        support_email_address: 'support@example.com',
        support_email_domain: 'example.com',
        is_enabled: true,
        delete_after_processing: false,
        check_interval_seconds: 300,
        max_attachment_size_mb: 10,
        allowed_attachment_types: ['pdf', 'jpg', 'png'],
        last_check_at: '2025-12-07T10:00:00Z',
        last_error: '',
        emails_processed_count: 42,
        is_configured: true,
        is_imap_configured: true,
        is_smtp_configured: true,
        created_at: '2025-01-01T00:00:00Z',
        updated_at: '2025-12-07T10:00:00Z',
      };

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockSettings });

      const result = await getTicketEmailSettings();

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/email-settings/');
      expect(apiClient.get).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockSettings);
    });
  });

  describe('updateTicketEmailSettings', () => {
    it('should call PATCH /tickets/email-settings/ with update data', async () => {
      const updateData: TicketEmailSettingsUpdate = {
        imap_host: 'imap.outlook.com',
        imap_port: 993,
        is_enabled: true,
      };

      const mockResponse: TicketEmailSettings = {
        imap_host: 'imap.outlook.com',
        imap_port: 993,
        imap_use_ssl: true,
        imap_username: 'support@example.com',
        imap_password_masked: '***',
        imap_folder: 'INBOX',
        smtp_host: 'smtp.outlook.com',
        smtp_port: 587,
        smtp_use_tls: true,
        smtp_use_ssl: false,
        smtp_username: 'support@example.com',
        smtp_password_masked: '***',
        smtp_from_email: 'support@example.com',
        smtp_from_name: 'Support Team',
        support_email_address: 'support@example.com',
        support_email_domain: 'example.com',
        is_enabled: true,
        delete_after_processing: false,
        check_interval_seconds: 300,
        max_attachment_size_mb: 10,
        allowed_attachment_types: ['pdf', 'jpg', 'png'],
        last_check_at: null,
        last_error: '',
        emails_processed_count: 0,
        is_configured: true,
        is_imap_configured: true,
        is_smtp_configured: true,
        created_at: '2025-01-01T00:00:00Z',
        updated_at: '2025-12-07T10:00:00Z',
      };

      vi.mocked(apiClient.patch).mockResolvedValue({ data: mockResponse });

      const result = await updateTicketEmailSettings(updateData);

      expect(apiClient.patch).toHaveBeenCalledWith('/tickets/email-settings/', updateData);
      expect(apiClient.patch).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResponse);
    });

    it('should handle password updates', async () => {
      const updateData: TicketEmailSettingsUpdate = {
        imap_password: 'newpassword123',
        smtp_password: 'newsmtppass456',
      };

      const mockResponse: TicketEmailSettings = {
        imap_host: 'imap.gmail.com',
        imap_port: 993,
        imap_use_ssl: true,
        imap_username: 'support@example.com',
        imap_password_masked: '***',
        imap_folder: 'INBOX',
        smtp_host: 'smtp.gmail.com',
        smtp_port: 587,
        smtp_use_tls: true,
        smtp_use_ssl: false,
        smtp_username: 'support@example.com',
        smtp_password_masked: '***',
        smtp_from_email: 'support@example.com',
        smtp_from_name: 'Support Team',
        support_email_address: 'support@example.com',
        support_email_domain: 'example.com',
        is_enabled: true,
        delete_after_processing: false,
        check_interval_seconds: 300,
        max_attachment_size_mb: 10,
        allowed_attachment_types: ['pdf', 'jpg', 'png'],
        last_check_at: null,
        last_error: '',
        emails_processed_count: 0,
        is_configured: true,
        is_imap_configured: true,
        is_smtp_configured: true,
        created_at: '2025-01-01T00:00:00Z',
        updated_at: '2025-12-07T10:00:00Z',
      };

      vi.mocked(apiClient.patch).mockResolvedValue({ data: mockResponse });

      const result = await updateTicketEmailSettings(updateData);

      expect(apiClient.patch).toHaveBeenCalledWith('/tickets/email-settings/', updateData);
      expect(result).toEqual(mockResponse);
    });
  });

  describe('testImapConnection', () => {
    it('should call POST /tickets/email-settings/test-imap/', async () => {
      const mockResult: TestConnectionResult = {
        success: true,
        message: 'IMAP connection successful',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await testImapConnection();

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/email-settings/test-imap/');
      expect(apiClient.post).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResult);
    });

    it('should handle connection failures', async () => {
      const mockResult: TestConnectionResult = {
        success: false,
        message: 'Failed to connect: Invalid credentials',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await testImapConnection();

      expect(result.success).toBe(false);
      expect(result.message).toContain('Failed to connect');
    });
  });

  describe('testSmtpConnection', () => {
    it('should call POST /tickets/email-settings/test-smtp/', async () => {
      const mockResult: TestConnectionResult = {
        success: true,
        message: 'SMTP connection successful',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await testSmtpConnection();

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/email-settings/test-smtp/');
      expect(apiClient.post).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResult);
    });

    it('should handle SMTP connection failures', async () => {
      const mockResult: TestConnectionResult = {
        success: false,
        message: 'SMTP error: Connection refused',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await testSmtpConnection();

      expect(result.success).toBe(false);
      expect(result.message).toContain('Connection refused');
    });
  });

  describe('testEmailConnection (legacy alias)', () => {
    it('should be an alias for testImapConnection', async () => {
      const mockResult: TestConnectionResult = {
        success: true,
        message: 'Connection successful',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await testEmailConnection();

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/email-settings/test-imap/');
      expect(result).toEqual(mockResult);
    });
  });

  describe('fetchEmailsNow', () => {
    it('should call POST /tickets/email-settings/fetch-now/', async () => {
      const mockResult: FetchNowResult = {
        success: true,
        message: 'Successfully processed 5 emails',
        processed: 5,
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await fetchEmailsNow();

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/email-settings/fetch-now/');
      expect(apiClient.post).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResult);
    });

    it('should handle no new emails', async () => {
      const mockResult: FetchNowResult = {
        success: true,
        message: 'No new emails found',
        processed: 0,
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await fetchEmailsNow();

      expect(result.processed).toBe(0);
      expect(result.success).toBe(true);
    });
  });

  describe('getIncomingEmails', () => {
    it('should call GET /tickets/incoming-emails/ without params', async () => {
      const mockEmails: IncomingTicketEmail[] = [
        {
          id: 1,
          message_id: '<msg1@example.com>',
          from_address: 'customer@example.com',
          from_name: 'John Doe',
          to_address: 'support@example.com',
          subject: 'Help needed',
          body_text: 'I need assistance with...',
          extracted_reply: 'I need assistance with...',
          ticket: 123,
          ticket_subject: 'Help needed',
          matched_user: 456,
          ticket_id_from_email: '#123',
          processing_status: 'PROCESSED',
          processing_status_display: 'Processed',
          error_message: '',
          email_date: '2025-12-07T09:00:00Z',
          received_at: '2025-12-07T09:01:00Z',
          processed_at: '2025-12-07T09:02:00Z',
        },
      ];

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockEmails });

      const result = await getIncomingEmails();

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/incoming-emails/', { params: undefined });
      expect(apiClient.get).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockEmails);
    });

    it('should call GET /tickets/incoming-emails/ with status filter', async () => {
      const mockEmails: IncomingTicketEmail[] = [];

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockEmails });

      const result = await getIncomingEmails({ status: 'FAILED' });

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/incoming-emails/', {
        params: { status: 'FAILED' },
      });
      expect(result).toEqual(mockEmails);
    });

    it('should call GET /tickets/incoming-emails/ with ticket filter', async () => {
      const mockEmails: IncomingTicketEmail[] = [];

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockEmails });

      const result = await getIncomingEmails({ ticket: 123 });

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/incoming-emails/', {
        params: { ticket: 123 },
      });
      expect(result).toEqual(mockEmails);
    });

    it('should call GET /tickets/incoming-emails/ with multiple filters', async () => {
      const mockEmails: IncomingTicketEmail[] = [];

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockEmails });

      const result = await getIncomingEmails({ status: 'PROCESSED', ticket: 123 });

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/incoming-emails/', {
        params: { status: 'PROCESSED', ticket: 123 },
      });
      expect(result).toEqual(mockEmails);
    });
  });

  describe('reprocessIncomingEmail', () => {
    it('should call POST /tickets/incoming-emails/:id/reprocess/', async () => {
      const mockResponse = {
        success: true,
        message: 'Email reprocessed successfully',
        comment_id: 789,
        ticket_id: 123,
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await reprocessIncomingEmail(456);

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/incoming-emails/456/reprocess/');
      expect(apiClient.post).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResponse);
    });

    it('should handle reprocessing failures', async () => {
      const mockResponse = {
        success: false,
        message: 'Failed to reprocess: Invalid email format',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await reprocessIncomingEmail(999);

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/incoming-emails/999/reprocess/');
      expect(result.success).toBe(false);
      expect(result.message).toContain('Failed to reprocess');
    });
  });

  describe('detectEmailProvider', () => {
    it('should call POST /tickets/email-settings/detect/ with email', async () => {
      const mockResult: EmailProviderDetectResult = {
        success: true,
        email: 'user@gmail.com',
        domain: 'gmail.com',
        detected: true,
        detected_via: 'domain_lookup',
        provider: 'google',
        display_name: 'Gmail',
        imap_host: 'imap.gmail.com',
        imap_port: 993,
        smtp_host: 'smtp.gmail.com',
        smtp_port: 587,
        oauth_supported: true,
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await detectEmailProvider('user@gmail.com');

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/email-settings/detect/', {
        email: 'user@gmail.com',
      });
      expect(apiClient.post).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResult);
    });

    it('should detect Microsoft provider', async () => {
      const mockResult: EmailProviderDetectResult = {
        success: true,
        email: 'user@outlook.com',
        domain: 'outlook.com',
        detected: true,
        detected_via: 'domain_lookup',
        provider: 'microsoft',
        display_name: 'Outlook.com',
        imap_host: 'outlook.office365.com',
        imap_port: 993,
        smtp_host: 'smtp.office365.com',
        smtp_port: 587,
        oauth_supported: true,
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await detectEmailProvider('user@outlook.com');

      expect(result.provider).toBe('microsoft');
      expect(result.oauth_supported).toBe(true);
    });

    it('should detect custom domain via MX records', async () => {
      const mockResult: EmailProviderDetectResult = {
        success: true,
        email: 'admin@company.com',
        domain: 'company.com',
        detected: true,
        detected_via: 'mx_record',
        provider: 'google',
        display_name: 'Google Workspace',
        oauth_supported: true,
        message: 'Detected Google Workspace via MX records',
        notes: 'Use OAuth for best security',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await detectEmailProvider('admin@company.com');

      expect(result.detected_via).toBe('mx_record');
      expect(result.provider).toBe('google');
    });

    it('should handle unknown provider', async () => {
      const mockResult: EmailProviderDetectResult = {
        success: true,
        email: 'user@custom-server.com',
        domain: 'custom-server.com',
        detected: false,
        provider: 'unknown',
        display_name: 'Unknown Provider',
        oauth_supported: false,
        message: 'Could not auto-detect email provider',
        suggested_imap_port: 993,
        suggested_smtp_port: 587,
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await detectEmailProvider('user@custom-server.com');

      expect(result.detected).toBe(false);
      expect(result.provider).toBe('unknown');
      expect(result.oauth_supported).toBe(false);
    });
  });

  describe('getOAuthStatus', () => {
    it('should call GET /oauth/status/', async () => {
      const mockStatus: OAuthStatusResult = {
        google: { configured: true },
        microsoft: { configured: false },
      };

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockStatus });

      const result = await getOAuthStatus();

      expect(apiClient.get).toHaveBeenCalledWith('/oauth/status/');
      expect(apiClient.get).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockStatus);
    });

    it('should handle no OAuth configured', async () => {
      const mockStatus: OAuthStatusResult = {
        google: { configured: false },
        microsoft: { configured: false },
      };

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockStatus });

      const result = await getOAuthStatus();

      expect(result.google.configured).toBe(false);
      expect(result.microsoft.configured).toBe(false);
    });
  });

  describe('initiateGoogleOAuth', () => {
    it('should call POST /oauth/google/initiate/ with default purpose', async () => {
      const mockResult: OAuthInitiateResult = {
        success: true,
        authorization_url: 'https://accounts.google.com/o/oauth2/auth?...',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await initiateGoogleOAuth();

      expect(apiClient.post).toHaveBeenCalledWith('/oauth/google/initiate/', { purpose: 'email' });
      expect(apiClient.post).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResult);
    });

    it('should call POST /oauth/google/initiate/ with custom purpose', async () => {
      const mockResult: OAuthInitiateResult = {
        success: true,
        authorization_url: 'https://accounts.google.com/o/oauth2/auth?...',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await initiateGoogleOAuth('calendar');

      expect(apiClient.post).toHaveBeenCalledWith('/oauth/google/initiate/', { purpose: 'calendar' });
      expect(result).toEqual(mockResult);
    });

    it('should handle OAuth initiation errors', async () => {
      const mockResult: OAuthInitiateResult = {
        success: false,
        error: 'OAuth client credentials not configured',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await initiateGoogleOAuth();

      expect(result.success).toBe(false);
      expect(result.error).toBeDefined();
    });
  });

  describe('initiateMicrosoftOAuth', () => {
    it('should call POST /oauth/microsoft/initiate/ with default purpose', async () => {
      const mockResult: OAuthInitiateResult = {
        success: true,
        authorization_url: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?...',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await initiateMicrosoftOAuth();

      expect(apiClient.post).toHaveBeenCalledWith('/oauth/microsoft/initiate/', { purpose: 'email' });
      expect(apiClient.post).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResult);
    });

    it('should call POST /oauth/microsoft/initiate/ with custom purpose', async () => {
      const mockResult: OAuthInitiateResult = {
        success: true,
        authorization_url: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?...',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await initiateMicrosoftOAuth('calendar');

      expect(apiClient.post).toHaveBeenCalledWith('/oauth/microsoft/initiate/', {
        purpose: 'calendar',
      });
      expect(result).toEqual(mockResult);
    });

    it('should handle Microsoft OAuth errors', async () => {
      const mockResult: OAuthInitiateResult = {
        success: false,
        error: 'Microsoft OAuth not configured',
      };

      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await initiateMicrosoftOAuth();

      expect(result.success).toBe(false);
      expect(result.error).toBe('Microsoft OAuth not configured');
    });
  });

  describe('getOAuthCredentials', () => {
    it('should call GET /oauth/credentials/', async () => {
      const mockCredentials: OAuthCredential[] = [
        {
          id: 1,
          provider: 'google',
          email: 'support@example.com',
          purpose: 'email',
          is_valid: true,
          is_expired: false,
          last_used_at: '2025-12-07T09:00:00Z',
          last_error: '',
          created_at: '2025-01-01T00:00:00Z',
        },
        {
          id: 2,
          provider: 'microsoft',
          email: 'admin@example.com',
          purpose: 'email',
          is_valid: false,
          is_expired: true,
          last_used_at: '2025-11-01T10:00:00Z',
          last_error: 'Token expired',
          created_at: '2025-01-15T00:00:00Z',
        },
      ];

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockCredentials });

      const result = await getOAuthCredentials();

      expect(apiClient.get).toHaveBeenCalledWith('/oauth/credentials/');
      expect(apiClient.get).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockCredentials);
      expect(result).toHaveLength(2);
    });

    it('should handle empty credentials list', async () => {
      const mockCredentials: OAuthCredential[] = [];

      vi.mocked(apiClient.get).mockResolvedValue({ data: mockCredentials });

      const result = await getOAuthCredentials();

      expect(result).toEqual([]);
      expect(result).toHaveLength(0);
    });
  });

  describe('deleteOAuthCredential', () => {
    it('should call DELETE /oauth/credentials/:id/', async () => {
      const mockResponse = {
        success: true,
        message: 'OAuth credential deleted successfully',
      };

      vi.mocked(apiClient.delete).mockResolvedValue({ data: mockResponse });

      const result = await deleteOAuthCredential(123);

      expect(apiClient.delete).toHaveBeenCalledWith('/oauth/credentials/123/');
      expect(apiClient.delete).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResponse);
    });

    it('should handle deletion of non-existent credential', async () => {
      const mockResponse = {
        success: false,
        message: 'Credential not found',
      };

      vi.mocked(apiClient.delete).mockResolvedValue({ data: mockResponse });

      const result = await deleteOAuthCredential(999);

      expect(apiClient.delete).toHaveBeenCalledWith('/oauth/credentials/999/');
      expect(result.success).toBe(false);
    });
  });
});
