/**
 * @ai-summary Unit tests for computeVehicleHealth pure function
 * @ai-context Tests health calculation logic from maintenance schedules and document expiry
 */

import { computeVehicleHealth } from '../../utils/computeVehicleHealth';
import { MaintenanceSchedule } from '../../../maintenance/types/maintenance.types';
import { DocumentRecord } from '../../../documents/types/documents.types';

// Helper factory functions for test data
const makeSchedule = (overrides: Partial<MaintenanceSchedule> = {}): MaintenanceSchedule => ({
  id: 'sched-1',
  userId: 'user-1',
  vehicleId: 'vehicle-1',
  category: 'routine_maintenance',
  subtypes: ['Engine Oil'],
  scheduleType: 'interval',
  isActive: true,
  createdAt: '2025-01-01T00:00:00Z',
  updatedAt: '2025-01-01T00:00:00Z',
  ...overrides,
});

const makeDocument = (overrides: Partial<DocumentRecord> = {}): DocumentRecord => ({
  id: 'doc-1',
  userId: 'user-1',
  vehicleId: 'vehicle-1',
  documentType: 'insurance',
  title: 'Insurance Policy',
  sharedVehicleIds: [],
  createdAt: '2025-01-01T00:00:00Z',
  updatedAt: '2025-01-01T00:00:00Z',
  ...overrides,
});

