import { createServiceFactory } from '@ngneat/spectator/jest';
import { ReturnProcessStore } from './return-process.store';
import { IDBStorageProvider } from '@isa/core/storage';
import { TabService } from '@isa/core/tabs';
import { patchState } from '@ngrx/signals';
import { setAllEntities, setEntity } from '@ngrx/signals/entities';
import { unprotected } from '@ngrx/signals/testing';
import { Product } from '../schemas';
import { ReturnProcess } from '../models';
import { CreateReturnProcessError } from '../errors/return-process';
import { ProductCategory } from '../questions';

const TEST_ITEMS: Record<number, ReturnProcess['receiptItem']> = {
  1: {
    id: 1,
    actions: [{ key: 'canReturn', value: 'true' }],
    product: {
      ean: '1234567890',
      format: 'TB',
      formatDetail: 'Taschenbuch',
    } as Product,
    quantity: { quantity: 1 },
    receiptNumber: 'R-001',
  },
  2: {
    id: 2,
    actions: [{ key: 'canReturn', value: 'false' }],
    product: {
      ean: '0987654321',
      format: 'GEB',
      formatDetail: 'Buch',
    } as Product,
    quantity: { quantity: 1 },
    receiptNumber: 'R-002',
  },
  3: {
    id: 3,
    actions: [{ key: 'canReturn', value: 'true' }],
    product: {
      ean: '1122334455',
      format: 'AU',
      formatDetail: 'Audio',
    } as Product,
    quantity: { quantity: 1 },
    receiptNumber: 'R-003',
  },
};

describe('ReturnProcessStore', () => {
  const createService = createServiceFactory({
    service: ReturnProcessStore,
    mocks: [IDBStorageProvider, TabService],
  });

  describe('Initialization', () => {
    it('should create an instance of ReturnProcessStore', () => {
      const spectator = createService();
      expect(spectator.service).toBeTruthy();
    });

    it('should have a nextId computed property', () => {
      const spectator = createService();
      expect(spectator.service.nextId()).toBe(1); // Assuming no entities exist initially
    });
  });

  describe('Entity Management', () => {
    it('should remove all entities by process id', () => {
      const spectator = createService();
      const store = spectator.service;

      patchState(
        unprotected(store),
        setAllEntities([
          {
            id: 1,
            processId: 1,
            receiptId: 1,
            receiptItem: TEST_ITEMS[1],
            receiptDate: '',
            answers: {},
            productCategory: ProductCategory.BookCalendar,
            quantity: 1,
            returnReceipt: undefined,
          },
          {
            id: 2,
            processId: 2,
            receiptId: 2,
            receiptItem: TEST_ITEMS[2],
            receiptDate: '',
            answers: {},
            productCategory: ProductCategory.BookCalendar,
            quantity: 1,
            returnReceipt: undefined,
          },
          {
            id: 3,
            processId: 1,
            receiptId: 3,
            receiptItem: TEST_ITEMS[3],
            receiptDate: '',
            answers: {},
            productCategory: ProductCategory.BookCalendar,
            quantity: 1,
            returnReceipt: undefined,
          },
        ] as ReturnProcess[]),
      );

      store.removeAllEntitiesByProcessId(1);
      expect(store.entities()).toHaveLength(1);
      expect(store.entities()[0].processId).toBe(2);
    });

    it('should set an answer for a given entity', () => {
      const spectator = createService();
      const store = spectator.service;

      patchState(
        unprotected(store),
        setAllEntities([
          {
            id: 1,
            processId: 1,
            receiptId: 1,
            receiptItem: TEST_ITEMS[1],
            receiptDate: '',
            answers: {},
            productCategory: ProductCategory.BookCalendar,
            quantity: 1,
            returnReceipt: undefined,
          },
        ] as ReturnProcess[]),
      );

      store.setAnswer(1, 'question1', 'answer1');
      expect(store.entityMap()[1].answers['question1']).toBe('answer1');
    });

    it('should remove an answer for a given entity', () => {
      const spectator = createService();
      const store = spectator.service;

      patchState(
        unprotected(store),
        setEntity({
          id: 1,
          processId: 1,
          answers: { question1: 'answer1', question2: 'answer2' } as Record<
            string,
            unknown
          >,
          receiptDate: new Date().toJSON(),
          receiptItem: TEST_ITEMS[1],
          receiptId: 123,
          productCategory: ProductCategory.BookCalendar,
          quantity: 1,
          returnReceipt: undefined,
        } as ReturnProcess),
      );

      store.removeAnswer(1, 'question1');
      expect(store.entityMap()[1].answers['question1']).toBeUndefined();
    });
  });

  describe('Process Management', () => {
    it('should initialize a new return process', () => {
      const spectator = createService();
      const store = spectator.service;

      store.startProcess({
        processId: 1,
        returns: [
          {
            receipt: {
              id: 1,
              printedDate: '',
              items: [],
              buyer: { buyerNumber: '' },
            },
            items: [
              {
                receiptItem: TEST_ITEMS[1],
                quantity: 1,
                category: ProductCategory.BookCalendar,
              },
            ],
          },
          {
            receipt: {
              id: 2,
              printedDate: '',
              items: [],
              buyer: { buyerNumber: '' },
            },
            items: [
              {
                receiptItem: TEST_ITEMS[3],
                quantity: 1,
                category: ProductCategory.BookCalendar,
              },
            ],
          },
        ],
      });

      expect(store.entities()).toHaveLength(2);
      expect(store.entities()[0].productCategory).toBe(
        ProductCategory.BookCalendar,
      );
      expect(store.entities()[0].quantity).toBe(1);
    });

    it('should throw an error if no returnable items are found', () => {
      const spectator = createService();
      const store = spectator.service;

      expect(() => {
        store.startProcess({
          processId: 1,
          returns: [
            {
              receipt: {
                id: 2,
                printedDate: '',
                items: [],
                buyer: { buyerNumber: '' },
              },
              items: [
                {
                  receiptItem: TEST_ITEMS[2], // Non-returnable item
                  quantity: 1,
                  category: ProductCategory.BookCalendar,
                },
              ],
            },
          ],
        });
      }).toThrow(CreateReturnProcessError);
    });

    it('should throw an error if the number of returnable items does not match the total items', () => {
      const spectator = createService();
      const store = spectator.service;

      expect(() => {
        store.startProcess({
          processId: 1,
          returns: [
            {
              receipt: {
                id: 3,
                printedDate: '',
                items: [],
                buyer: { buyerNumber: '' },
              },
              items: [
                {
                  receiptItem: TEST_ITEMS[1],
                  quantity: 1,
                  category: ProductCategory.BookCalendar,
                },
                {
                  receiptItem: TEST_ITEMS[2],
                  quantity: 1,
                  category: ProductCategory.BookCalendar,
                },
                {
                  receiptItem: TEST_ITEMS[3],
                  quantity: 1,
                  category: ProductCategory.BookCalendar,
                },
              ],
            },
          ],
        });
      }).toThrow(CreateReturnProcessError);
    });
  });
});
