import { ProductRepository } from '../src/base/product/product.repository';
import {
  describe,
  expect,
  it,
  beforeAll,
  afterEach,
  jest,
} from '@jest/globals';
import { ProductWithInventoryBase } from '../src/base/product-with-inventory/product-with-inventory.base';
import { IProductWithInventoryAttrBase } from '../src/interfaces';
import { ProductWithInventoryRepository } from '../src/base/product-with-inventory/product-with-inventory.repository';
import { SessionService, LoginUser } from '@tomei/sso';
import { ApplicationConfig } from '@tomei/config';
import { Activity } from '@tomei/activity-history';

describe('ProductWithInventoryBase', () => {
  class TestProduct extends ProductWithInventoryBase {
    constructor(product?: IProductWithInventoryAttrBase) {
      super(product);
    }
  }

  const data = {
    ProductId: '123',
    Name: 'Test',
    Description: 'Test',
    SKU: 'Test',
    Type: 'Test',
    Remark: 'Test',
    IsTaxableYN: 'N',
    TaxCode: 'Test',
    IsPriceInclusiveTaxYN: 'N',
    Status: 'Active',
    VerifiedYN: 'Y',
    VerifiedById: '123',
    VerifiedAt: new Date(),
    VariantLevels: 0,
    VariantTypeLevel1: '',
    VariantTypeLevel2: '',
    VariantTypeLevel3: '',
    CreatedById: '123',
    CreatedAt: new Date(),
    UpdatedById: '123',
    UpdatedAt: new Date(),
    UpdatedSSYN: 'Y',
    UOM: 'Test',
    TotalUnits: 1,
    TotalUnitsAvailable: 2,
    TotalUnitsInCurrentOrder: 3,
    TotalUnitsSold: 4,
    TotalUnitsReserved: 5,
    TotalUnitsOnConsignment: 6,
    TotalUnitsUnderMaintenance: 7,
    TotalUnitsVoid: 8,
    TotalUnitsInTransit: 9,
    TotalUnitsBackOrdered: 10,
    TotalUnitsPreOrdered: 11,
    StockLowAlertLevel: 12,
    StockReorderLevel: 13,
    BufferStockLevel: 14,
  };
  beforeAll(() => {});

  const pRepoCreate = jest
    .spyOn(ProductRepository.prototype, 'create')
    .mockResolvedValue({
      ...data,
      get: () => data,
    } as any);

  jest.spyOn(ProductRepository.prototype, 'findByPk').mockResolvedValue({
    ...data,
    get: () => data,
  } as any);

  jest.spyOn(SessionService, 'init').mockResolvedValue({
    setUserSession: jest.fn(),
    retrieveUserSession: jest.fn(),
    refreshDuration: jest.fn(),
  } as any);

  jest.spyOn(ApplicationConfig, 'getComponentConfigValue').mockResolvedValue("ezc" as any);
  jest.spyOn(LoginUser.prototype, 'checkPrivileges').mockResolvedValue(true as any);
  jest.spyOn(Activity.prototype, 'create').mockResolvedValue(undefined as any);

  afterEach(() => {
    jest.clearAllMocks();
  });

  describe('init', () => {
    it('should initialize the product base with existing data', async () => {
      const product = new TestProduct({
        ...data,
      });

      expect(product.ProductId).toBe('123');
      expect(product.Name).toBe('Test');
      expect(product.Description).toBe('Test');
      expect(product.SKU).toBe('Test');
      expect(product.Type).toBe('Test');
      expect(product.Remark).toBe('Test');
      expect(product.IsTaxableYN).toBe('N');
      expect(product.TaxCode).toBe('Test');
      expect(product.IsPriceInclusiveTaxYN).toBe('N');
      expect(product.Status).toBe('Active');
      expect(product.VerifiedYN).toBe('Y');
      expect(product.VerifiedById).toBe('123');
      expect(product.VerifiedAt).toBeInstanceOf(Date);
      expect(product.VariantLevels).toBe(0);
      expect(product.VariantTypeLevel1).toBe('');
      expect(product.VariantTypeLevel2).toBe('');
      expect(product.VariantTypeLevel3).toBe('');
      expect(product.CreatedById).toBe('123');
      expect(product.CreatedAt).toBeInstanceOf(Date);
      expect(product.UpdatedById).toBe('123');
      expect(product.UpdatedAt).toBeInstanceOf(Date);
      expect(product.UpdatedSSYN).toBe('Y');
      expect(product.UOM).toBe('Test');
      expect(product.TotalUnits).toBe(1);
      expect(product.TotalUnitsAvailable).toBe(2);
      expect(product.TotalUnitsInCurrentOrder).toBe(3);
      expect(product.TotalUnitsSold).toBe(4);
      expect(product.TotalUnitsReserved).toBe(5);
      expect(product.TotalUnitsOnConsignment).toBe(6);
      expect(product.TotalUnitsUnderMaintenance).toBe(7);
      expect(product.TotalUnitsVoid).toBe(8);
      expect(product.TotalUnitsInTransit).toBe(9);
      expect(product.TotalUnitsBackOrdered).toBe(10);
      expect(product.TotalUnitsPreOrdered).toBe(11);
      expect(product.StockLowAlertLevel).toBe(12);
      expect(product['StockReorderLevel']).toBe(13);
      expect(product.BufferStockLevel).toBe(14);
    });
  });

  describe('create', () => {
    it('should create a new product', async () => {
      jest
        .spyOn(ProductRepository.prototype, 'findOne')
        .mockReturnValueOnce(null as any);

      jest
        .spyOn(ProductWithInventoryRepository.prototype, 'create')
        .mockReturnValue({
          ...data,
          get: () => data,
        } as any);

      const product = new TestProduct({
        ...data,
      });
      const sessionService = await SessionService.init();
      const loginUser = await LoginUser.init(sessionService);
      loginUser.ObjectId = '1234567890';
      await product.create(loginUser);

      expect(pRepoCreate).toBeCalledTimes(1);
    });

    it('should throw error if SKU is already existed', async () => {
      jest.spyOn(ProductRepository.prototype, 'findOne').mockReturnValueOnce({
        ...data,
        get: () => data,
      } as any);

      const product = new TestProduct({
        ...data,
      });
      try {
        const sessionService = await SessionService.init();
        const loginUser = await LoginUser.init(sessionService);
        loginUser.ObjectId = '1234567890';
        await product.create(loginUser);
      } catch (error) {
        expect(error.message).toEqual('SKU is already existed.');
      }

      expect(pRepoCreate).toBeCalledTimes(0);
    });

    it('should throw error if IsPriceInclusiveTaxYN not defined if IsTaxable equal to Y', async () => {
      const wrongData = data;
      wrongData.IsTaxableYN = 'Y';
      wrongData.IsPriceInclusiveTaxYN = 'Y';

      jest
        .spyOn(ProductRepository.prototype, 'findByPk')
        .mockResolvedValueOnce({
          ...wrongData,
          get: () => wrongData,
        } as any);

      jest
        .spyOn(ProductRepository.prototype, 'findOne')
        .mockReturnValueOnce(null as any);

      const product = new TestProduct({
        ...data,
      });
      try {
        const sessionService = await SessionService.init();
        const loginUser = await LoginUser.init(sessionService);
        loginUser.ObjectId = '1234567890';
        await product.create(loginUser);
      } catch (error) {
        expect(error.message).toEqual(
          'IsPriceInclusiveTaxYN is required if IsTaxableYN equal Y.',
        );
      }

      expect(pRepoCreate).toBeCalledTimes(0);
    });

    it('should throw error if TaxCode not defined if IsTaxable equal to Y', async () => {
      const wrongData = data;
      wrongData.IsTaxableYN = 'Y';
      wrongData.TaxCode = null as any;

      jest
        .spyOn(ProductRepository.prototype, 'findByPk')
        .mockResolvedValueOnce({
          ...wrongData,
          get: () => wrongData,
        } as any);

      jest
        .spyOn(ProductRepository.prototype, 'findOne')
        .mockReturnValueOnce(null as any);

      const product = new TestProduct({
        ...data,
      });
      try {
        const sessionService = await SessionService.init();
        const loginUser = await LoginUser.init(sessionService);
        loginUser.ObjectId = '1234567890';
        await product.create(loginUser);
      } catch (error) {
        expect(error.message).toEqual(
          'TaxCode is required if IsTaxableYN equal Y.',
        );
      }

      expect(pRepoCreate).toBeCalledTimes(0);
    });
  });
});
