import { describe, it, expect } from 'vitest';
import { FreshnessCalculatorService } from '../../../src/modules/pantry/freshness-calculator.service.js';
import { ItemStatus, FreshnessUrgency, FreshnessSource, StorageLocation } from '@meshitrack/shared';

describe(FreshnessCalculatorService.name, () => {
  const service = new FreshnessCalculatorService();

  const baseItem = {
    status: ItemStatus.SEALED,
    storageLocation: StorageLocation.FRIDGE,
    purchaseDate: new Date('2024-01-01'),
    expirationDate: undefined,
    openedDate: undefined,
    preparedDate: undefined,
  };

  const rule = {
    shelfLifeDays: 14,
    openedLifeDays: 7,
    freezerLifeDays: 90,
  };

  describe('calculate', () => {
    it('uses packaging expiration date when present', () => {
      const item = { ...baseItem, expirationDate: new Date('2099-12-31') };
      const result = service.calculate(item, rule);
      expect(result.source).toBe(FreshnessSource.PACKAGING);
      expect(result.estimatedExpiryDate).toEqual(new Date('2099-12-31'));
    });

    it('falls back to 7-day default when no rule provided', () => {
      const result = service.calculate(baseItem, null);
      expect(result.source).toBe(FreshnessSource.RULE);
      const expected = new Date('2024-01-01');
      expected.setDate(expected.getDate() + 7);
      expect(result.estimatedExpiryDate).toEqual(expected);
    });

    it('uses freezerLifeDays for freezer storage', () => {
      const item = { ...baseItem, storageLocation: StorageLocation.FREEZER };
      const result = service.calculate(item, rule);
      expect(result.source).toBe(FreshnessSource.RULE);
      const expected = new Date('2024-01-01');
      expected.setDate(expected.getDate() + 90);
      expect(result.estimatedExpiryDate).toEqual(expected);
    });

    it('uses shelfLifeDays for sealed items in freezer without freezerLifeDays', () => {
      const item = { ...baseItem, storageLocation: StorageLocation.FREEZER };
      const ruleNoFreezer = { shelfLifeDays: 14, openedLifeDays: 7 };
      const result = service.calculate(item, ruleNoFreezer);
      const expected = new Date('2024-01-01');
      expected.setDate(expected.getDate() + 14);
      expect(result.estimatedExpiryDate).toEqual(expected);
    });

    it('uses openedLifeDays for opened items', () => {
      const openedDate = new Date('2024-01-05');
      const item = {
        ...baseItem,
        status: ItemStatus.OPENED,
        openedDate,
      };
      const result = service.calculate(item, rule);
      const expected = new Date('2024-01-05');
      expected.setDate(expected.getDate() + 7);
      expect(result.estimatedExpiryDate).toEqual(expected);
    });

    it('uses openedLifeDays for prepared items with openedDate', () => {
      const openedDate = new Date('2024-01-05');
      const item = {
        ...baseItem,
        status: ItemStatus.PREPARED,
        openedDate,
      };
      const result = service.calculate(item, rule);
      const expected = new Date('2024-01-05');
      expected.setDate(expected.getDate() + 7);
      expect(result.estimatedExpiryDate).toEqual(expected);
    });

    it('uses shelfLifeDays for opened item without openedDate', () => {
      const item = { ...baseItem, status: ItemStatus.OPENED };
      const result = service.calculate(item, rule);
      const expected = new Date('2024-01-01');
      expected.setDate(expected.getDate() + 14);
      expect(result.estimatedExpiryDate).toEqual(expected);
    });

    it('uses shelfLifeDays for sealed items', () => {
      const result = service.calculate(baseItem, rule);
      const expected = new Date('2024-01-01');
      expected.setDate(expected.getDate() + 14);
      expect(result.estimatedExpiryDate).toEqual(expected);
    });

    it('computes daysRemaining and urgency', () => {
      const future = new Date();
      future.setDate(future.getDate() + 10);
      const item = { ...baseItem, expirationDate: future };
      const result = service.calculate(item, rule);
      expect(result.daysRemaining).toBeGreaterThan(5);
      expect(result.urgency).toBe(FreshnessUrgency.FRESH);
    });
  });

  describe('isActive', () => {
    it('returns true for sealed', () => {
      expect(service.isActive('sealed')).toBe(true);
    });
    it('returns true for opened', () => {
      expect(service.isActive('opened')).toBe(true);
    });
    it('returns true for prepared', () => {
      expect(service.isActive('prepared')).toBe(true);
    });
    it('returns false for consumed', () => {
      expect(service.isActive('consumed')).toBe(false);
    });
    it('returns false for discarded', () => {
      expect(service.isActive('discarded')).toBe(false);
    });
    it('returns false for expired', () => {
      expect(service.isActive('expired')).toBe(false);
    });
  });

  describe('mapUrgency', () => {
    it('returns FRESH for > 5 days', () => {
      expect(service.mapUrgency(6)).toBe(FreshnessUrgency.FRESH);
    });
    it('returns USE_SOON for 2-5 days', () => {
      expect(service.mapUrgency(3)).toBe(FreshnessUrgency.USE_SOON);
    });
    it('returns URGENT for 0-1 days', () => {
      expect(service.mapUrgency(1)).toBe(FreshnessUrgency.URGENT);
    });
    it('returns CHECK for -1 to -3 days', () => {
      expect(service.mapUrgency(-1)).toBe(FreshnessUrgency.CHECK);
    });
    it('returns EXPIRED for < -3 days', () => {
      expect(service.mapUrgency(-4)).toBe(FreshnessUrgency.EXPIRED);
    });
    it('returns USE_SOON for exactly 2', () => {
      expect(service.mapUrgency(2)).toBe(FreshnessUrgency.USE_SOON);
    });
    it('returns USE_SOON for exactly 5', () => {
      expect(service.mapUrgency(5)).toBe(FreshnessUrgency.USE_SOON);
    });
    it('returns URGENT for exactly 0', () => {
      expect(service.mapUrgency(0)).toBe(FreshnessUrgency.URGENT);
    });
    it('returns CHECK for exactly -3', () => {
      expect(service.mapUrgency(-3)).toBe(FreshnessUrgency.CHECK);
    });
  });
});
