import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor, act } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';

vi.mock('../../api/ticketEmailSettings', () => ({
  getTicketEmailSettings: vi.fn(),
  updateTicketEmailSettings: vi.fn(),
  testImapConnection: vi.fn(),
  testSmtpConnection: vi.fn(),
  fetchEmailsNow: vi.fn(),
  getIncomingEmails: vi.fn(),
  reprocessIncomingEmail: vi.fn(),
  detectEmailProvider: vi.fn(),
  getOAuthStatus: vi.fn(),
  initiateGoogleOAuth: vi.fn(),
  initiateMicrosoftOAuth: vi.fn(),
  getOAuthCredentials: vi.fn(),
  deleteOAuthCredential: vi.fn(),
}));

import {
  useTicketEmailSettings,
  useUpdateTicketEmailSettings,
  useTestImapConnection,
  useTestSmtpConnection,
  useFetchEmailsNow,
  useIncomingEmails,
  useReprocessIncomingEmail,
  useDetectEmailProvider,
  useOAuthStatus,
  useInitiateGoogleOAuth,
  useInitiateMicrosoftOAuth,
  useOAuthCredentials,
  useDeleteOAuthCredential,
} from '../useTicketEmailSettings';
import * as api from '../../api/ticketEmailSettings';

// Create wrapper
const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

  return function Wrapper({ children }: { children: React.ReactNode }) {
    return React.createElement(QueryClientProvider, { client: queryClient }, children);
  };
};

