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

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

import {
  getQuotaStatus,
  getQuotaResources,
  archiveResources,
  unarchiveResource,
  getOverageDetail,
  QuotaStatus,
  QuotaResourcesResponse,
  ArchiveResponse,
  QuotaOverageDetail,
} from '../quota';
import apiClient from '../client';

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

  describe('getQuotaStatus', () => {
    it('fetches quota status from API', async () => {
      const mockQuotaStatus: QuotaStatus = {
        active_overages: [
          {
            id: 1,
            quota_type: 'resources',
            display_name: 'Resources',
            current_usage: 15,
            allowed_limit: 10,
            overage_amount: 5,
            days_remaining: 7,
            grace_period_ends_at: '2025-12-14T00:00:00Z',
          },
        ],
        usage: {
          resources: {
            current: 15,
            limit: 10,
            display_name: 'Resources',
          },
          staff: {
            current: 3,
            limit: 5,
            display_name: 'Staff Members',
          },
          services: {
            current: 8,
            limit: 20,
            display_name: 'Services',
          },
        },
      };

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

      const result = await getQuotaStatus();

      expect(apiClient.get).toHaveBeenCalledWith('/quota/status/');
      expect(result).toEqual(mockQuotaStatus);
      expect(result.active_overages).toHaveLength(1);
      expect(result.usage.resources.current).toBe(15);
    });

    it('returns empty active_overages when no overages exist', async () => {
      const mockQuotaStatus: QuotaStatus = {
        active_overages: [],
        usage: {
          resources: {
            current: 5,
            limit: 10,
            display_name: 'Resources',
          },
        },
      };

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

      const result = await getQuotaStatus();

      expect(result.active_overages).toHaveLength(0);
      expect(result.usage.resources.current).toBeLessThan(result.usage.resources.limit);
    });

    it('handles multiple quota types in usage', async () => {
      const mockQuotaStatus: QuotaStatus = {
        active_overages: [],
        usage: {
          resources: {
            current: 5,
            limit: 10,
            display_name: 'Resources',
          },
          staff: {
            current: 2,
            limit: 5,
            display_name: 'Staff Members',
          },
          services: {
            current: 15,
            limit: 20,
            display_name: 'Services',
          },
          customers: {
            current: 100,
            limit: 500,
            display_name: 'Customers',
          },
        },
      };

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

      const result = await getQuotaStatus();

      expect(Object.keys(result.usage)).toHaveLength(4);
      expect(result.usage).toHaveProperty('resources');
      expect(result.usage).toHaveProperty('staff');
      expect(result.usage).toHaveProperty('services');
      expect(result.usage).toHaveProperty('customers');
    });
  });

  describe('getQuotaResources', () => {
    it('fetches resources for a specific quota type', async () => {
      const mockResourcesResponse: QuotaResourcesResponse = {
        quota_type: 'resources',
        resources: [
          {
            id: 1,
            name: 'Conference Room A',
            type: 'room',
            created_at: '2025-01-01T10:00:00Z',
            is_archived: false,
            archived_at: null,
          },
          {
            id: 2,
            name: 'Conference Room B',
            type: 'room',
            created_at: '2025-01-02T11:00:00Z',
            is_archived: false,
            archived_at: null,
          },
        ],
      };

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

      const result = await getQuotaResources('resources');

      expect(apiClient.get).toHaveBeenCalledWith('/quota/resources/resources/');
      expect(result).toEqual(mockResourcesResponse);
      expect(result.quota_type).toBe('resources');
      expect(result.resources).toHaveLength(2);
    });

    it('fetches staff members for staff quota type', async () => {
      const mockStaffResponse: QuotaResourcesResponse = {
        quota_type: 'staff',
        resources: [
          {
            id: 10,
            name: 'John Doe',
            email: 'john@example.com',
            role: 'staff',
            created_at: '2025-01-15T09:00:00Z',
            is_archived: false,
            archived_at: null,
          },
          {
            id: 11,
            name: 'Jane Smith',
            email: 'jane@example.com',
            role: 'manager',
            created_at: '2025-01-16T09:00:00Z',
            is_archived: false,
            archived_at: null,
          },
        ],
      };

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

      const result = await getQuotaResources('staff');

      expect(apiClient.get).toHaveBeenCalledWith('/quota/resources/staff/');
      expect(result.quota_type).toBe('staff');
      expect(result.resources[0]).toHaveProperty('email');
      expect(result.resources[0]).toHaveProperty('role');
    });

    it('fetches services for services quota type', async () => {
      const mockServicesResponse: QuotaResourcesResponse = {
        quota_type: 'services',
        resources: [
          {
            id: 20,
            name: 'Haircut',
            duration: 30,
            price: '25.00',
            created_at: '2025-02-01T10:00:00Z',
            is_archived: false,
            archived_at: null,
          },
          {
            id: 21,
            name: 'Color Treatment',
            duration: 90,
            price: '75.00',
            created_at: '2025-02-02T10:00:00Z',
            is_archived: false,
            archived_at: null,
          },
        ],
      };

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

      const result = await getQuotaResources('services');

      expect(apiClient.get).toHaveBeenCalledWith('/quota/resources/services/');
      expect(result.quota_type).toBe('services');
      expect(result.resources[0]).toHaveProperty('duration');
      expect(result.resources[0]).toHaveProperty('price');
    });

    it('includes archived resources', async () => {
      const mockResourcesResponse: QuotaResourcesResponse = {
        quota_type: 'resources',
        resources: [
          {
            id: 1,
            name: 'Active Resource',
            created_at: '2025-01-01T10:00:00Z',
            is_archived: false,
            archived_at: null,
          },
          {
            id: 2,
            name: 'Archived Resource',
            created_at: '2024-12-01T10:00:00Z',
            is_archived: true,
            archived_at: '2025-12-01T15:30:00Z',
          },
        ],
      };

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

      const result = await getQuotaResources('resources');

      expect(result.resources).toHaveLength(2);
      expect(result.resources[0].is_archived).toBe(false);
      expect(result.resources[1].is_archived).toBe(true);
      expect(result.resources[1].archived_at).toBe('2025-12-01T15:30:00Z');
    });

    it('handles empty resources list', async () => {
      const mockEmptyResponse: QuotaResourcesResponse = {
        quota_type: 'resources',
        resources: [],
      };

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

      const result = await getQuotaResources('resources');

      expect(result.resources).toHaveLength(0);
      expect(result.quota_type).toBe('resources');
    });
  });

  describe('archiveResources', () => {
    it('archives multiple resources successfully', async () => {
      const mockArchiveResponse: ArchiveResponse = {
        archived_count: 3,
        current_usage: 7,
        limit: 10,
        is_resolved: true,
      };

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

      const result = await archiveResources('resources', [1, 2, 3]);

      expect(apiClient.post).toHaveBeenCalledWith('/quota/archive/', {
        quota_type: 'resources',
        resource_ids: [1, 2, 3],
      });
      expect(result).toEqual(mockArchiveResponse);
      expect(result.archived_count).toBe(3);
      expect(result.is_resolved).toBe(true);
    });

    it('archives single resource', async () => {
      const mockArchiveResponse: ArchiveResponse = {
        archived_count: 1,
        current_usage: 9,
        limit: 10,
        is_resolved: true,
      };

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

      const result = await archiveResources('staff', [5]);

      expect(apiClient.post).toHaveBeenCalledWith('/quota/archive/', {
        quota_type: 'staff',
        resource_ids: [5],
      });
      expect(result.archived_count).toBe(1);
    });

    it('indicates overage is still not resolved after archiving', async () => {
      const mockArchiveResponse: ArchiveResponse = {
        archived_count: 2,
        current_usage: 12,
        limit: 10,
        is_resolved: false,
      };

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

      const result = await archiveResources('resources', [1, 2]);

      expect(result.is_resolved).toBe(false);
      expect(result.current_usage).toBeGreaterThan(result.limit);
    });

    it('handles archiving with different quota types', async () => {
      const mockArchiveResponse: ArchiveResponse = {
        archived_count: 5,
        current_usage: 15,
        limit: 20,
        is_resolved: true,
      };

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

      await archiveResources('services', [10, 11, 12, 13, 14]);

      expect(apiClient.post).toHaveBeenCalledWith('/quota/archive/', {
        quota_type: 'services',
        resource_ids: [10, 11, 12, 13, 14],
      });
    });

    it('handles empty resource_ids array', async () => {
      const mockArchiveResponse: ArchiveResponse = {
        archived_count: 0,
        current_usage: 10,
        limit: 10,
        is_resolved: true,
      };

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

      const result = await archiveResources('resources', []);

      expect(apiClient.post).toHaveBeenCalledWith('/quota/archive/', {
        quota_type: 'resources',
        resource_ids: [],
      });
      expect(result.archived_count).toBe(0);
    });
  });

  describe('unarchiveResource', () => {
    it('unarchives a resource successfully', async () => {
      const mockUnarchiveResponse = {
        success: true,
        resource_id: 5,
      };

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

      const result = await unarchiveResource('resources', 5);

      expect(apiClient.post).toHaveBeenCalledWith('/quota/unarchive/', {
        quota_type: 'resources',
        resource_id: 5,
      });
      expect(result).toEqual(mockUnarchiveResponse);
      expect(result.success).toBe(true);
      expect(result.resource_id).toBe(5);
    });

    it('unarchives staff member', async () => {
      const mockUnarchiveResponse = {
        success: true,
        resource_id: 10,
      };

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

      const result = await unarchiveResource('staff', 10);

      expect(apiClient.post).toHaveBeenCalledWith('/quota/unarchive/', {
        quota_type: 'staff',
        resource_id: 10,
      });
      expect(result.success).toBe(true);
    });

    it('unarchives service', async () => {
      const mockUnarchiveResponse = {
        success: true,
        resource_id: 20,
      };

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

      const result = await unarchiveResource('services', 20);

      expect(apiClient.post).toHaveBeenCalledWith('/quota/unarchive/', {
        quota_type: 'services',
        resource_id: 20,
      });
      expect(result.resource_id).toBe(20);
    });

    it('handles unsuccessful unarchive', async () => {
      const mockUnarchiveResponse = {
        success: false,
        resource_id: 5,
      };

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

      const result = await unarchiveResource('resources', 5);

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

  describe('getOverageDetail', () => {
    it('fetches detailed overage information', async () => {
      const mockOverageDetail: QuotaOverageDetail = {
        id: 1,
        quota_type: 'resources',
        display_name: 'Resources',
        current_usage: 15,
        allowed_limit: 10,
        overage_amount: 5,
        days_remaining: 7,
        grace_period_ends_at: '2025-12-14T00:00:00Z',
        status: 'active',
        created_at: '2025-12-07T10:00:00Z',
        initial_email_sent_at: '2025-12-07T10:05:00Z',
        week_reminder_sent_at: null,
        day_reminder_sent_at: null,
        archived_resource_ids: [],
      };

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

      const result = await getOverageDetail(1);

      expect(apiClient.get).toHaveBeenCalledWith('/quota/overages/1/');
      expect(result).toEqual(mockOverageDetail);
      expect(result.status).toBe('active');
      expect(result.overage_amount).toBe(5);
    });

    it('includes sent email timestamps', async () => {
      const mockOverageDetail: QuotaOverageDetail = {
        id: 2,
        quota_type: 'staff',
        display_name: 'Staff Members',
        current_usage: 8,
        allowed_limit: 5,
        overage_amount: 3,
        days_remaining: 3,
        grace_period_ends_at: '2025-12-10T00:00:00Z',
        status: 'active',
        created_at: '2025-11-30T10:00:00Z',
        initial_email_sent_at: '2025-11-30T10:05:00Z',
        week_reminder_sent_at: '2025-12-03T09:00:00Z',
        day_reminder_sent_at: '2025-12-06T09:00:00Z',
        archived_resource_ids: [],
      };

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

      const result = await getOverageDetail(2);

      expect(result.initial_email_sent_at).toBe('2025-11-30T10:05:00Z');
      expect(result.week_reminder_sent_at).toBe('2025-12-03T09:00:00Z');
      expect(result.day_reminder_sent_at).toBe('2025-12-06T09:00:00Z');
    });

    it('includes archived resource IDs', async () => {
      const mockOverageDetail: QuotaOverageDetail = {
        id: 3,
        quota_type: 'resources',
        display_name: 'Resources',
        current_usage: 10,
        allowed_limit: 10,
        overage_amount: 0,
        days_remaining: 5,
        grace_period_ends_at: '2025-12-12T00:00:00Z',
        status: 'resolved',
        created_at: '2025-12-01T10:00:00Z',
        initial_email_sent_at: '2025-12-01T10:05:00Z',
        week_reminder_sent_at: null,
        day_reminder_sent_at: null,
        archived_resource_ids: [1, 3, 5, 7],
      };

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

      const result = await getOverageDetail(3);

      expect(result.archived_resource_ids).toHaveLength(4);
      expect(result.archived_resource_ids).toEqual([1, 3, 5, 7]);
      expect(result.status).toBe('resolved');
    });

    it('handles resolved overage with zero overage_amount', async () => {
      const mockOverageDetail: QuotaOverageDetail = {
        id: 4,
        quota_type: 'services',
        display_name: 'Services',
        current_usage: 18,
        allowed_limit: 20,
        overage_amount: 0,
        days_remaining: 0,
        grace_period_ends_at: '2025-12-05T00:00:00Z',
        status: 'resolved',
        created_at: '2025-11-25T10:00:00Z',
        initial_email_sent_at: '2025-11-25T10:05:00Z',
        week_reminder_sent_at: '2025-11-28T09:00:00Z',
        day_reminder_sent_at: null,
        archived_resource_ids: [20, 21],
      };

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

      const result = await getOverageDetail(4);

      expect(result.overage_amount).toBe(0);
      expect(result.status).toBe('resolved');
      expect(result.current_usage).toBeLessThanOrEqual(result.allowed_limit);
    });

    it('handles expired overage', async () => {
      const mockOverageDetail: QuotaOverageDetail = {
        id: 5,
        quota_type: 'resources',
        display_name: 'Resources',
        current_usage: 15,
        allowed_limit: 10,
        overage_amount: 5,
        days_remaining: 0,
        grace_period_ends_at: '2025-12-06T00:00:00Z',
        status: 'expired',
        created_at: '2025-11-20T10:00:00Z',
        initial_email_sent_at: '2025-11-20T10:05:00Z',
        week_reminder_sent_at: '2025-11-27T09:00:00Z',
        day_reminder_sent_at: '2025-12-05T09:00:00Z',
        archived_resource_ids: [],
      };

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

      const result = await getOverageDetail(5);

      expect(result.status).toBe('expired');
      expect(result.days_remaining).toBe(0);
      expect(result.overage_amount).toBeGreaterThan(0);
    });

    it('handles null email timestamps when no reminders sent', async () => {
      const mockOverageDetail: QuotaOverageDetail = {
        id: 6,
        quota_type: 'staff',
        display_name: 'Staff Members',
        current_usage: 6,
        allowed_limit: 5,
        overage_amount: 1,
        days_remaining: 14,
        grace_period_ends_at: '2025-12-21T00:00:00Z',
        status: 'active',
        created_at: '2025-12-07T10:00:00Z',
        initial_email_sent_at: null,
        week_reminder_sent_at: null,
        day_reminder_sent_at: null,
        archived_resource_ids: [],
      };

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

      const result = await getOverageDetail(6);

      expect(result.initial_email_sent_at).toBeNull();
      expect(result.week_reminder_sent_at).toBeNull();
      expect(result.day_reminder_sent_at).toBeNull();
    });
  });
});
