import { Test, type TestingModule } from '@nestjs/testing';
import { ConflictException } from '@nestjs/common';
import { VehicleTypeService } from './vehicle-type.service';
import { VehicleTypeRepository } from './repositories/vehicle-type.repository';
import { VehicleTypeMediaRepository } from './repositories/vehicle-type-media.repository';
import { MediaService } from '../../common/media/media.service';

describe('VehicleTypeService', () => {
  let service: VehicleTypeService;
  let qb: {
    insert: jest.Mock;
    into: jest.Mock;
    values: jest.Mock;
    orIgnore: jest.Mock;
    execute: jest.Mock;
  };
  let manager: { query: jest.Mock; createQueryBuilder: jest.Mock };
  let mockRepo: {
    findByName: jest.Mock;
    save: jest.Mock;
    currentTenantId: jest.Mock;
    currentUserId: jest.Mock;
    manager: typeof manager;
  };

  beforeEach(async () => {
    qb = {
      insert: jest.fn().mockReturnThis(),
      into: jest.fn().mockReturnThis(),
      values: jest.fn().mockReturnThis(),
      orIgnore: jest.fn().mockReturnThis(),
      execute: jest.fn().mockResolvedValue(undefined),
    };
    manager = {
      query: jest.fn(),
      createQueryBuilder: jest.fn().mockReturnValue(qb),
    };
    mockRepo = {
      findByName: jest.fn(),
      save: jest.fn(),
      currentTenantId: jest.fn().mockReturnValue('tenant-1'),
      currentUserId: jest.fn().mockReturnValue('user-1'),
      manager,
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        VehicleTypeService,
        { provide: VehicleTypeRepository, useValue: mockRepo },
        { provide: VehicleTypeMediaRepository, useValue: {} },
        {
          provide: MediaService,
          useValue: { list: jest.fn(), prepare: jest.fn(), sync: jest.fn() },
        },
      ],
    }).compile();
    service = module.get(VehicleTypeService);
  });

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

  describe('create', () => {
    it('throws ConflictException on duplicate name', async () => {
      mockRepo.findByName.mockResolvedValue({ id: 'existing' });
      await expect(service.create({ name: 'SEDAN' } as never)).rejects.toThrow(ConflictException);
    });

    it('rejects operational_cap > seating_capacity', async () => {
      mockRepo.findByName.mockResolvedValue(null);
      await expect(
        service.create({
          name: 'SEDAN',
          seating_capacity: 4,
          operational_cap: 6,
        } as never),
      ).rejects.toThrow(ConflictException);
    });

    it('saves + triggers backfill when active', async () => {
      mockRepo.findByName.mockResolvedValue(null);
      mockRepo.save.mockResolvedValue({ id: 'vt-1', is_active: true });
      manager.query.mockResolvedValue([
        { id: 'svc-1', destination_id: 'dest-1', category: 'AIRPORT', currency_id: 'INR' },
        { id: 'svc-2', destination_id: 'dest-2', category: 'CITY', currency_id: null }, // skipped — no currency
      ]);

      const result = await service.create({ name: 'SUV' } as never);

      expect(result.id).toBe('vt-1');
      // backfill ran once on the row with a currency
      expect(qb.execute).toHaveBeenCalledTimes(1);
      expect(qb.values).toHaveBeenCalledWith(
        expect.objectContaining({
          tenant_id: 'tenant-1',
          vehicle_type_id: 'vt-1',
          transport_service_id: 'svc-1',
          mode: 'PVT',
          currency_id: 'INR',
          created_by: 'user-1',
        }),
      );
    });

    it('swallows backfill errors — create still succeeds', async () => {
      mockRepo.findByName.mockResolvedValue(null);
      mockRepo.save.mockResolvedValue({ id: 'vt-1', is_active: true });
      manager.query.mockRejectedValue(new Error('DB unreachable'));

      await expect(service.create({ name: 'SUV' } as never)).resolves.toEqual(
        expect.objectContaining({ id: 'vt-1' }),
      );
    });
  });
});
