import { BadRequestException } from '@nestjs/common';
import { Test, type TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ClsService } from 'nestjs-cls';
import { SupplierCostPriceService } from './supplier-cost-price.service';
import { SupplierCostPriceRepository } from './repositories/supplier-cost-price.repository';
import { TransportServiceEntity } from '../../entities/transport-service.entity';
import { VehicleTypeEntity } from '../../entities/vehicle-type.entity';
import { SeasonEntity } from '../../entities/season.entity';
import { SupplierEntity } from '../../entities/supplier.entity';
import { CLS_TENANT_ID, CLS_USER_ID } from '../../common/cls/cls.constants';

describe('SupplierCostPriceService', () => {
  let service: SupplierCostPriceService;

  const TENANT = 'tenant-a';
  const OTHER_TENANT_SERVICE = 'svc-other-tenant';

  const mockRepo = {
    findBySupplier: jest.fn(),
    findBestMatch: jest.fn(),
    ormRepo: { findOne: jest.fn(), update: jest.fn() },
    save: jest.fn(),
    softDelete: jest.fn(),
  };
  const mockCls = {
    get: jest.fn((key: string) => {
      if (key === CLS_TENANT_ID) return TENANT;
      if (key === CLS_USER_ID) return 'user-1';
      return undefined;
    }),
  };
  const mockTransportServiceRepo = { findOne: jest.fn(), manager: { query: jest.fn() } };
  const mockVehicleTypeRepo = { find: jest.fn() };
  const mockSeasonRepo = { find: jest.fn() };
  const mockSupplierRepo = { findOne: jest.fn() };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        SupplierCostPriceService,
        { provide: SupplierCostPriceRepository, useValue: mockRepo },
        { provide: ClsService, useValue: mockCls },
        { provide: getRepositoryToken(TransportServiceEntity), useValue: mockTransportServiceRepo },
        { provide: getRepositoryToken(VehicleTypeEntity), useValue: mockVehicleTypeRepo },
        { provide: getRepositoryToken(SeasonEntity), useValue: mockSeasonRepo },
        { provide: getRepositoryToken(SupplierEntity), useValue: mockSupplierRepo },
      ],
    }).compile();
    service = module.get(SupplierCostPriceService);
  });

  afterEach(() => jest.clearAllMocks());

  describe('findBestMatch', () => {
    it('resolves the destination season from date and prefers that cost row', async () => {
      mockTransportServiceRepo.findOne.mockResolvedValue({
        id: 'svc-1',
        destination_id: 'dest-1',
      });
      mockSeasonRepo.find.mockResolvedValue([
        { id: 'peak-id', start_date: '11-01', end_date: '03-31', is_active: true },
      ]);
      mockRepo.findBestMatch.mockResolvedValue({ id: 'cost-1', cost_price: 150 });

      await service.findBestMatch({
        supplier_id: 'sup-1',
        transport_service_id: 'svc-1',
        vehicle_type_id: 'vt-1',
        mode: 'PVT',
        date: '2026-12-15',
      });

      expect(mockTransportServiceRepo.findOne).toHaveBeenCalledWith({
        where: { id: 'svc-1', tenant_id: TENANT, is_deleted: false },
        select: { id: true, destination_id: true },
      });
      expect(mockRepo.findBestMatch).toHaveBeenCalledWith(
        expect.objectContaining({ season_id: 'peak-id' }),
      );
    });

    it("does not apply another tenant's transport service when resolving season", async () => {
      mockTransportServiceRepo.findOne.mockResolvedValue(null);
      mockRepo.findBestMatch.mockResolvedValue(null);

      await service.findBestMatch({
        supplier_id: 'sup-1',
        transport_service_id: OTHER_TENANT_SERVICE,
        vehicle_type_id: 'vt-1',
        date: '2026-12-15',
      });

      expect(mockSeasonRepo.find).not.toHaveBeenCalled();
      expect(mockRepo.findBestMatch).toHaveBeenCalledWith(
        expect.objectContaining({ season_id: null }),
      );
    });
  });

  describe('upsert floor cap', () => {
    it('rejects a per-vehicle cost above the seasonal floor', async () => {
      mockTransportServiceRepo.manager.query.mockResolvedValue([
        {
          transport_service_id: 'svc-1',
          vehicle_type_id: 'vt-1',
          mode: 'PVT',
          floor_default_price: '80',
          floor_season_1_price: '100',
          floor_season_2_price: null,
          floor_season_3_price: null,
          floor_season_4_price: null,
          floor_season_5_price: null,
          season_id: 'peak-id',
          season_index: '1',
        },
      ]);

      await expect(
        service.upsert({
          supplier_id: 'sup-1',
          transport_service_id: 'svc-1',
          vehicle_type_id: 'vt-1',
          mode: 'PVT',
          season_id: 'peak-id',
          cost_price: 150,
        }),
      ).rejects.toBeInstanceOf(BadRequestException);
      expect(mockRepo.save).not.toHaveBeenCalled();
    });

    it('rejects a per-person cost above the SIC floor', async () => {
      mockTransportServiceRepo.manager.query.mockResolvedValue([
        {
          transport_service_id: 'svc-1',
          vehicle_type_id: null,
          mode: 'SIC',
          floor_default_price: null,
          floor_season_1_price: '40',
          floor_season_2_price: null,
          floor_season_3_price: null,
          floor_season_4_price: null,
          floor_season_5_price: null,
          season_id: 'peak-id',
          season_index: '1',
        },
      ]);

      await expect(
        service.upsert({
          supplier_id: 'sup-1',
          transport_service_id: 'svc-1',
          vehicle_type_id: null,
          mode: 'SIC',
          season_id: 'peak-id',
          cost_price: 41,
        }),
      ).rejects.toBeInstanceOf(BadRequestException);
    });

    it('allows a cost equal to the floor', async () => {
      mockTransportServiceRepo.manager.query.mockResolvedValue([
        {
          transport_service_id: 'svc-1',
          vehicle_type_id: 'vt-1',
          mode: 'PVT',
          floor_default_price: null,
          floor_season_1_price: '100',
          floor_season_2_price: null,
          floor_season_3_price: null,
          floor_season_4_price: null,
          floor_season_5_price: null,
          season_id: 'peak-id',
          season_index: '1',
        },
      ]);
      mockRepo.ormRepo.findOne.mockResolvedValue(null);
      mockRepo.save.mockResolvedValue({ id: 'cost-1' });

      await expect(
        service.upsert({
          supplier_id: 'sup-1',
          transport_service_id: 'svc-1',
          vehicle_type_id: 'vt-1',
          mode: 'PVT',
          season_id: 'peak-id',
          cost_price: 100,
        }),
      ).resolves.toEqual({ id: 'cost-1', created: true });
    });
  });
});
