import { ForbiddenException, NotFoundException } from '@nestjs/common';
import { FlyerImportService } from './flyer-import.service';

describe('FlyerImportService', () => {
  const prismaMock = {
    flyerSession: {
      findFirst: jest.fn(),
      findUnique: jest.fn(),
    },
    flyerItem: {
      findUnique: jest.fn(),
      update: jest.fn(),
    },
    category: {
      findUnique: jest.fn(),
    },
  };

  const createService = () =>
    new FlyerImportService(
      prismaMock as any,
      {} as any,
      {} as any,
      {} as any,
    );

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('getSession', () => {
    it('throws NotFoundException when session is missing', async () => {
      prismaMock.flyerSession.findFirst.mockResolvedValue(null);
      const service = createService();

      await expect(service.getSession(123, 1)).rejects.toBeInstanceOf(NotFoundException);
      expect(prismaMock.flyerSession.findFirst).toHaveBeenCalledWith({
        where: { id: 123, userId: 1 },
        select: {
          id: true,
          sourceFileName: true,
          sourceMimeType: true,
          sourceFileSize: true,
          sourceStorageKey: true,
          items: {
            include: {
              categoryRef: {
                include: {
                  parent: {
                    include: {
                      parent: true,
                    },
                  },
                },
              },
            },
            orderBy: { id: 'asc' },
          },
        },
      });
    });

    it('returns mapped response for owned session', async () => {
      prismaMock.flyerSession.findFirst.mockResolvedValue({
        id: 42,
        items: [
          {
            id: 99,
            rawName: 'Tomat',
            normalizedName: 'tomat',
            brand: null,
            categoryHint: 'Gronsaker',
            price: { toNumber: () => 19.9 },
            priceUnit: 'kg',
            comparisonPrice: null,
            comparisonUnit: null,
            weight: null,
            bundleWeight: null,
            isBundle: false,
            bundleItems: [],
            offerText: 'Max 2 kop/hushall',
            parseConfidence: 0.9,
            parseReasons: ['ai_parsed'],
            matchedProductId: 5,
            matchedProductName: 'Tomat',
            matchedVia: 'exact',
            matchConfidence: 0.95,
            matchReasons: ['normalized_exact'],
          },
        ],
      });
      const service = createService();

      const result = await service.getSession(42, 1);

      expect(result.sessionId).toBe(42);
      expect(result.items).toHaveLength(1);
      expect(result.items[0].flyerItemId).toBe(99);
      expect(result.items[0].matchedVia).toBe('exact');
      expect(result.sourceAvailable).toBe(false);
    });

    it('sanitizes bundleItems without breaking response mapping', async () => {
      prismaMock.flyerSession.findFirst.mockResolvedValue({
        id: 51,
        items: [
          {
            id: 100,
            rawName: 'Kaptenens Favoriter',
            normalizedName: 'kaptenens favoriter',
            brand: 'Kapten Royal',
            categoryHint: 'Fisk',
            price: { toNumber: () => 49.9 },
            priceUnit: 'pkt',
            comparisonPrice: { toNumber: () => 83.17 },
            comparisonUnit: 'kg',
            weight: null,
            bundleWeight: '600g',
            isBundle: true,
            bundleItems: ['  Chumlax 3x100g  ', '', 'Alaska pollock 3x100g'],
            offerText: 'Max 10 kop/hushall',
            parseConfidence: 0.9,
            parseReasons: ['ai_parsed'],
            matchedProductId: null,
            matchedProductName: null,
            matchedVia: 'none',
            matchConfidence: null,
            matchReasons: [],
          },
        ],
      });

      const service = createService();
      const result = await service.getSession(51, 1);

      expect(result.items[0].isBundle).toBe(true);
      expect(result.items[0].bundleItems).toEqual(['Chumlax 3x100g', 'Alaska pollock 3x100g']);
    });
  });

  describe('getLatestSession', () => {
    it('returns empty response when no sessions exist', async () => {
      prismaMock.flyerSession.findFirst.mockResolvedValue(null);
      const service = createService();

      const result = await service.getLatestSession(1);

      expect(result.sessionId).toBeNull();
      expect(result.items).toEqual([]);
      expect(prismaMock.flyerSession.findFirst).toHaveBeenCalledWith({
        where: { userId: 1 },
        orderBy: { createdAt: 'desc' },
        select: {
          id: true,
          sourceFileName: true,
          sourceMimeType: true,
          sourceFileSize: true,
          sourceStorageKey: true,
          items: {
            include: {
              categoryRef: {
                include: {
                  parent: {
                    include: {
                      parent: true,
                    },
                  },
                },
              },
            },
            orderBy: { id: 'asc' },
          },
        },
      });
    });
  });

  describe('updateSessionItem', () => {
    it('updates rawName and category path', async () => {
      prismaMock.flyerSession.findUnique.mockResolvedValue({ id: 7, userId: 1 });
      prismaMock.flyerItem.findUnique.mockResolvedValue({
        id: 12,
        sessionId: 7,
        rawName: 'Tomat',
      });
      prismaMock.category.findUnique.mockResolvedValue({
        id: 3,
        name: 'Tomater',
        parent: { name: 'Grönsaker', parent: { name: 'Mat', parent: null } },
      });
      prismaMock.flyerItem.update.mockResolvedValue({
        id: 12,
        rawName: 'Cocktailtomater',
        normalizedName: 'cocktailtomater',
        brand: null,
        categoryHint: 'Mat > Grönsaker > Tomater',
        categoryId: 3,
        price: null,
        priceUnit: null,
        comparisonPrice: null,
        comparisonUnit: null,
        weight: null,
        bundleWeight: null,
        isBundle: false,
        bundleItems: [],
        offerText: null,
        parseConfidence: 1,
        parseReasons: [],
        matchedProductId: null,
        matchedProductName: null,
        matchedVia: 'none',
        matchConfidence: null,
        matchReasons: [],
        categoryRef: { name: 'Tomater', parent: { name: 'Grönsaker', parent: { name: 'Mat' } } },
      });

      const service = createService();
      const result = await service.updateSessionItem(7, 12, 1, {
        rawName: 'Cocktailtomater',
        categoryId: 3,
      });

      expect(result.rawName).toBe('Cocktailtomater');
      expect(result.categoryId).toBe(3);
      expect(result.category).toBe('Mat > Grönsaker > Tomater');
    });
  });

  describe('getSessionSource', () => {
    it('throws when session belongs to another user', async () => {
      prismaMock.flyerSession.findUnique.mockResolvedValue({
        userId: 99,
        sourceFileName: 'flyer.pdf',
        sourceMimeType: 'application/pdf',
        sourceFileSize: 10,
        sourceData: Buffer.from('abc'),
      });
      const service = createService();

      await expect(service.getSessionSource(1, 1)).rejects.toBeInstanceOf(ForbiddenException);
    });
  });
});