describe('useTicketEmailSettings hooks', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('useTicketEmailSettings', () => {
    it('fetches ticket email settings', async () => {
      const mockSettings = {
        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', 'png', 'jpg'],
        last_check_at: '2025-01-01T12: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-01-01T12:00:00Z',
      };

      vi.mocked(api.getTicketEmailSettings).mockResolvedValue(mockSettings);

      const { result } = renderHook(() => useTicketEmailSettings(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(api.getTicketEmailSettings).toHaveBeenCalledOnce();
      expect(result.current.data).toEqual(mockSettings);
      expect(result.current.data?.imap_host).toBe('imap.gmail.com');
      expect(result.current.data?.smtp_host).toBe('smtp.gmail.com');
    });

    it('handles error when fetching settings fails', async () => {
      vi.mocked(api.getTicketEmailSettings).mockRejectedValue(new Error('Network error'));

      const { result } = renderHook(() => useTicketEmailSettings(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isError).toBe(true);
      });

      expect(result.current.error).toBeDefined();
    });
  });

  describe('useUpdateTicketEmailSettings', () => {
    it('updates ticket email settings', async () => {
      const mockUpdate = {
        imap_host: 'imap.gmail.com',
        imap_port: 993,
        imap_use_ssl: true,
      };

      const mockResponse = {
        ...mockUpdate,
        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',
        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'],
        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-01-01T12:00:00Z',
      };

      vi.mocked(api.updateTicketEmailSettings).mockResolvedValue(mockResponse);

      const { result } = renderHook(() => useUpdateTicketEmailSettings(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync(mockUpdate);
      });

      expect(api.updateTicketEmailSettings).toHaveBeenCalledWith(mockUpdate);
      expect(mutationResult).toEqual(mockResponse);
    });

    it('invalidates query cache on successful update', async () => {
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });

      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

      const mockResponse = {
        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',
        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: [],
        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-01-01T12:00:00Z',
      };

      vi.mocked(api.updateTicketEmailSettings).mockResolvedValue(mockResponse);

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      const { result } = renderHook(() => useUpdateTicketEmailSettings(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync({ is_enabled: true });
      });

      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['ticketEmailSettings'] });
    });

    it('handles validation errors', async () => {
      vi.mocked(api.updateTicketEmailSettings).mockRejectedValue(
        new Error('Invalid IMAP port')
      );

      const { result } = renderHook(() => useUpdateTicketEmailSettings(), {
        wrapper: createWrapper(),
      });

      let error;
      await act(async () => {
        try {
          await result.current.mutateAsync({ imap_port: -1 });
        } catch (e) {
          error = e;
        }
      });

      expect(error).toBeDefined();
      expect(error).toBeInstanceOf(Error);
    });
  });

  describe('useTestImapConnection', () => {
    it('successfully tests IMAP connection', async () => {
      const mockResult = {
        success: true,
        message: 'IMAP connection successful',
      };

      vi.mocked(api.testImapConnection).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useTestImapConnection(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(api.testImapConnection).toHaveBeenCalledOnce();
      expect(mutationResult).toEqual(mockResult);
      expect(mutationResult?.success).toBe(true);
    });

    it('handles IMAP connection failure', async () => {
      const mockResult = {
        success: false,
        message: 'Authentication failed',
      };

      vi.mocked(api.testImapConnection).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useTestImapConnection(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(mutationResult?.success).toBe(false);
      expect(mutationResult?.message).toBe('Authentication failed');
    });
  });

  describe('useTestSmtpConnection', () => {
    it('successfully tests SMTP connection', async () => {
      const mockResult = {
        success: true,
        message: 'SMTP connection successful',
      };

      vi.mocked(api.testSmtpConnection).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useTestSmtpConnection(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(api.testSmtpConnection).toHaveBeenCalledOnce();
      expect(mutationResult).toEqual(mockResult);
      expect(mutationResult?.success).toBe(true);
    });

    it('handles SMTP connection failure', async () => {
      const mockResult = {
        success: false,
        message: 'Connection timeout',
      };

      vi.mocked(api.testSmtpConnection).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useTestSmtpConnection(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(mutationResult?.success).toBe(false);
      expect(mutationResult?.message).toBe('Connection timeout');
    });
  });

  describe('useFetchEmailsNow', () => {
    it('manually fetches emails', async () => {
      const mockResult = {
        success: true,
        message: 'Fetched 5 new emails',
        processed: 5,
      };

      vi.mocked(api.fetchEmailsNow).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useFetchEmailsNow(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(api.fetchEmailsNow).toHaveBeenCalledOnce();
      expect(mutationResult?.processed).toBe(5);
    });

    it('invalidates queries after fetching emails', async () => {
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });

      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

      const mockResult = {
        success: true,
        message: 'Fetched 3 emails',
        processed: 3,
      };

      vi.mocked(api.fetchEmailsNow).mockResolvedValue(mockResult);

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      const { result } = renderHook(() => useFetchEmailsNow(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync();
      });

      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['ticketEmailSettings'] });
      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['incomingTicketEmails'] });
    });

    it('handles fetch error', async () => {
      const mockResult = {
        success: false,
        message: 'IMAP connection failed',
        processed: 0,
      };

      vi.mocked(api.fetchEmailsNow).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useFetchEmailsNow(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(mutationResult?.success).toBe(false);
      expect(mutationResult?.processed).toBe(0);
    });
  });

  describe('useIncomingEmails', () => {
    it('fetches incoming emails without filters', async () => {
      const mockEmails = [
        {
          id: 1,
          message_id: '<msg-1@example.com>',
          from_address: 'customer@example.com',
          from_name: 'John Doe',
          to_address: 'support@example.com',
          subject: 'Help with order',
          body_text: 'I need help with my order',
          extracted_reply: 'I need help',
          ticket: 101,
          ticket_subject: 'Help with order',
          matched_user: 42,
          ticket_id_from_email: '101',
          processing_status: 'PROCESSED' as const,
          processing_status_display: 'Processed',
          error_message: '',
          email_date: '2025-01-01T10:00:00Z',
          received_at: '2025-01-01T10:05:00Z',
          processed_at: '2025-01-01T10:06:00Z',
        },
      ];

      vi.mocked(api.getIncomingEmails).mockResolvedValue(mockEmails);

      const { result } = renderHook(() => useIncomingEmails(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(api.getIncomingEmails).toHaveBeenCalledWith(undefined);
      expect(result.current.data).toHaveLength(1);
      expect(result.current.data?.[0].from_address).toBe('customer@example.com');
    });

    it('fetches incoming emails with status filter', async () => {
      const mockEmails = [
        {
          id: 2,
          message_id: '<msg-2@example.com>',
          from_address: 'user@example.com',
          from_name: 'Jane Smith',
          to_address: 'support@example.com',
          subject: 'Failed email',
          body_text: 'Test',
          extracted_reply: 'Test',
          ticket: null,
          ticket_subject: '',
          matched_user: null,
          ticket_id_from_email: '',
          processing_status: 'FAILED' as const,
          processing_status_display: 'Failed',
          error_message: 'Could not parse ticket ID',
          email_date: '2025-01-01T11:00:00Z',
          received_at: '2025-01-01T11:05:00Z',
          processed_at: null,
        },
      ];

      vi.mocked(api.getIncomingEmails).mockResolvedValue(mockEmails);

      const { result } = renderHook(() => useIncomingEmails({ status: 'FAILED' }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(api.getIncomingEmails).toHaveBeenCalledWith({ status: 'FAILED' });
      expect(result.current.data?.[0].processing_status).toBe('FAILED');
    });

    it('fetches incoming emails with ticket filter', async () => {
      vi.mocked(api.getIncomingEmails).mockResolvedValue([]);

      const { result } = renderHook(() => useIncomingEmails({ ticket: 101 }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(api.getIncomingEmails).toHaveBeenCalledWith({ ticket: 101 });
    });

    it('fetches incoming emails with multiple filters', async () => {
      vi.mocked(api.getIncomingEmails).mockResolvedValue([]);

      const { result } = renderHook(
        () => useIncomingEmails({ status: 'PROCESSED', ticket: 101 }),
        { wrapper: createWrapper() }
      );

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(api.getIncomingEmails).toHaveBeenCalledWith({
        status: 'PROCESSED',
        ticket: 101,
      });
    });
  });

  describe('useReprocessIncomingEmail', () => {
    it('reprocesses failed incoming email', async () => {
      const mockResult = {
        success: true,
        message: 'Email reprocessed successfully',
        comment_id: 456,
        ticket_id: 789,
      };

      vi.mocked(api.reprocessIncomingEmail).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useReprocessIncomingEmail(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync(123);
      });

      expect(api.reprocessIncomingEmail).toHaveBeenCalledWith(123);
      expect(mutationResult?.success).toBe(true);
      expect(mutationResult?.ticket_id).toBe(789);
    });

    it('invalidates incoming emails cache on success', async () => {
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });

      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

      const mockResult = {
        success: true,
        message: 'Reprocessed',
      };

      vi.mocked(api.reprocessIncomingEmail).mockResolvedValue(mockResult);

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      const { result } = renderHook(() => useReprocessIncomingEmail(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync(123);
      });

      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['incomingTicketEmails'] });
    });

    it('handles reprocessing failure', async () => {
      const mockResult = {
        success: false,
        message: 'Still cannot parse ticket ID',
      };

      vi.mocked(api.reprocessIncomingEmail).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useReprocessIncomingEmail(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync(123);
      });

      expect(mutationResult?.success).toBe(false);
    });
  });

  describe('useDetectEmailProvider', () => {
    it('detects Gmail provider', async () => {
      const mockResult = {
        success: true,
        email: 'user@gmail.com',
        domain: 'gmail.com',
        detected: true,
        detected_via: 'domain_lookup' as const,
        provider: 'google' as const,
        display_name: 'Gmail',
        imap_host: 'imap.gmail.com',
        imap_port: 993,
        smtp_host: 'smtp.gmail.com',
        smtp_port: 587,
        oauth_supported: true,
        notes: 'OAuth recommended',
      };

      vi.mocked(api.detectEmailProvider).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useDetectEmailProvider(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync('user@gmail.com');
      });

      expect(api.detectEmailProvider).toHaveBeenCalledWith('user@gmail.com');
      expect(mutationResult?.provider).toBe('google');
      expect(mutationResult?.oauth_supported).toBe(true);
    });

    it('detects Microsoft provider', async () => {
      const mockResult = {
        success: true,
        email: 'user@outlook.com',
        domain: 'outlook.com',
        detected: true,
        detected_via: 'domain_lookup' as const,
        provider: 'microsoft' as const,
        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(api.detectEmailProvider).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useDetectEmailProvider(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync('user@outlook.com');
      });

      expect(mutationResult?.provider).toBe('microsoft');
    });

    it('detects custom domain using MX records', async () => {
      const mockResult = {
        success: true,
        email: 'user@company.com',
        domain: 'company.com',
        detected: true,
        detected_via: 'mx_record' as const,
        provider: 'google' as const,
        display_name: 'Google Workspace',
        oauth_supported: true,
        message: 'Detected Google Workspace via MX records',
      };

      vi.mocked(api.detectEmailProvider).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useDetectEmailProvider(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync('user@company.com');
      });

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

    it('handles unknown provider', async () => {
      const mockResult = {
        success: true,
        email: 'user@custom.com',
        domain: 'custom.com',
        detected: false,
        provider: 'unknown' as const,
        display_name: 'Unknown Provider',
        oauth_supported: false,
        message: 'Could not auto-detect provider',
      };

      vi.mocked(api.detectEmailProvider).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useDetectEmailProvider(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync('user@custom.com');
      });

      expect(mutationResult?.detected).toBe(false);
      expect(mutationResult?.provider).toBe('unknown');
    });
  });

  describe('useOAuthStatus', () => {
    it('fetches OAuth status for both providers', async () => {
      const mockStatus = {
        google: { configured: true },
        microsoft: { configured: false },
      };

      vi.mocked(api.getOAuthStatus).mockResolvedValue(mockStatus);

      const { result } = renderHook(() => useOAuthStatus(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(api.getOAuthStatus).toHaveBeenCalledOnce();
      expect(result.current.data?.google.configured).toBe(true);
      expect(result.current.data?.microsoft.configured).toBe(false);
    });

    it('handles both providers not configured', async () => {
      const mockStatus = {
        google: { configured: false },
        microsoft: { configured: false },
      };

      vi.mocked(api.getOAuthStatus).mockResolvedValue(mockStatus);

      const { result } = renderHook(() => useOAuthStatus(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

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

  describe('useInitiateGoogleOAuth', () => {
    it('initiates Google OAuth flow with default purpose', async () => {
      const mockResult = {
        success: true,
        authorization_url: 'https://accounts.google.com/o/oauth2/auth?...',
      };

      vi.mocked(api.initiateGoogleOAuth).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useInitiateGoogleOAuth(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(api.initiateGoogleOAuth).toHaveBeenCalledWith('email');
      expect(mutationResult?.success).toBe(true);
      expect(mutationResult?.authorization_url).toContain('google.com');
    });

    it('initiates Google OAuth flow with custom purpose', async () => {
      const mockResult = {
        success: true,
        authorization_url: 'https://accounts.google.com/o/oauth2/auth?...',
      };

      vi.mocked(api.initiateGoogleOAuth).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useInitiateGoogleOAuth(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync('calendar');
      });

      expect(api.initiateGoogleOAuth).toHaveBeenCalledWith('calendar');
    });

    it('handles OAuth initiation error', async () => {
      const mockResult = {
        success: false,
        error: 'OAuth not configured',
      };

      vi.mocked(api.initiateGoogleOAuth).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useInitiateGoogleOAuth(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(mutationResult?.success).toBe(false);
      expect(mutationResult?.error).toBe('OAuth not configured');
    });
  });

  describe('useInitiateMicrosoftOAuth', () => {
    it('initiates Microsoft OAuth flow with default purpose', async () => {
      const mockResult = {
        success: true,
        authorization_url: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?...',
      };

      vi.mocked(api.initiateMicrosoftOAuth).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useInitiateMicrosoftOAuth(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(api.initiateMicrosoftOAuth).toHaveBeenCalledWith('email');
      expect(mutationResult?.success).toBe(true);
      expect(mutationResult?.authorization_url).toContain('microsoft');
    });

    it('initiates Microsoft OAuth flow with custom purpose', async () => {
      const mockResult = {
        success: true,
        authorization_url: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?...',
      };

      vi.mocked(api.initiateMicrosoftOAuth).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useInitiateMicrosoftOAuth(), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        await result.current.mutateAsync('calendar');
      });

      expect(api.initiateMicrosoftOAuth).toHaveBeenCalledWith('calendar');
    });

    it('handles OAuth initiation error', async () => {
      const mockResult = {
        success: false,
        error: 'OAuth not configured',
      };

      vi.mocked(api.initiateMicrosoftOAuth).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useInitiateMicrosoftOAuth(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync();
      });

      expect(mutationResult?.success).toBe(false);
      expect(mutationResult?.error).toBe('OAuth not configured');
    });
  });

  describe('useOAuthCredentials', () => {
    it('fetches OAuth credentials list', async () => {
      const mockCredentials = [
        {
          id: 1,
          provider: 'google' as const,
          email: 'support@example.com',
          purpose: 'email',
          is_valid: true,
          is_expired: false,
          last_used_at: '2025-01-01T12:00:00Z',
          last_error: '',
          created_at: '2025-01-01T00:00:00Z',
        },
        {
          id: 2,
          provider: 'microsoft' as const,
          email: 'admin@example.com',
          purpose: 'calendar',
          is_valid: false,
          is_expired: true,
          last_used_at: '2024-12-01T10:00:00Z',
          last_error: 'Token expired',
          created_at: '2024-11-01T00:00:00Z',
        },
      ];

      vi.mocked(api.getOAuthCredentials).mockResolvedValue(mockCredentials);

      const { result } = renderHook(() => useOAuthCredentials(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(api.getOAuthCredentials).toHaveBeenCalledOnce();
      expect(result.current.data).toHaveLength(2);
      expect(result.current.data?.[0].provider).toBe('google');
      expect(result.current.data?.[1].is_expired).toBe(true);
    });

    it('handles empty credentials list', async () => {
      vi.mocked(api.getOAuthCredentials).mockResolvedValue([]);

      const { result } = renderHook(() => useOAuthCredentials(), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data).toHaveLength(0);
    });
  });

  describe('useDeleteOAuthCredential', () => {
    it('deletes OAuth credential', async () => {
      const mockResult = {
        success: true,
        message: 'Credential deleted successfully',
      };

      vi.mocked(api.deleteOAuthCredential).mockResolvedValue(mockResult);

      const { result } = renderHook(() => useDeleteOAuthCredential(), {
        wrapper: createWrapper(),
      });

      let mutationResult;
      await act(async () => {
        mutationResult = await result.current.mutateAsync(123);
      });

      expect(api.deleteOAuthCredential).toHaveBeenCalledWith(123);
      expect(mutationResult?.success).toBe(true);
    });

    it('invalidates credentials cache on successful delete', async () => {
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });

      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

      const mockResult = {
        success: true,
        message: 'Deleted',
      };

      vi.mocked(api.deleteOAuthCredential).mockResolvedValue(mockResult);

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      const { result } = renderHook(() => useDeleteOAuthCredential(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync(123);
      });

      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['oauthCredentials'] });
    });

    it('handles delete error', async () => {
      vi.mocked(api.deleteOAuthCredential).mockRejectedValue(
        new Error('Credential not found')
      );

      const { result } = renderHook(() => useDeleteOAuthCredential(), {
        wrapper: createWrapper(),
      });

      let error;
      await act(async () => {
        try {
          await result.current.mutateAsync(999);
        } catch (e) {
          error = e;
        }
      });

      expect(error).toBeDefined();
      expect(error).toBeInstanceOf(Error);
    });
  });
});
