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 tickets API module
vi.mock('../../api/tickets', () => ({
  getTickets: vi.fn(),
  getTicket: vi.fn(),
  createTicket: vi.fn(),
  updateTicket: vi.fn(),
  deleteTicket: vi.fn(),
  getTicketComments: vi.fn(),
  createTicketComment: vi.fn(),
  getTicketTemplates: vi.fn(),
  getTicketTemplate: vi.fn(),
  getCannedResponses: vi.fn(),
  refreshTicketEmails: vi.fn(),
}));

import {
  useTickets,
  useTicket,
  useCreateTicket,
  useUpdateTicket,
  useDeleteTicket,
  useTicketComments,
  useCreateTicketComment,
  useTicketTemplates,
  useTicketTemplate,
  useCannedResponses,
  useRefreshTicketEmails,
} from '../useTickets';
import * as ticketsApi from '../../api/tickets';

// 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('useTickets hooks', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('useTickets', () => {
    it('fetches tickets and transforms snake_case to camelCase', async () => {
      const mockTickets = [
        {
          id: 1,
          tenant: 5,
          creator: 10,
          creator_email: 'creator@example.com',
          creator_full_name: 'John Doe',
          assignee: 20,
          assignee_email: 'assignee@example.com',
          assignee_full_name: 'Jane Smith',
          ticket_type: 'SUPPORT',
          status: 'OPEN',
          priority: 'HIGH',
          subject: 'Test Ticket',
          description: 'Test description',
          category: 'BILLING',
          related_appointment_id: 100,
          due_at: '2025-12-08T12:00:00Z',
          first_response_at: '2025-12-07T10:00:00Z',
          is_overdue: false,
          created_at: '2025-12-07T09:00:00Z',
          updated_at: '2025-12-07T11:00:00Z',
          resolved_at: null,
          comments: 3,
        },
      ];
      vi.mocked(ticketsApi.getTickets).mockResolvedValue(mockTickets as any);

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

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

      expect(ticketsApi.getTickets).toHaveBeenCalledWith({});
      expect(result.current.data).toHaveLength(1);
      expect(result.current.data?.[0]).toEqual({
        id: '1',
        tenant: '5',
        creator: '10',
        creatorEmail: 'creator@example.com',
        creatorFullName: 'John Doe',
        assignee: '20',
        assigneeEmail: 'assignee@example.com',
        assigneeFullName: 'Jane Smith',
        ticketType: 'SUPPORT',
        status: 'OPEN',
        priority: 'HIGH',
        subject: 'Test Ticket',
        description: 'Test description',
        category: 'BILLING',
        relatedAppointmentId: 100,
        dueAt: '2025-12-08T12:00:00Z',
        firstResponseAt: '2025-12-07T10:00:00Z',
        isOverdue: false,
        createdAt: '2025-12-07T09:00:00Z',
        updatedAt: '2025-12-07T11:00:00Z',
        resolvedAt: null,
        comments: 3,
      });
    });

    it('handles undefined optional fields correctly', async () => {
      const mockTickets = [
        {
          id: 2,
          tenant: null,
          creator: 15,
          creator_email: 'test@example.com',
          creator_full_name: 'Test User',
          assignee: null,
          assignee_email: null,
          assignee_full_name: null,
          ticket_type: 'FEATURE_REQUEST',
          status: 'NEW',
          priority: 'LOW',
          subject: 'Feature Request',
          description: 'New feature',
          category: 'GENERAL',
          related_appointment_id: null,
          due_at: null,
          first_response_at: null,
          is_overdue: false,
          created_at: '2025-12-07T09:00:00Z',
          updated_at: '2025-12-07T09:00:00Z',
          resolved_at: null,
          comments: 0,
        },
      ];
      vi.mocked(ticketsApi.getTickets).mockResolvedValue(mockTickets as any);

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

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

      expect(result.current.data?.[0]).toMatchObject({
        id: '2',
        tenant: undefined,
        assignee: undefined,
        relatedAppointmentId: undefined,
      });
    });

    it('applies status filter', async () => {
      vi.mocked(ticketsApi.getTickets).mockResolvedValue([]);

      renderHook(() => useTickets({ status: 'OPEN' }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(ticketsApi.getTickets).toHaveBeenCalledWith({ status: 'OPEN' });
      });
    });

    it('applies priority filter', async () => {
      vi.mocked(ticketsApi.getTickets).mockResolvedValue([]);

      renderHook(() => useTickets({ priority: 'HIGH' }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(ticketsApi.getTickets).toHaveBeenCalledWith({ priority: 'HIGH' });
      });
    });

    it('applies category filter', async () => {
      vi.mocked(ticketsApi.getTickets).mockResolvedValue([]);

      renderHook(() => useTickets({ category: 'BILLING' }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(ticketsApi.getTickets).toHaveBeenCalledWith({ category: 'BILLING' });
      });
    });

    it('applies ticketType filter', async () => {
      vi.mocked(ticketsApi.getTickets).mockResolvedValue([]);

      renderHook(() => useTickets({ ticketType: 'SUPPORT' }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(ticketsApi.getTickets).toHaveBeenCalledWith({ ticketType: 'SUPPORT' });
      });
    });

    it('applies assignee filter', async () => {
      vi.mocked(ticketsApi.getTickets).mockResolvedValue([]);

      renderHook(() => useTickets({ assignee: '42' }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(ticketsApi.getTickets).toHaveBeenCalledWith({ assignee: '42' });
      });
    });

    it('applies multiple filters', async () => {
      vi.mocked(ticketsApi.getTickets).mockResolvedValue([]);

      renderHook(() => useTickets({ status: 'IN_PROGRESS', priority: 'HIGH', assignee: '10' }), {
        wrapper: createWrapper(),
      });

      await waitFor(() => {
        expect(ticketsApi.getTickets).toHaveBeenCalledWith({
          status: 'IN_PROGRESS',
          priority: 'HIGH',
          assignee: '10',
        });
      });
    });
  });

  describe('useTicket', () => {
    it('fetches single ticket by id and transforms data', async () => {
      const mockTicket = {
        id: 5,
        tenant: 3,
        creator: 12,
        creator_email: 'user@example.com',
        creator_full_name: 'User Name',
        assignee: 25,
        assignee_email: 'staff@example.com',
        assignee_full_name: 'Staff Member',
        ticket_type: 'BUG',
        status: 'IN_PROGRESS',
        priority: 'MEDIUM',
        subject: 'Bug Report',
        description: 'Bug details',
        category: 'TECHNICAL',
        related_appointment_id: 200,
        due_at: '2025-12-10T12:00:00Z',
        first_response_at: '2025-12-07T11:00:00Z',
        is_overdue: false,
        created_at: '2025-12-07T10:00:00Z',
        updated_at: '2025-12-07T12:00:00Z',
        resolved_at: null,
        comments: 5,
      };
      vi.mocked(ticketsApi.getTicket).mockResolvedValue(mockTicket as any);

      const { result } = renderHook(() => useTicket('5'), {
        wrapper: createWrapper(),
      });

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

      expect(ticketsApi.getTicket).toHaveBeenCalledWith('5');
      expect(result.current.data).toEqual({
        id: '5',
        tenant: '3',
        creator: '12',
        creatorEmail: 'user@example.com',
        creatorFullName: 'User Name',
        assignee: '25',
        assigneeEmail: 'staff@example.com',
        assigneeFullName: 'Staff Member',
        ticketType: 'BUG',
        status: 'IN_PROGRESS',
        priority: 'MEDIUM',
        subject: 'Bug Report',
        description: 'Bug details',
        category: 'TECHNICAL',
        relatedAppointmentId: 200,
        dueAt: '2025-12-10T12:00:00Z',
        firstResponseAt: '2025-12-07T11:00:00Z',
        isOverdue: false,
        createdAt: '2025-12-07T10:00:00Z',
        updatedAt: '2025-12-07T12:00:00Z',
        resolvedAt: null,
        comments: 5,
      });
    });

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

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

      expect(ticketsApi.getTicket).not.toHaveBeenCalled();
    });

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

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

      expect(ticketsApi.getTicket).not.toHaveBeenCalled();
    });
  });

  describe('useCreateTicket', () => {
    it('creates ticket with camelCase to snake_case transformation', async () => {
      const mockResponse = { id: 10 };
      vi.mocked(ticketsApi.createTicket).mockResolvedValue(mockResponse as any);

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

      await act(async () => {
        await result.current.mutateAsync({
          ticketType: 'SUPPORT',
          subject: 'New Ticket',
          description: 'Ticket description',
          priority: 'HIGH',
          status: 'NEW',
          category: 'BILLING',
          assignee: '15',
        });
      });

      expect(ticketsApi.createTicket).toHaveBeenCalledWith({
        ticketType: 'SUPPORT',
        subject: 'New Ticket',
        description: 'Ticket description',
        priority: 'HIGH',
        status: 'NEW',
        category: 'BILLING',
        assignee: '15',
        ticket_type: 'SUPPORT',
      });
    });

    it('sets assignee to null when not provided', async () => {
      const mockResponse = { id: 11 };
      vi.mocked(ticketsApi.createTicket).mockResolvedValue(mockResponse as any);

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

      await act(async () => {
        await result.current.mutateAsync({
          ticketType: 'BUG',
          subject: 'Bug Report',
          description: 'Description',
          priority: 'LOW',
          status: 'NEW',
          category: 'TECHNICAL',
        });
      });

      expect(ticketsApi.createTicket).toHaveBeenCalledWith(
        expect.objectContaining({
          assignee: null,
        })
      );
    });

    it('invalidates tickets query on success', async () => {
      const mockResponse = { id: 12 };
      vi.mocked(ticketsApi.createTicket).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(() => useCreateTicket(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync({
          ticketType: 'SUPPORT',
          subject: 'Test',
          description: 'Test',
          priority: 'MEDIUM',
          status: 'NEW',
          category: 'GENERAL',
        });
      });

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

  describe('useUpdateTicket', () => {
    it('updates ticket with field transformation', async () => {
      const mockResponse = { id: 20 };
      vi.mocked(ticketsApi.updateTicket).mockResolvedValue(mockResponse as any);

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

      await act(async () => {
        await result.current.mutateAsync({
          id: '20',
          updates: {
            subject: 'Updated Subject',
            priority: 'HIGH',
            ticketType: 'SUPPORT',
            assignee: '30',
          },
        });
      });

      expect(ticketsApi.updateTicket).toHaveBeenCalledWith('20', {
        subject: 'Updated Subject',
        priority: 'HIGH',
        ticketType: 'SUPPORT',
        assignee: '30',
        ticket_type: 'SUPPORT',
      });
    });

    it('sets assignee to null when not provided', async () => {
      const mockResponse = { id: 21 };
      vi.mocked(ticketsApi.updateTicket).mockResolvedValue(mockResponse as any);

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

      await act(async () => {
        await result.current.mutateAsync({
          id: '21',
          updates: {
            status: 'RESOLVED',
          },
        });
      });

      expect(ticketsApi.updateTicket).toHaveBeenCalledWith('21', {
        status: 'RESOLVED',
        ticket_type: undefined,
        assignee: null,
      });
    });

    it('invalidates tickets queries on success', async () => {
      const mockResponse = { id: 22 };
      vi.mocked(ticketsApi.updateTicket).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(() => useUpdateTicket(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync({
          id: '22',
          updates: { status: 'IN_PROGRESS' },
        });
      });

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

  describe('useDeleteTicket', () => {
    it('deletes ticket by id', async () => {
      vi.mocked(ticketsApi.deleteTicket).mockResolvedValue(undefined);

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

      await act(async () => {
        const returnedId = await result.current.mutateAsync('30');
        expect(returnedId).toBe('30');
      });

      expect(ticketsApi.deleteTicket).toHaveBeenCalledWith('30');
    });

    it('invalidates tickets query on success', async () => {
      vi.mocked(ticketsApi.deleteTicket).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(() => useDeleteTicket(), { wrapper });

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

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

  describe('useTicketComments', () => {
    it('fetches comments and transforms snake_case to camelCase', async () => {
      const mockComments = [
        {
          id: 100,
          ticket: 50,
          author: 15,
          comment_text: 'This is a comment',
          is_internal: false,
          created_at: '2025-12-07T10:00:00Z',
        },
        {
          id: 101,
          ticket: 50,
          author: 20,
          comment_text: 'Internal note',
          is_internal: true,
          created_at: '2025-12-07T11:00:00Z',
        },
      ];
      vi.mocked(ticketsApi.getTicketComments).mockResolvedValue(mockComments as any);

      const { result } = renderHook(() => useTicketComments('50'), {
        wrapper: createWrapper(),
      });

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

      expect(ticketsApi.getTicketComments).toHaveBeenCalledWith('50');
      expect(result.current.data).toHaveLength(2);
      expect(result.current.data?.[0]).toMatchObject({
        id: '100',
        ticket: '50',
        author: '15',
        commentText: 'This is a comment',
        isInternal: false,
        createdAt: expect.any(String),
      });
      expect(result.current.data?.[1]).toMatchObject({
        id: '101',
        ticket: '50',
        author: '20',
        commentText: 'Internal note',
        isInternal: true,
      });
    });

    it('transforms created_at to ISO string', async () => {
      const mockComments = [
        {
          id: 102,
          ticket: 51,
          author: 16,
          comment_text: 'Test comment',
          is_internal: false,
          created_at: '2025-12-07T10:30:00Z',
        },
      ];
      vi.mocked(ticketsApi.getTicketComments).mockResolvedValue(mockComments as any);

      const { result } = renderHook(() => useTicketComments('51'), {
        wrapper: createWrapper(),
      });

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

      expect(result.current.data?.[0].createdAt).toBe('2025-12-07T10:30:00.000Z');
    });

    it('returns empty array when ticketId is undefined', async () => {
      const { result } = renderHook(() => useTicketComments(undefined), {
        wrapper: createWrapper(),
      });

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

      expect(ticketsApi.getTicketComments).not.toHaveBeenCalled();
    });

    it('does not fetch when ticketId is empty string', async () => {
      const { result } = renderHook(() => useTicketComments(''), {
        wrapper: createWrapper(),
      });

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

      expect(ticketsApi.getTicketComments).not.toHaveBeenCalled();
    });
  });

  describe('useCreateTicketComment', () => {
    it('creates comment with field transformation', async () => {
      const mockResponse = { id: 105 };
      vi.mocked(ticketsApi.createTicketComment).mockResolvedValue(mockResponse as any);

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

      await act(async () => {
        await result.current.mutateAsync({
          ticketId: '60',
          commentData: {
            commentText: 'New comment',
            isInternal: false,
          },
        });
      });

      expect(ticketsApi.createTicketComment).toHaveBeenCalledWith('60', {
        commentText: 'New comment',
        isInternal: false,
        comment_text: 'New comment',
        is_internal: false,
      });
    });

    it('handles internal comments', async () => {
      const mockResponse = { id: 106 };
      vi.mocked(ticketsApi.createTicketComment).mockResolvedValue(mockResponse as any);

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

      await act(async () => {
        await result.current.mutateAsync({
          ticketId: '61',
          commentData: {
            commentText: 'Internal note',
            isInternal: true,
          },
        });
      });

      expect(ticketsApi.createTicketComment).toHaveBeenCalledWith('61', {
        commentText: 'Internal note',
        isInternal: true,
        comment_text: 'Internal note',
        is_internal: true,
      });
    });

    it('invalidates queries on success', async () => {
      const mockResponse = { id: 107 };
      vi.mocked(ticketsApi.createTicketComment).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(() => useCreateTicketComment(), { wrapper });

      await act(async () => {
        await result.current.mutateAsync({
          ticketId: '62',
          commentData: {
            commentText: 'Comment',
            isInternal: false,
          },
        });
      });

      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['ticketComments', '62'] });
      expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['tickets', '62'] });
    });
  });

  describe('useTicketTemplates', () => {
    it('fetches templates and transforms snake_case to camelCase', async () => {
      const mockTemplates = [
        {
          id: 200,
          tenant: 10,
          name: 'Support Template',
          description: 'Template for support tickets',
          ticket_type: 'SUPPORT',
          category: 'GENERAL',
          default_priority: 'MEDIUM',
          subject_template: 'Support: {{issue}}',
          description_template: 'Issue: {{description}}',
          is_active: true,
          created_at: '2025-12-01T10:00:00Z',
        },
      ];
      vi.mocked(ticketsApi.getTicketTemplates).mockResolvedValue(mockTemplates as any);

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

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

      expect(ticketsApi.getTicketTemplates).toHaveBeenCalled();
      expect(result.current.data).toHaveLength(1);
      expect(result.current.data?.[0]).toEqual({
        id: '200',
        tenant: '10',
        name: 'Support Template',
        description: 'Template for support tickets',
        ticketType: 'SUPPORT',
        category: 'GENERAL',
        defaultPriority: 'MEDIUM',
        subjectTemplate: 'Support: {{issue}}',
        descriptionTemplate: 'Issue: {{description}}',
        isActive: true,
        createdAt: '2025-12-01T10:00:00Z',
      });
    });

    it('handles undefined tenant', async () => {
      const mockTemplates = [
        {
          id: 201,
          tenant: null,
          name: 'Global Template',
          description: 'Global template',
          ticket_type: 'BUG',
          category: 'TECHNICAL',
          default_priority: 'HIGH',
          subject_template: 'Bug: {{title}}',
          description_template: 'Details: {{details}}',
          is_active: true,
          created_at: '2025-12-01T10:00:00Z',
        },
      ];
      vi.mocked(ticketsApi.getTicketTemplates).mockResolvedValue(mockTemplates as any);

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

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

      expect(result.current.data?.[0].tenant).toBeUndefined();
    });
  });

  describe('useTicketTemplate', () => {
    it('fetches single template by id and transforms data', async () => {
      const mockTemplate = {
        id: 202,
        tenant: 11,
        name: 'Feature Template',
        description: 'Template for features',
        ticket_type: 'FEATURE_REQUEST',
        category: 'GENERAL',
        default_priority: 'LOW',
        subject_template: 'Feature: {{name}}',
        description_template: 'Feature request: {{details}}',
        is_active: true,
        created_at: '2025-12-01T11:00:00Z',
      };
      vi.mocked(ticketsApi.getTicketTemplate).mockResolvedValue(mockTemplate as any);

      const { result } = renderHook(() => useTicketTemplate('202'), {
        wrapper: createWrapper(),
      });

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

      expect(ticketsApi.getTicketTemplate).toHaveBeenCalledWith('202');
      expect(result.current.data).toEqual({
        id: '202',
        tenant: '11',
        name: 'Feature Template',
        description: 'Template for features',
        ticketType: 'FEATURE_REQUEST',
        category: 'GENERAL',
        defaultPriority: 'LOW',
        subjectTemplate: 'Feature: {{name}}',
        descriptionTemplate: 'Feature request: {{details}}',
        isActive: true,
        createdAt: '2025-12-01T11:00:00Z',
      });
    });

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

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

      expect(ticketsApi.getTicketTemplate).not.toHaveBeenCalled();
    });

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

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

      expect(ticketsApi.getTicketTemplate).not.toHaveBeenCalled();
    });
  });

  describe('useCannedResponses', () => {
    it('fetches canned responses and transforms snake_case to camelCase', async () => {
      const mockResponses = [
        {
          id: 300,
          tenant: 12,
          title: 'Thank You',
          content: 'Thank you for contacting us!',
          category: 'GENERAL',
          is_active: true,
          use_count: 42,
          created_by: 20,
          created_at: '2025-11-01T10:00:00Z',
        },
        {
          id: 301,
          tenant: 12,
          title: 'Billing Info',
          content: 'Here is billing information...',
          category: 'BILLING',
          is_active: true,
          use_count: 15,
          created_by: null,
          created_at: '2025-11-02T10:00:00Z',
        },
      ];
      vi.mocked(ticketsApi.getCannedResponses).mockResolvedValue(mockResponses as any);

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

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

      expect(ticketsApi.getCannedResponses).toHaveBeenCalled();
      expect(result.current.data).toHaveLength(2);
      expect(result.current.data?.[0]).toEqual({
        id: '300',
        tenant: '12',
        title: 'Thank You',
        content: 'Thank you for contacting us!',
        category: 'GENERAL',
        isActive: true,
        useCount: 42,
        createdBy: '20',
        createdAt: '2025-11-01T10:00:00Z',
      });
      expect(result.current.data?.[1].createdBy).toBeUndefined();
    });

    it('handles null tenant and createdBy', async () => {
      const mockResponses = [
        {
          id: 302,
          tenant: null,
          title: 'Global Response',
          content: 'This is a global response',
          category: 'GENERAL',
          is_active: true,
          use_count: 0,
          created_by: null,
          created_at: '2025-11-03T10:00:00Z',
        },
      ];
      vi.mocked(ticketsApi.getCannedResponses).mockResolvedValue(mockResponses as any);

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

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

      expect(result.current.data?.[0]).toMatchObject({
        id: '302',
        tenant: undefined,
        createdBy: undefined,
      });
    });
  });

  describe('useRefreshTicketEmails', () => {
    it('calls refreshTicketEmails API', async () => {
      const mockResponse = {
        success: true,
        processed: 3,
        results: [
          {
            address: 'support@example.com',
            display_name: 'Support',
            processed: 3,
            status: 'success',
            last_check_at: '2025-12-07T12:00:00Z',
          },
        ],
      };
      vi.mocked(ticketsApi.refreshTicketEmails).mockResolvedValue(mockResponse);

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

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

      expect(ticketsApi.refreshTicketEmails).toHaveBeenCalled();
    });

    it('invalidates tickets query when emails are processed', async () => {
      const mockResponse = {
        success: true,
        processed: 2,
        results: [],
      };
      vi.mocked(ticketsApi.refreshTicketEmails).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(() => useRefreshTicketEmails(), { wrapper });

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

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

    it('does not invalidate queries when no emails are processed', async () => {
      const mockResponse = {
        success: true,
        processed: 0,
        results: [],
      };
      vi.mocked(ticketsApi.refreshTicketEmails).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(() => useRefreshTicketEmails(), { wrapper });

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

      expect(invalidateSpy).not.toHaveBeenCalled();
    });

    it('handles error responses', async () => {
      const mockResponse = {
        success: false,
        processed: 0,
        results: [
          {
            address: null,
            status: 'error',
            error: 'Connection failed',
            message: 'Unable to connect to mail server',
          },
        ],
      };
      vi.mocked(ticketsApi.refreshTicketEmails).mockResolvedValue(mockResponse);

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

      await act(async () => {
        const response = await result.current.mutateAsync();
        expect(response.success).toBe(false);
        expect(response.results[0].error).toBe('Connection failed');
      });
    });
  });
});
