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';

// Mock the ticket email addresses API module
vi.mock('../../api/ticketEmailAddresses', () => ({
  getTicketEmailAddresses: vi.fn(),
  getTicketEmailAddress: vi.fn(),
  createTicketEmailAddress: vi.fn(),
  updateTicketEmailAddress: vi.fn(),
  deleteTicketEmailAddress: vi.fn(),
  testImapConnection: vi.fn(),
  testSmtpConnection: vi.fn(),
  fetchEmailsNow: vi.fn(),
  setAsDefault: vi.fn(),
}));

import {
  useTicketEmailAddresses,
  useTicketEmailAddress,
  useCreateTicketEmailAddress,
  useUpdateTicketEmailAddress,
  useDeleteTicketEmailAddress,
  useTestImapConnection,
  useTestSmtpConnection,
  useFetchEmailsNow,
  useSetAsDefault,
} from '../useTicketEmailAddresses';
import * as ticketEmailAddressesApi from '../../api/ticketEmailAddresses';

// Create wrapper with QueryClient
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('useTicketEmailAddresses hooks', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('useTicketEmailAddresses', () => {
    it('fetches all ticket email addresses', async () => {
      const mockAddresses = [
        {
          id: 1,
          display_name: 'Support',
          email_address: 'support@example.com',
          color: '#FF5733',
          is_active: true,
          is_default: true,
          last_check_at: '2025-12-07T10:00:00Z',
          emails_processed_count: 42,
          created_at: '2025-12-01T10:00:00Z',
          updated_at: '2025-12-07T10:00:00Z',
        },
        {
          id: 2,
          display_name: 'Sales',
          email_address: 'sales@example.com',
          color: '#33A1FF',
          is_active: true,
          is_default: false,
          last_check_at: null,
          emails_processed_count: 0,
          created_at: '2025-12-02T10:00:00Z',
          updated_at: '2025-12-02T10:00:00Z',
        },
      ];
      vi.mocked(ticketEmailAddressesApi.getTicketEmailAddresses).mockResolvedValue(
        mockAddresses as any
      );

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

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

      expect(ticketEmailAddressesApi.getTicketEmailAddresses).toHaveBeenCalled();
      expect(result.current.data).toHaveLength(2);
      expect(result.current.data?.[0]).toEqual(mockAddresses[0]);
      expect(result.current.data?.[1]).toEqual(mockAddresses[1]);
    });

    it('handles empty list', async () => {
      vi.mocked(ticketEmailAddressesApi.getTicketEmailAddresses).mockResolvedValue([]);

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

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

      expect(result.current.data).toEqual([]);
    });

    it('handles API errors', async () => {
      vi.mocked(ticketEmailAddressesApi.getTicketEmailAddresses).mockRejectedValue(
        new Error('API Error')
      );

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

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

      expect(result.current.error).toBeInstanceOf(Error);
    });
  });

  describe('useTicketEmailAddress', () => {
    it('fetches single ticket email address by id', async () => {
      const mockAddress = {
        id: 1,
        tenant: 5,
        tenant_name: 'Example Business',
        display_name: 'Support',
        email_address: 'support@example.com',
        color: '#FF5733',
        imap_host: 'imap.gmail.com',
        imap_port: 993,
        imap_use_ssl: true,
        imap_username: 'support@example.com',
        imap_folder: 'INBOX',
        smtp_host: 'smtp.gmail.com',
        smtp_port: 587,
        smtp_use_tls: true,
        smtp_use_ssl: false,
        smtp_username: 'support@example.com',
        is_active: true,
        is_default: true,
        last_check_at: '2025-12-07T10:00:00Z',
        last_error: null,
        emails_processed_count: 42,
        created_at: '2025-12-01T10:00:00Z',
        updated_at: '2025-12-07T10:00:00Z',
        is_imap_configured: true,
        is_smtp_configured: true,
        is_fully_configured: true,
      };
      vi.mocked(ticketEmailAddressesApi.getTicketEmailAddress).mockResolvedValue(
        mockAddress as any
      );

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

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

      expect(ticketEmailAddressesApi.getTicketEmailAddress).toHaveBeenCalledWith(1);
      expect(result.current.data).toEqual(mockAddress);
    });

    it('does not fetch when id is 0', async () => {
      const { result } = renderHook(() => useTicketEmailAddress(0), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(result.current.isLoading).toBe(false);
      });

      expect(ticketEmailAddressesApi.getTicketEmailAddress).not.toHaveBeenCalled();
    });

    it('handles API errors', async () => {
      vi.mocked(ticketEmailAddressesApi.getTicketEmailAddress).mockRejectedValue(
        new Error('Not found')
      );

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

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

      expect(result.current.error).toBeInstanceOf(Error);
    });

    it('handles addresses with last_error', async () => {
      const mockAddress = {
        id: 3,
        tenant: 5,
        tenant_name: 'Example Business',
        display_name: 'Broken Email',
        email_address: 'broken@example.com',
        color: '#FF0000',
        imap_host: 'imap.example.com',
        imap_port: 993,
        imap_use_ssl: true,
        imap_username: 'broken@example.com',
        imap_folder: 'INBOX',
        smtp_host: 'smtp.example.com',
        smtp_port: 587,
        smtp_use_tls: true,
        smtp_use_ssl: false,
        smtp_username: 'broken@example.com',
        is_active: true,
        is_default: false,
        last_check_at: '2025-12-07T10:00:00Z',
        last_error: 'Authentication failed',
        emails_processed_count: 0,
        created_at: '2025-12-01T10:00:00Z',
        updated_at: '2025-12-07T10:00:00Z',
        is_imap_configured: true,
        is_smtp_configured: true,
        is_fully_configured: true,
      };
      vi.mocked(ticketEmailAddressesApi.getTicketEmailAddress).mockResolvedValue(
        mockAddress as any
      );

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

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

      expect(result.current.data?.last_error).toBe('Authentication failed');
    });
  });

  describe('useCreateTicketEmailAddress', () => {
    it('creates a new ticket email address', async () => {
      const newAddress = {
        display_name: 'Info',
        email_address: 'info@example.com',
        color: '#00FF00',
        imap_host: 'imap.gmail.com',
        imap_port: 993,
        imap_use_ssl: true,
        imap_username: 'info@example.com',
        imap_password: 'password123',
        imap_folder: 'INBOX',
        smtp_host: 'smtp.gmail.com',
        smtp_port: 587,
        smtp_use_tls: true,
        smtp_use_ssl: false,
        smtp_username: 'info@example.com',
        smtp_password: 'password123',
        is_active: true,
        is_default: false,
      };
      const mockResponse = { id: 10, ...newAddress };
      vi.mocked(ticketEmailAddressesApi.createTicketEmailAddress).mockResolvedValue(
        mockResponse as any
      );

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

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

      expect(ticketEmailAddressesApi.createTicketEmailAddress).toHaveBeenCalledWith(newAddress);
    });

    it('invalidates query cache on success', async () => {
      const newAddress = {
        display_name: 'Test',
        email_address: 'test@example.com',
        color: '#0000FF',
        imap_host: 'imap.example.com',
        imap_port: 993,
        imap_use_ssl: true,
        imap_username: 'test@example.com',
        imap_password: 'pass',
        imap_folder: 'INBOX',
        smtp_host: 'smtp.example.com',
        smtp_port: 587,
        smtp_use_tls: true,
        smtp_use_ssl: false,
        smtp_username: 'test@example.com',
        smtp_password: 'pass',
        is_active: true,
        is_default: false,
      };
      const mockResponse = { id: 11, ...newAddress };
      vi.mocked(ticketEmailAddressesApi.createTicketEmailAddress).mockResolvedValue(
        mockResponse as any
      );

      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });
      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

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

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

    it('handles creation errors', async () => {
      const newAddress = {
        display_name: 'Error',
        email_address: 'error@example.com',
        color: '#FF0000',
        imap_host: 'imap.example.com',
        imap_port: 993,
        imap_use_ssl: true,
        imap_username: 'error@example.com',
        imap_password: 'pass',
        imap_folder: 'INBOX',
        smtp_host: 'smtp.example.com',
        smtp_port: 587,
        smtp_use_tls: true,
        smtp_use_ssl: false,
        smtp_username: 'error@example.com',
        smtp_password: 'pass',
        is_active: true,
        is_default: false,
      };
      vi.mocked(ticketEmailAddressesApi.createTicketEmailAddress).mockRejectedValue(
        new Error('Validation error')
      );

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

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

      expect(caughtError).toBeInstanceOf(Error);
    });
  });

  describe('useUpdateTicketEmailAddress', () => {
    it('updates an existing ticket email address', async () => {
      const updates = {
        display_name: 'Updated Support',
        color: '#FF00FF',
      };
      const mockResponse = { id: 1, ...updates };
      vi.mocked(ticketEmailAddressesApi.updateTicketEmailAddress).mockResolvedValue(
        mockResponse as any
      );

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

      await act(async () => {
        await result.current.mutateAsync({ id: 1, data: updates });
      });

      expect(ticketEmailAddressesApi.updateTicketEmailAddress).toHaveBeenCalledWith(1, updates);
    });

    it('updates email configuration', async () => {
      const updates = {
        imap_host: 'imap.newserver.com',
        imap_port: 993,
        imap_password: 'newpassword',
      };
      const mockResponse = { id: 2, ...updates };
      vi.mocked(ticketEmailAddressesApi.updateTicketEmailAddress).mockResolvedValue(
        mockResponse as any
      );

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

      await act(async () => {
        await result.current.mutateAsync({ id: 2, data: updates });
      });

      expect(ticketEmailAddressesApi.updateTicketEmailAddress).toHaveBeenCalledWith(2, updates);
    });

    it('invalidates queries on success', async () => {
      const updates = { display_name: 'New Name' };
      const mockResponse = { id: 3, ...updates };
      vi.mocked(ticketEmailAddressesApi.updateTicketEmailAddress).mockResolvedValue(
        mockResponse as any
      );

      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });
      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

      await act(async () => {
        await result.current.mutateAsync({ id: 3, data: updates });
      });

      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['ticketEmailAddresses'] });
      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['ticketEmailAddresses', 3] });
    });

    it('handles update errors', async () => {
      vi.mocked(ticketEmailAddressesApi.updateTicketEmailAddress).mockRejectedValue(
        new Error('Update failed')
      );

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

      let caughtError;
      await act(async () => {
        try {
          await result.current.mutateAsync({ id: 999, data: { display_name: 'Test' } });
        } catch (error) {
          caughtError = error;
        }
      });

      expect(caughtError).toBeInstanceOf(Error);
    });
  });

  describe('useDeleteTicketEmailAddress', () => {
    it('deletes a ticket email address', async () => {
      vi.mocked(ticketEmailAddressesApi.deleteTicketEmailAddress).mockResolvedValue(undefined);

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

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

      expect(ticketEmailAddressesApi.deleteTicketEmailAddress).toHaveBeenCalledWith(5);
    });

    it('invalidates query cache on success', async () => {
      vi.mocked(ticketEmailAddressesApi.deleteTicketEmailAddress).mockResolvedValue(undefined);

      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });
      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

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

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

    it('handles deletion errors', async () => {
      vi.mocked(ticketEmailAddressesApi.deleteTicketEmailAddress).mockRejectedValue(
        new Error('Cannot delete default address')
      );

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

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

      expect(caughtError).toBeInstanceOf(Error);
    });
  });

  describe('useTestImapConnection', () => {
    it('tests IMAP connection successfully', async () => {
      const mockResponse = {
        success: true,
        message: 'IMAP connection successful',
      };
      vi.mocked(ticketEmailAddressesApi.testImapConnection).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync(1);
        expect(response).toEqual(mockResponse);
      });

      expect(ticketEmailAddressesApi.testImapConnection).toHaveBeenCalledWith(1);
    });

    it('handles IMAP connection failure', async () => {
      const mockResponse = {
        success: false,
        message: 'Authentication failed',
      };
      vi.mocked(ticketEmailAddressesApi.testImapConnection).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync(2);
        expect(response.success).toBe(false);
        expect(response.message).toBe('Authentication failed');
      });
    });

    it('handles API errors during IMAP test', async () => {
      vi.mocked(ticketEmailAddressesApi.testImapConnection).mockRejectedValue(
        new Error('Network error')
      );

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

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

      expect(caughtError).toBeInstanceOf(Error);
    });
  });

  describe('useTestSmtpConnection', () => {
    it('tests SMTP connection successfully', async () => {
      const mockResponse = {
        success: true,
        message: 'SMTP connection successful',
      };
      vi.mocked(ticketEmailAddressesApi.testSmtpConnection).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync(1);
        expect(response).toEqual(mockResponse);
      });

      expect(ticketEmailAddressesApi.testSmtpConnection).toHaveBeenCalledWith(1);
    });

    it('handles SMTP connection failure', async () => {
      const mockResponse = {
        success: false,
        message: 'Could not connect to SMTP server',
      };
      vi.mocked(ticketEmailAddressesApi.testSmtpConnection).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync(2);
        expect(response.success).toBe(false);
        expect(response.message).toBe('Could not connect to SMTP server');
      });
    });

    it('handles API errors during SMTP test', async () => {
      vi.mocked(ticketEmailAddressesApi.testSmtpConnection).mockRejectedValue(
        new Error('Timeout')
      );

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

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

      expect(caughtError).toBeInstanceOf(Error);
    });
  });

  describe('useFetchEmailsNow', () => {
    it('fetches emails successfully', async () => {
      const mockResponse = {
        success: true,
        message: 'Successfully fetched 5 new emails',
        processed: 5,
        errors: 0,
      };
      vi.mocked(ticketEmailAddressesApi.fetchEmailsNow).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync(1);
        expect(response).toEqual(mockResponse);
      });

      expect(ticketEmailAddressesApi.fetchEmailsNow).toHaveBeenCalledWith(1);
    });

    it('handles no new emails', async () => {
      const mockResponse = {
        success: true,
        message: 'No new emails',
        processed: 0,
        errors: 0,
      };
      vi.mocked(ticketEmailAddressesApi.fetchEmailsNow).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync(2);
        expect(response.processed).toBe(0);
      });
    });

    it('handles errors during email fetch', async () => {
      const mockResponse = {
        success: false,
        message: 'Failed to fetch emails',
        processed: 2,
        errors: 3,
      };
      vi.mocked(ticketEmailAddressesApi.fetchEmailsNow).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync(3);
        expect(response.success).toBe(false);
        expect(response.errors).toBe(3);
      });
    });

    it('invalidates queries on success', async () => {
      const mockResponse = {
        success: true,
        message: 'Fetched 3 emails',
        processed: 3,
        errors: 0,
      };
      vi.mocked(ticketEmailAddressesApi.fetchEmailsNow).mockResolvedValue(mockResponse);

      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });
      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

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

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

    it('handles API errors during fetch', async () => {
      vi.mocked(ticketEmailAddressesApi.fetchEmailsNow).mockRejectedValue(
        new Error('Connection timeout')
      );

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

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

      expect(caughtError).toBeInstanceOf(Error);
    });
  });

  describe('useSetAsDefault', () => {
    it('sets email address as default successfully', async () => {
      const mockResponse = {
        success: true,
        message: 'Email address set as default',
      };
      vi.mocked(ticketEmailAddressesApi.setAsDefault).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync(1);
        expect(response).toEqual(mockResponse);
      });

      expect(ticketEmailAddressesApi.setAsDefault).toHaveBeenCalledWith(1);
    });

    it('invalidates query cache on success', async () => {
      const mockResponse = {
        success: true,
        message: 'Email address set as default',
      };
      vi.mocked(ticketEmailAddressesApi.setAsDefault).mockResolvedValue(mockResponse);

      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });
      const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

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

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

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

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

    it('handles errors when setting default', async () => {
      vi.mocked(ticketEmailAddressesApi.setAsDefault).mockRejectedValue(
        new Error('Cannot set inactive address as default')
      );

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

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

      expect(caughtError).toBeInstanceOf(Error);
    });

    it('handles setting already default address', async () => {
      const mockResponse = {
        success: true,
        message: 'Email address is already the default',
      };
      vi.mocked(ticketEmailAddressesApi.setAsDefault).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync(1);
        expect(response.message).toBe('Email address is already the default');
      });
    });
  });
});
