import { describe, it, expect, vi, beforeEach } from 'vitest';
import { CabinetEventsService } from '../../../src/modules/cabinet-events/cabinet-events.service.js';

describe(CabinetEventsService.name, () => {
  const mockCabinetEventsRepo = {
    create: vi.fn(),
    createMany: vi.fn(),
    findByHousehold: vi.fn(),
    findByCabinetItem: vi.fn(),
    getSpendingSummary: vi.fn(),
    getAvgUnitPriceByMedicine: vi.fn(),
  };

  let service: CabinetEventsService;

  const baseEventData = {
    householdId: 'hh1',
    userId: 'user-1',
    cabinetItemId: 'ci-1',
    medicineId: 'med-1',
    medicineName: 'Metformin',
    eventType: 'purchased' as const,
    quantity: 10,
    quantityBefore: 0,
    quantityAfter: 10,
    sourceType: 'manual' as const,
  };

  beforeEach(() => {
    vi.clearAllMocks();
    service = new CabinetEventsService({
      cabinetEventsRepository: mockCabinetEventsRepo as never,
    });
  });

  describe('logEvent', () => {
    it('delegates to repository create', async () => {
      const created = { _id: 'ev-1', ...baseEventData };
      mockCabinetEventsRepo.create.mockResolvedValue(created);

      const result = await service.logEvent(baseEventData);

      expect(result).toEqual(created);
      expect(mockCabinetEventsRepo.create).toHaveBeenCalledWith(baseEventData);
    });
  });

  describe('logEvents', () => {
    it('delegates to repository createMany', async () => {
      const events = [baseEventData, { ...baseEventData, quantity: 5 }];
      const inserted = events.map((e, i) => ({ _id: `ev-${i}`, ...e }));
      mockCabinetEventsRepo.createMany.mockResolvedValue(inserted);

      const result = await service.logEvents(events);

      expect(result).toEqual(inserted);
      expect(mockCabinetEventsRepo.createMany).toHaveBeenCalledWith(events);
    });

    it('returns empty array for empty input without calling repository', async () => {
      const result = await service.logEvents([]);

      expect(result).toEqual([]);
      expect(mockCabinetEventsRepo.createMany).not.toHaveBeenCalled();
    });
  });

  describe('listEvents', () => {
    it('delegates to repository findByHousehold', async () => {
      const expected = {
        data: [{ _id: 'ev-1' }],
        pagination: { cursor: null, hasMore: false },
      };
      mockCabinetEventsRepo.findByHousehold.mockResolvedValue(expected);

      const query = { limit: 20 };
      const result = await service.listEvents('hh1', query);

      expect(result).toEqual(expected);
      expect(mockCabinetEventsRepo.findByHousehold).toHaveBeenCalledWith('hh1', query);
    });
  });

  describe('getEventsByItem', () => {
    it('delegates to repository findByCabinetItem', async () => {
      const expected = {
        data: [{ _id: 'ev-1', cabinetItemId: 'ci-1' }],
        pagination: { cursor: null, hasMore: false },
      };
      mockCabinetEventsRepo.findByCabinetItem.mockResolvedValue(expected);

      const query = { limit: 20 };
      const result = await service.getEventsByItem('hh1', 'ci-1', query);

      expect(result).toEqual(expected);
      expect(mockCabinetEventsRepo.findByCabinetItem).toHaveBeenCalledWith('hh1', 'ci-1', query);
    });

    it('passes cursor through to repository', async () => {
      const expected = {
        data: [],
        pagination: { cursor: null, hasMore: false },
      };
      mockCabinetEventsRepo.findByCabinetItem.mockResolvedValue(expected);

      const query = { cursor: 'abc123', limit: 10 };
      await service.getEventsByItem('hh1', 'ci-1', query);

      expect(mockCabinetEventsRepo.findByCabinetItem).toHaveBeenCalledWith('hh1', 'ci-1', query);
    });
  });

  describe('getSpendingSummary', () => {
    it('delegates to repository getSpendingSummary', async () => {
      const expected = {
        totalSpent: 100,
        currency: 'USD',
        byMedicine: [],
        byPeriod: [],
      };
      mockCabinetEventsRepo.getSpendingSummary.mockResolvedValue(expected);

      const query = { period: 'month' as const };
      const result = await service.getSpendingSummary('hh1', query);

      expect(result).toEqual(expected);
      expect(mockCabinetEventsRepo.getSpendingSummary).toHaveBeenCalledWith('hh1', query);
    });
  });

  describe('getAvgUnitPrices', () => {
    it('delegates to repository getAvgUnitPriceByMedicine', async () => {
      const expected = new Map([['med-1', { avgUnitPrice: 10, currency: 'USD' }]]);
      mockCabinetEventsRepo.getAvgUnitPriceByMedicine.mockResolvedValue(expected);

      const result = await service.getAvgUnitPrices('hh1', ['med-1']);

      expect(result).toEqual(expected);
      expect(mockCabinetEventsRepo.getAvgUnitPriceByMedicine).toHaveBeenCalledWith('hh1', [
        'med-1',
      ]);
    });
  });
});