describe('computeVehicleHealth', () => {
  beforeAll(() => {
    jest.useFakeTimers();
    jest.setSystemTime(new Date('2026-02-15T00:00:00Z'));
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  describe('Green health', () => {
    it('should return green health with no schedules and no documents', () => {
      const { health, attentionItems } = computeVehicleHealth([], []);

      expect(health).toBe('green');
      expect(attentionItems).toEqual([]);
    });

    it('should return green health with schedule due in 20 days and 1 upcoming attention item', () => {
      const schedules = [
        makeSchedule({
          nextDueDate: '2026-03-07T00:00:00Z', // 20 days from now
          subtypes: ['Engine Oil'],
        }),
      ];

      const { health, attentionItems } = computeVehicleHealth(schedules, []);

      expect(health).toBe('green');
      expect(attentionItems).toHaveLength(1);
      expect(attentionItems[0]).toEqual({
        label: 'Engine Oil',
        urgency: 'upcoming',
        daysUntilDue: 20,
        source: 'maintenance',
      });
    });
  });

  describe('Yellow health', () => {
    it('should return yellow health with schedule due in 10 days, no overdue', () => {
      const schedules = [
        makeSchedule({
          nextDueDate: '2026-02-25T00:00:00Z', // 10 days from now
          subtypes: ['Air Filter Element'],
        }),
      ];

      const { health, attentionItems } = computeVehicleHealth(schedules, []);

      expect(health).toBe('yellow');
      expect(attentionItems).toHaveLength(1);
      expect(attentionItems[0]).toEqual({
        label: 'Air Filter Element',
        urgency: 'due-soon',
        daysUntilDue: 10,
        source: 'maintenance',
      });
    });

    it('should return yellow health with registration expiring in 7 days', () => {
      const documents = [
        makeDocument({
          documentType: 'registration',
          expirationDate: '2026-02-22T00:00:00Z', // 7 days from now
        }),
      ];

      const { health, attentionItems } = computeVehicleHealth([], documents);

      expect(health).toBe('yellow');
      expect(attentionItems).toHaveLength(1);
      expect(attentionItems[0]).toEqual({
        label: 'Registration',
        urgency: 'due-soon',
        daysUntilDue: 7,
        source: 'document',
      });
    });
  });

  describe('Red health', () => {
    it('should return red health with maintenance overdue by 5 days', () => {
      const schedules = [
        makeSchedule({
          nextDueDate: '2026-02-10T00:00:00Z', // 5 days ago
          subtypes: ['Brakes and Traction Control'],
        }),
      ];

      const { health, attentionItems } = computeVehicleHealth(schedules, []);

      expect(health).toBe('red');
      expect(attentionItems).toHaveLength(1);
      expect(attentionItems[0]).toEqual({
        label: 'Brakes and Traction Control',
        urgency: 'overdue',
        daysUntilDue: -5,
        source: 'maintenance',
      });
    });

    it('should return red health with insurance expired 3 days ago', () => {
      const documents = [
        makeDocument({
          documentType: 'insurance',
          expirationDate: '2026-02-12T00:00:00Z', // 3 days ago
        }),
      ];

      const { health, attentionItems } = computeVehicleHealth([], documents);

      expect(health).toBe('red');
      expect(attentionItems).toHaveLength(1);
      expect(attentionItems[0]).toEqual({
        label: 'Insurance',
        urgency: 'overdue',
        daysUntilDue: -3,
        source: 'document',
      });
    });

    it('should return red health with one overdue maintenance and one due-soon document', () => {
      const schedules = [
        makeSchedule({
          nextDueDate: '2026-02-10T00:00:00Z', // 5 days ago
          subtypes: ['Coolant'],
        }),
      ];

      const documents = [
        makeDocument({
          documentType: 'registration',
          expirationDate: '2026-02-20T00:00:00Z', // 5 days from now
        }),
      ];

      const { health, attentionItems } = computeVehicleHealth(schedules, documents);

      expect(health).toBe('red');
      expect(attentionItems).toHaveLength(2);
      expect(attentionItems[0]).toEqual({
        label: 'Coolant',
        urgency: 'overdue',
        daysUntilDue: -5,
        source: 'maintenance',
      });
      expect(attentionItems[1]).toEqual({
        label: 'Registration',
        urgency: 'due-soon',
        daysUntilDue: 5,
        source: 'document',
      });
    });
  });

  describe('Attention items sorting', () => {
    it('should sort attention items with overdue first by most overdue, then due-soon by proximity', () => {
      const schedules = [
        makeSchedule({
          id: 'sched-1',
          nextDueDate: '2026-02-13T00:00:00Z', // 2 days ago (overdue, less urgent)
          subtypes: ['Cabin Air Filter / Purifier'],
        }),
        makeSchedule({
          id: 'sched-2',
          nextDueDate: '2026-02-05T00:00:00Z', // 10 days ago (overdue, more urgent)
          subtypes: ['Engine Oil'],
        }),
        makeSchedule({
          id: 'sched-3',
          nextDueDate: '2026-02-20T00:00:00Z', // 5 days from now (due-soon)
          subtypes: ['Wiper Blade'],
        }),
        makeSchedule({
          id: 'sched-4',
          nextDueDate: '2026-02-17T00:00:00Z', // 2 days from now (due-soon, more urgent)
          subtypes: ['Brakes and Traction Control'],
        }),
      ];

      const { attentionItems } = computeVehicleHealth(schedules, []);

      expect(attentionItems).toHaveLength(3); // Max 3 items
      expect(attentionItems[0]).toEqual({
        label: 'Engine Oil',
        urgency: 'overdue',
        daysUntilDue: -10,
        source: 'maintenance',
      });
      expect(attentionItems[1]).toEqual({
        label: 'Cabin Air Filter / Purifier',
        urgency: 'overdue',
        daysUntilDue: -2,
        source: 'maintenance',
      });
      expect(attentionItems[2]).toEqual({
        label: 'Brakes and Traction Control',
        urgency: 'due-soon',
        daysUntilDue: 2,
        source: 'maintenance',
      });
    });
  });

  describe('Max 3 attention items enforcement', () => {
    it('should enforce max 3 attention items when 5 items are present', () => {
      const schedules = [
        makeSchedule({
          id: 'sched-1',
          nextDueDate: '2026-02-05T00:00:00Z', // 10 days ago
          subtypes: ['Item 1'],
        }),
        makeSchedule({
          id: 'sched-2',
          nextDueDate: '2026-02-08T00:00:00Z', // 7 days ago
          subtypes: ['Item 2'],
        }),
        makeSchedule({
          id: 'sched-3',
          nextDueDate: '2026-02-10T00:00:00Z', // 5 days ago
          subtypes: ['Item 3'],
        }),
        makeSchedule({
          id: 'sched-4',
          nextDueDate: '2026-02-12T00:00:00Z', // 3 days ago
          subtypes: ['Item 4'],
        }),
        makeSchedule({
          id: 'sched-5',
          nextDueDate: '2026-02-14T00:00:00Z', // 1 day ago
          subtypes: ['Item 5'],
        }),
      ];

      const { attentionItems } = computeVehicleHealth(schedules, []);

      expect(attentionItems).toHaveLength(3);
      expect(attentionItems[0].label).toBe('Item 1'); // Most overdue
      expect(attentionItems[1].label).toBe('Item 2');
      expect(attentionItems[2].label).toBe('Item 3');
    });
  });

  describe('Inactive schedule handling', () => {
    it('should ignore inactive schedules (isActive: false)', () => {
      const schedules = [
        makeSchedule({
          nextDueDate: '2026-02-10T00:00:00Z', // 5 days ago (overdue)
          subtypes: ['Ignored Item'],
          isActive: false,
        }),
        makeSchedule({
          nextDueDate: '2026-02-20T00:00:00Z', // 5 days from now
          subtypes: ['Active Item'],
          isActive: true,
        }),
      ];

      const { health, attentionItems } = computeVehicleHealth(schedules, []);

      expect(health).toBe('yellow');
      expect(attentionItems).toHaveLength(1);
      expect(attentionItems[0].label).toBe('Active Item');
    });
  });

  describe('Missing date handling', () => {
    it('should ignore schedules without nextDueDate', () => {
      const schedules = [
        makeSchedule({
          nextDueDate: undefined,
          subtypes: ['No Due Date'],
          isActive: true,
        }),
        makeSchedule({
          nextDueDate: '2026-02-20T00:00:00Z', // 5 days from now
          subtypes: ['With Due Date'],
          isActive: true,
        }),
      ];

      const { health, attentionItems } = computeVehicleHealth(schedules, []);

      expect(health).toBe('yellow');
      expect(attentionItems).toHaveLength(1);
      expect(attentionItems[0].label).toBe('With Due Date');
    });

    it('should ignore documents without expirationDate', () => {
      const documents = [
        makeDocument({
          documentType: 'manual',
          expirationDate: null,
        }),
        makeDocument({
          documentType: 'insurance',
          expirationDate: '2026-02-20T00:00:00Z', // 5 days from now
        }),
      ];

      const { health, attentionItems } = computeVehicleHealth([], documents);

      expect(health).toBe('yellow');
      expect(attentionItems).toHaveLength(1);
      expect(attentionItems[0].label).toBe('Insurance');
    });
  });

  describe('Label extraction', () => {
    it('should use first subtype as label when subtypes array is not empty', () => {
      const schedules = [
        makeSchedule({
          nextDueDate: '2026-02-20T00:00:00Z',
          subtypes: ['Air Filter Element', 'Engine Oil'],
        }),
      ];

      const { attentionItems } = computeVehicleHealth(schedules, []);

      expect(attentionItems[0].label).toBe('Air Filter Element');
    });

    it('should use formatted category as label when subtypes array is empty', () => {
      const schedules = [
        makeSchedule({
          nextDueDate: '2026-02-20T00:00:00Z',
          category: 'routine_maintenance',
          subtypes: [],
        }),
      ];

      const { attentionItems } = computeVehicleHealth(schedules, []);

      expect(attentionItems[0].label).toBe('routine maintenance');
    });
  });
});
