import { describe, it, expect } from 'vitest';
import { adaptResources, adaptEvents, adaptPending, BackendResource, BackendAppointment } from '../uiAdapter';

describe('uiAdapter', () => {
  describe('adaptResources', () => {
    it('should return empty array when given empty array', () => {
      const result = adaptResources([]);
      expect(result).toEqual([]);
    });

    it('should transform single resource correctly', () => {
      const backendResources: BackendResource[] = [
        {
          id: 1,
          name: 'Conference Room A',
          description: 'Large conference room with projector',
        },
      ];

      const result = adaptResources(backendResources);

      expect(result).toEqual([
        {
          id: 1,
          name: 'Conference Room A',
        },
      ]);
    });

    it('should transform multiple resources correctly', () => {
      const backendResources: BackendResource[] = [
        {
          id: 1,
          name: 'Conference Room A',
          description: 'Large conference room',
        },
        {
          id: 2,
          name: 'Conference Room B',
        },
        {
          id: 3,
          name: 'Meeting Room',
          description: 'Small meeting space',
        },
      ];

      const result = adaptResources(backendResources);

      expect(result).toHaveLength(3);
      expect(result).toEqual([
        { id: 1, name: 'Conference Room A' },
        { id: 2, name: 'Conference Room B' },
        { id: 3, name: 'Meeting Room' },
      ]);
    });

    it('should only include id and name properties', () => {
      const backendResources: BackendResource[] = [
        {
          id: 1,
          name: 'Conference Room A',
          description: 'Should not be included',
        },
      ];

      const result = adaptResources(backendResources);

      expect(result[0]).toHaveProperty('id');
      expect(result[0]).toHaveProperty('name');
      expect(result[0]).not.toHaveProperty('description');
    });
  });

  describe('adaptEvents', () => {
    it('should return empty array when given empty array', () => {
      const result = adaptEvents([]);
      expect(result).toEqual([]);
    });

    it('should filter out appointments with null resource', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
          customer_name: 'John Doe',
          service_name: 'Consultation',
        },
        {
          id: 2,
          resource: null,
          customer: 101,
          service: 201,
          start_time: '2024-01-15T14:00:00Z',
          end_time: '2024-01-15T15:00:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Jane Smith',
          service_name: 'Follow-up',
        },
      ];

      const result = adaptEvents(appointments);

      expect(result).toHaveLength(1);
      expect(result[0].id).toBe(1);
    });

    it('should filter out appointments with undefined resource', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
          customer_name: 'John Doe',
          service_name: 'Consultation',
        },
        {
          id: 2,
          resource: undefined,
          customer: 101,
          service: 201,
          start_time: '2024-01-15T14:00:00Z',
          end_time: '2024-01-15T15:00:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Jane Smith',
          service_name: 'Follow-up',
        },
      ];

      const result = adaptEvents(appointments);

      expect(result).toHaveLength(1);
      expect(result[0].id).toBe(1);
    });

    it('should transform dates correctly to Date objects', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
          customer_name: 'John Doe',
          service_name: 'Consultation',
        },
      ];

      const result = adaptEvents(appointments);

      expect(result[0].start).toBeInstanceOf(Date);
      expect(result[0].end).toBeInstanceOf(Date);
      expect(result[0].start.toISOString()).toBe('2024-01-15T10:00:00.000Z');
      expect(result[0].end.toISOString()).toBe('2024-01-15T11:00:00.000Z');
    });

    it('should use customer_name and service_name when provided', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
          customer_name: 'John Doe',
          service_name: 'Consultation',
        },
      ];

      const result = adaptEvents(appointments);

      expect(result[0].title).toBe('John Doe');
      expect(result[0].serviceName).toBe('Consultation');
    });

    it('should use fallback names when customer_name missing', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
        },
      ];

      const result = adaptEvents(appointments);

      expect(result[0].title).toBe('Customer 100');
    });

    it('should use fallback names when service_name missing', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
        },
      ];

      const result = adaptEvents(appointments);

      expect(result[0].serviceName).toBe('Service 200');
    });

    it('should correctly map all appointment fields', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 42,
          resource: 5,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:30:00Z',
          status: 'COMPLETED',
          is_paid: true,
          customer_name: 'Alice Johnson',
          service_name: 'Deep Tissue Massage',
        },
      ];

      const result = adaptEvents(appointments);

      expect(result[0]).toMatchObject({
        id: 42,
        resourceId: 5,
        title: 'Alice Johnson',
        serviceName: 'Deep Tissue Massage',
        status: 'COMPLETED',
        isPaid: true,
      });
      expect(result[0].start).toBeInstanceOf(Date);
      expect(result[0].end).toBeInstanceOf(Date);
    });

    it('should handle all status types', () => {
      const statuses: Array<'PENDING' | 'CONFIRMED' | 'COMPLETED' | 'CANCELLED' | 'NO_SHOW'> = [
        'PENDING',
        'CONFIRMED',
        'COMPLETED',
        'CANCELLED',
        'NO_SHOW',
      ];

      const appointments: BackendAppointment[] = statuses.map((status, index) => ({
        id: index + 1,
        resource: 1,
        customer: 100,
        service: 200,
        start_time: '2024-01-15T10:00:00Z',
        end_time: '2024-01-15T11:00:00Z',
        status,
        is_paid: false,
        customer_name: 'Test User',
        service_name: 'Test Service',
      }));

      const result = adaptEvents(appointments);

      expect(result).toHaveLength(5);
      result.forEach((event, index) => {
        expect(event.status).toBe(statuses[index]);
      });
    });

    it('should handle isPaid true and false', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
        },
        {
          id: 2,
          resource: 1,
          customer: 101,
          service: 201,
          start_time: '2024-01-15T12:00:00Z',
          end_time: '2024-01-15T13:00:00Z',
          status: 'CONFIRMED',
          is_paid: false,
        },
      ];

      const result = adaptEvents(appointments);

      expect(result[0].isPaid).toBe(true);
      expect(result[1].isPaid).toBe(false);
    });
  });

  describe('adaptPending', () => {
    it('should return empty array when given empty array', () => {
      const result = adaptPending([]);
      expect(result).toEqual([]);
    });

    it('should filter to only null resource appointments', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
          customer_name: 'John Doe',
          service_name: 'Consultation',
        },
        {
          id: 2,
          resource: null,
          customer: 101,
          service: 201,
          start_time: '2024-01-15T14:00:00Z',
          end_time: '2024-01-15T15:00:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Jane Smith',
          service_name: 'Follow-up',
        },
      ];

      const result = adaptPending(appointments);

      expect(result).toHaveLength(1);
      expect(result[0].id).toBe(2);
    });

    it('should filter to only undefined resource appointments', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
          customer_name: 'John Doe',
          service_name: 'Consultation',
        },
        {
          id: 2,
          resource: undefined,
          customer: 101,
          service: 201,
          start_time: '2024-01-15T14:00:00Z',
          end_time: '2024-01-15T15:00:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Jane Smith',
          service_name: 'Follow-up',
        },
      ];

      const result = adaptPending(appointments);

      expect(result).toHaveLength(1);
      expect(result[0].id).toBe(2);
    });

    it('should calculate durationMinutes correctly for 1 hour', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'John Doe',
          service_name: 'Consultation',
        },
      ];

      const result = adaptPending(appointments);

      expect(result[0].durationMinutes).toBe(60);
    });

    it('should calculate durationMinutes correctly for 30 minutes', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T10:30:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'John Doe',
          service_name: 'Quick Checkup',
        },
      ];

      const result = adaptPending(appointments);

      expect(result[0].durationMinutes).toBe(30);
    });

    it('should calculate durationMinutes correctly for 90 minutes', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:30:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'John Doe',
          service_name: 'Extended Session',
        },
      ];

      const result = adaptPending(appointments);

      expect(result[0].durationMinutes).toBe(90);
    });

    it('should round durationMinutes correctly', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T10:25:30Z', // 25.5 minutes
          status: 'PENDING',
          is_paid: false,
          customer_name: 'John Doe',
          service_name: 'Quick Session',
        },
      ];

      const result = adaptPending(appointments);

      // Math.round(25.5) = 26
      expect(result[0].durationMinutes).toBe(26);
    });

    it('should use customer_name and service_name when provided', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Alice Johnson',
          service_name: 'Therapy Session',
        },
      ];

      const result = adaptPending(appointments);

      expect(result[0].customerName).toBe('Alice Johnson');
      expect(result[0].serviceName).toBe('Therapy Session');
    });

    it('should use fallback name when customer_name missing', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 123,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'PENDING',
          is_paid: false,
        },
      ];

      const result = adaptPending(appointments);

      expect(result[0].customerName).toBe('Customer 123');
    });

    it('should use fallback name when service_name missing', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 456,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'PENDING',
          is_paid: false,
        },
      ];

      const result = adaptPending(appointments);

      expect(result[0].serviceName).toBe('Service 456');
    });

    it('should correctly map all required fields', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 99,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:15:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Bob Williams',
          service_name: 'Initial Consultation',
        },
      ];

      const result = adaptPending(appointments);

      expect(result[0]).toEqual({
        id: 99,
        customerName: 'Bob Williams',
        serviceName: 'Initial Consultation',
        durationMinutes: 75,
      });
    });

    it('should handle multiple pending appointments', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Alice',
          service_name: 'Service A',
        },
        {
          id: 2,
          resource: undefined,
          customer: 101,
          service: 201,
          start_time: '2024-01-15T14:00:00Z',
          end_time: '2024-01-15T14:30:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Bob',
          service_name: 'Service B',
        },
        {
          id: 3,
          resource: null,
          customer: 102,
          service: 202,
          start_time: '2024-01-15T16:00:00Z',
          end_time: '2024-01-15T17:30:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Charlie',
          service_name: 'Service C',
        },
      ];

      const result = adaptPending(appointments);

      expect(result).toHaveLength(3);
      expect(result[0].durationMinutes).toBe(60);
      expect(result[1].durationMinutes).toBe(30);
      expect(result[2].durationMinutes).toBe(90);
    });

    it('should only include required properties in output', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'John Doe',
          service_name: 'Consultation',
        },
      ];

      const result = adaptPending(appointments);

      expect(Object.keys(result[0])).toEqual(['id', 'customerName', 'serviceName', 'durationMinutes']);
      expect(result[0]).not.toHaveProperty('resource');
      expect(result[0]).not.toHaveProperty('customer');
      expect(result[0]).not.toHaveProperty('service');
      expect(result[0]).not.toHaveProperty('start_time');
      expect(result[0]).not.toHaveProperty('end_time');
      expect(result[0]).not.toHaveProperty('status');
      expect(result[0]).not.toHaveProperty('is_paid');
    });
  });

  describe('edge cases and integration', () => {
    it('should handle mixed appointments correctly', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: true,
          customer_name: 'Scheduled User',
          service_name: 'Scheduled Service',
        },
        {
          id: 2,
          resource: null,
          customer: 101,
          service: 201,
          start_time: '2024-01-15T14:00:00Z',
          end_time: '2024-01-15T15:00:00Z',
          status: 'PENDING',
          is_paid: false,
          customer_name: 'Pending User',
          service_name: 'Pending Service',
        },
      ];

      const events = adaptEvents(appointments);
      const pending = adaptPending(appointments);

      expect(events).toHaveLength(1);
      expect(events[0].id).toBe(1);
      expect(pending).toHaveLength(1);
      expect(pending[0].id).toBe(2);
    });

    it('should handle appointments with resource: 0 as scheduled', () => {
      // Resource ID 0 should be treated as a valid resource
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 0,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T11:00:00Z',
          status: 'CONFIRMED',
          is_paid: false,
        },
      ];

      const events = adaptEvents(appointments);
      const pending = adaptPending(appointments);

      // resource: 0 is a valid resource ID (not null/undefined)
      expect(events).toHaveLength(1);
      expect(events[0].resourceId).toBe(0);
      expect(pending).toHaveLength(0);
    });

    it('should handle date strings with different formats', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: 1,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00.000Z',
          end_time: '2024-01-15T11:00:00.000Z',
          status: 'CONFIRMED',
          is_paid: false,
        },
      ];

      const result = adaptEvents(appointments);

      expect(result[0].start).toBeInstanceOf(Date);
      expect(result[0].end).toBeInstanceOf(Date);
      expect(result[0].start.getTime()).toBeLessThan(result[0].end.getTime());
    });

    it('should handle very short duration in adaptPending', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T10:00:00Z',
          end_time: '2024-01-15T10:01:00Z', // 1 minute
          status: 'PENDING',
          is_paid: false,
        },
      ];

      const result = adaptPending(appointments);

      expect(result[0].durationMinutes).toBe(1);
    });

    it('should handle very long duration in adaptPending', () => {
      const appointments: BackendAppointment[] = [
        {
          id: 1,
          resource: null,
          customer: 100,
          service: 200,
          start_time: '2024-01-15T08:00:00Z',
          end_time: '2024-01-15T18:00:00Z', // 10 hours
          status: 'PENDING',
          is_paid: false,
        },
      ];

      const result = adaptPending(appointments);

      expect(result[0].durationMinutes).toBe(600);
    });
  });
});
