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

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

import {
  getTickets,
  getTicket,
  createTicket,
  updateTicket,
  deleteTicket,
  getTicketComments,
  createTicketComment,
  getTicketTemplates,
  getTicketTemplate,
  getCannedResponses,
  refreshTicketEmails,
} from '../tickets';
import apiClient from '../client';
import type { Ticket, TicketComment, TicketTemplate, CannedResponse } from '../../types';

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

  describe('getTickets', () => {
    it('fetches all tickets without filters', async () => {
      const mockTickets: Ticket[] = [
        {
          id: '1',
          creator: 'user1',
          creatorEmail: 'user1@example.com',
          creatorFullName: 'User One',
          ticketType: 'CUSTOMER',
          status: 'OPEN',
          priority: 'HIGH',
          subject: 'Test Ticket',
          description: 'Test description',
          category: 'TECHNICAL',
          createdAt: '2024-01-01T00:00:00Z',
          updatedAt: '2024-01-01T00:00:00Z',
        },
        {
          id: '2',
          creator: 'user2',
          creatorEmail: 'user2@example.com',
          creatorFullName: 'User Two',
          ticketType: 'PLATFORM',
          status: 'IN_PROGRESS',
          priority: 'MEDIUM',
          subject: 'Another Ticket',
          description: 'Another description',
          category: 'BILLING',
          createdAt: '2024-01-02T00:00:00Z',
          updatedAt: '2024-01-02T00:00:00Z',
        },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockTickets });

      const result = await getTickets();

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/');
      expect(result).toEqual(mockTickets);
    });

    it('applies status filter', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      await getTickets({ status: 'OPEN' });

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/?status=OPEN');
    });

    it('applies priority filter', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      await getTickets({ priority: 'HIGH' });

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/?priority=HIGH');
    });

    it('applies category filter', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      await getTickets({ category: 'TECHNICAL' });

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/?category=TECHNICAL');
    });

    it('applies ticketType filter with snake_case conversion', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      await getTickets({ ticketType: 'CUSTOMER' });

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/?ticket_type=CUSTOMER');
    });

    it('applies assignee filter', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      await getTickets({ assignee: 'user123' });

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/?assignee=user123');
    });

    it('applies multiple filters', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      await getTickets({
        status: 'OPEN',
        priority: 'HIGH',
        category: 'BILLING',
        ticketType: 'CUSTOMER',
        assignee: 'user456',
      });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/tickets/?status=OPEN&priority=HIGH&category=BILLING&ticket_type=CUSTOMER&assignee=user456'
      );
    });

    it('applies partial filters', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      await getTickets({ status: 'CLOSED', priority: 'LOW' });

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/?status=CLOSED&priority=LOW');
    });

    it('handles empty filters object', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      await getTickets({});

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/');
    });
  });

  describe('getTicket', () => {
    it('fetches a single ticket by ID', async () => {
      const mockTicket: Ticket = {
        id: '123',
        creator: 'user1',
        creatorEmail: 'user1@example.com',
        creatorFullName: 'User One',
        assignee: 'user2',
        assigneeEmail: 'user2@example.com',
        assigneeFullName: 'User Two',
        ticketType: 'CUSTOMER',
        status: 'IN_PROGRESS',
        priority: 'HIGH',
        subject: 'Important Ticket',
        description: 'This needs attention',
        category: 'TECHNICAL',
        relatedAppointmentId: 'appt-456',
        createdAt: '2024-01-01T00:00:00Z',
        updatedAt: '2024-01-02T00:00:00Z',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockTicket });

      const result = await getTicket('123');

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/123/');
      expect(result).toEqual(mockTicket);
    });
  });

  describe('createTicket', () => {
    it('creates a new ticket', async () => {
      const newTicketData: Partial<Ticket> = {
        subject: 'New Ticket',
        description: 'New ticket description',
        ticketType: 'CUSTOMER',
        priority: 'MEDIUM',
        category: 'GENERAL_INQUIRY',
      };
      const createdTicket: Ticket = {
        id: '789',
        creator: 'current-user',
        creatorEmail: 'current@example.com',
        creatorFullName: 'Current User',
        status: 'OPEN',
        createdAt: '2024-01-03T00:00:00Z',
        updatedAt: '2024-01-03T00:00:00Z',
        ...newTicketData,
      } as Ticket;
      vi.mocked(apiClient.post).mockResolvedValue({ data: createdTicket });

      const result = await createTicket(newTicketData);

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/', newTicketData);
      expect(result).toEqual(createdTicket);
    });

    it('creates a ticket with all optional fields', async () => {
      const newTicketData: Partial<Ticket> = {
        subject: 'Complex Ticket',
        description: 'Complex description',
        ticketType: 'STAFF_REQUEST',
        priority: 'URGENT',
        category: 'TIME_OFF',
        assignee: 'manager-123',
        relatedAppointmentId: 'appt-999',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: {} });

      await createTicket(newTicketData);

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/', newTicketData);
    });
  });

  describe('updateTicket', () => {
    it('updates a ticket', async () => {
      const updateData: Partial<Ticket> = {
        status: 'RESOLVED',
        priority: 'LOW',
      };
      const updatedTicket: Ticket = {
        id: '123',
        creator: 'user1',
        creatorEmail: 'user1@example.com',
        creatorFullName: 'User One',
        ticketType: 'CUSTOMER',
        subject: 'Existing Ticket',
        description: 'Existing description',
        category: 'TECHNICAL',
        createdAt: '2024-01-01T00:00:00Z',
        updatedAt: '2024-01-05T00:00:00Z',
        ...updateData,
      } as Ticket;
      vi.mocked(apiClient.patch).mockResolvedValue({ data: updatedTicket });

      const result = await updateTicket('123', updateData);

      expect(apiClient.patch).toHaveBeenCalledWith('/tickets/123/', updateData);
      expect(result).toEqual(updatedTicket);
    });

    it('updates ticket assignee', async () => {
      const updateData = { assignee: 'new-assignee-456' };
      vi.mocked(apiClient.patch).mockResolvedValue({ data: {} });

      await updateTicket('123', updateData);

      expect(apiClient.patch).toHaveBeenCalledWith('/tickets/123/', updateData);
    });

    it('updates multiple ticket fields', async () => {
      const updateData: Partial<Ticket> = {
        status: 'CLOSED',
        priority: 'LOW',
        assignee: 'user789',
        category: 'RESOLVED',
      };
      vi.mocked(apiClient.patch).mockResolvedValue({ data: {} });

      await updateTicket('456', updateData);

      expect(apiClient.patch).toHaveBeenCalledWith('/tickets/456/', updateData);
    });
  });

  describe('deleteTicket', () => {
    it('deletes a ticket', async () => {
      vi.mocked(apiClient.delete).mockResolvedValue({});

      await deleteTicket('123');

      expect(apiClient.delete).toHaveBeenCalledWith('/tickets/123/');
    });

    it('returns void', async () => {
      vi.mocked(apiClient.delete).mockResolvedValue({});

      const result = await deleteTicket('456');

      expect(result).toBeUndefined();
    });
  });

  describe('getTicketComments', () => {
    it('fetches all comments for a ticket', async () => {
      const mockComments: TicketComment[] = [
        {
          id: 'c1',
          ticket: 't1',
          author: 'user1',
          authorEmail: 'user1@example.com',
          authorFullName: 'User One',
          commentText: 'First comment',
          createdAt: '2024-01-01T00:00:00Z',
          isInternal: false,
        },
        {
          id: 'c2',
          ticket: 't1',
          author: 'user2',
          authorEmail: 'user2@example.com',
          authorFullName: 'User Two',
          commentText: 'Second comment',
          createdAt: '2024-01-02T00:00:00Z',
          isInternal: true,
        },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockComments });

      const result = await getTicketComments('t1');

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/t1/comments/');
      expect(result).toEqual(mockComments);
    });

    it('handles ticket with no comments', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      const result = await getTicketComments('t999');

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/t999/comments/');
      expect(result).toEqual([]);
    });
  });

  describe('createTicketComment', () => {
    it('creates a new comment on a ticket', async () => {
      const commentData: Partial<TicketComment> = {
        commentText: 'This is a new comment',
        isInternal: false,
      };
      const createdComment: TicketComment = {
        id: 'c123',
        ticket: 't1',
        author: 'current-user',
        authorEmail: 'current@example.com',
        authorFullName: 'Current User',
        createdAt: '2024-01-03T00:00:00Z',
        ...commentData,
      } as TicketComment;
      vi.mocked(apiClient.post).mockResolvedValue({ data: createdComment });

      const result = await createTicketComment('t1', commentData);

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/t1/comments/', commentData);
      expect(result).toEqual(createdComment);
    });

    it('creates an internal comment', async () => {
      const commentData: Partial<TicketComment> = {
        commentText: 'Internal note',
        isInternal: true,
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: {} });

      await createTicketComment('t2', commentData);

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/t2/comments/', commentData);
    });
  });

  describe('getTicketTemplates', () => {
    it('fetches all ticket templates', async () => {
      const mockTemplates: TicketTemplate[] = [
        {
          id: 'tmpl1',
          name: 'Bug Report Template',
          description: 'Template for bug reports',
          ticketType: 'CUSTOMER',
          category: 'TECHNICAL',
          defaultPriority: 'HIGH',
          subjectTemplate: 'Bug: {{title}}',
          descriptionTemplate: 'Steps to reproduce:\n{{steps}}',
          isActive: true,
          createdAt: '2024-01-01T00:00:00Z',
        },
        {
          id: 'tmpl2',
          tenant: 'tenant123',
          name: 'Time Off Request',
          description: 'Staff time off template',
          ticketType: 'STAFF_REQUEST',
          category: 'TIME_OFF',
          defaultPriority: 'MEDIUM',
          subjectTemplate: 'Time Off: {{dates}}',
          descriptionTemplate: 'Reason:\n{{reason}}',
          isActive: true,
          createdAt: '2024-01-02T00:00:00Z',
        },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockTemplates });

      const result = await getTicketTemplates();

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/templates/');
      expect(result).toEqual(mockTemplates);
    });

    it('handles empty template list', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      const result = await getTicketTemplates();

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

  describe('getTicketTemplate', () => {
    it('fetches a single ticket template by ID', async () => {
      const mockTemplate: TicketTemplate = {
        id: 'tmpl123',
        name: 'Feature Request Template',
        description: 'Template for feature requests',
        ticketType: 'CUSTOMER',
        category: 'FEATURE_REQUEST',
        defaultPriority: 'LOW',
        subjectTemplate: 'Feature Request: {{feature}}',
        descriptionTemplate: 'Description:\n{{description}}\n\nBenefit:\n{{benefit}}',
        isActive: true,
        createdAt: '2024-01-01T00:00:00Z',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockTemplate });

      const result = await getTicketTemplate('tmpl123');

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/templates/tmpl123/');
      expect(result).toEqual(mockTemplate);
    });
  });

  describe('getCannedResponses', () => {
    it('fetches all canned responses', async () => {
      const mockResponses: CannedResponse[] = [
        {
          id: 'cr1',
          title: 'Thank You Response',
          content: 'Thank you for contacting us. We will get back to you soon.',
          category: 'GENERAL_INQUIRY',
          isActive: true,
          useCount: 42,
          createdBy: 'admin',
          createdAt: '2024-01-01T00:00:00Z',
        },
        {
          id: 'cr2',
          tenant: 'tenant456',
          title: 'Billing Issue',
          content: 'We have received your billing inquiry and are investigating.',
          category: 'BILLING',
          isActive: true,
          useCount: 18,
          createdBy: 'manager',
          createdAt: '2024-01-02T00:00:00Z',
        },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponses });

      const result = await getCannedResponses();

      expect(apiClient.get).toHaveBeenCalledWith('/tickets/canned-responses/');
      expect(result).toEqual(mockResponses);
    });

    it('handles empty canned responses list', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      const result = await getCannedResponses();

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

  describe('refreshTicketEmails', () => {
    it('successfully refreshes ticket emails', async () => {
      const mockResult = {
        success: true,
        processed: 5,
        results: [
          {
            address: 'support@example.com',
            display_name: 'Support',
            processed: 3,
            status: 'success',
            last_check_at: '2024-01-05T12:00:00Z',
          },
          {
            address: 'help@example.com',
            display_name: 'Help Desk',
            processed: 2,
            status: 'success',
            last_check_at: '2024-01-05T12:00:00Z',
          },
        ],
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await refreshTicketEmails();

      expect(apiClient.post).toHaveBeenCalledWith('/tickets/refresh-emails/');
      expect(result.success).toBe(true);
      expect(result.processed).toBe(5);
      expect(result.results).toHaveLength(2);
    });

    it('handles refresh with errors', async () => {
      const mockResult = {
        success: false,
        processed: 0,
        results: [
          {
            address: 'invalid@example.com',
            display_name: 'Invalid Email',
            status: 'error',
            error: 'Connection timeout',
          },
        ],
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await refreshTicketEmails();

      expect(result.success).toBe(false);
      expect(result.processed).toBe(0);
      expect(result.results[0].status).toBe('error');
      expect(result.results[0].error).toBe('Connection timeout');
    });

    it('handles partial success', async () => {
      const mockResult = {
        success: true,
        processed: 2,
        results: [
          {
            address: 'working@example.com',
            processed: 2,
            status: 'success',
          },
          {
            address: null,
            status: 'skipped',
            message: 'No email address configured',
          },
        ],
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await refreshTicketEmails();

      expect(result.success).toBe(true);
      expect(result.processed).toBe(2);
      expect(result.results).toHaveLength(2);
      expect(result.results[0].status).toBe('success');
      expect(result.results[1].status).toBe('skipped');
    });

    it('handles no configured email addresses', async () => {
      const mockResult = {
        success: false,
        processed: 0,
        results: [],
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResult });

      const result = await refreshTicketEmails();

      expect(result.success).toBe(false);
      expect(result.processed).toBe(0);
      expect(result.results).toHaveLength(0);
    });
  });
});
