import { type QueryBus } from '@nestjs/cqrs';
import { BatchValuationQuery } from '../../application/queries/batch-valuation/batch-valuation.query';
import { IndustrialValuationQuery } from '../../application/queries/industrial-valuation/industrial-valuation.query';
import { ValuationComparisonQuery } from '../../application/queries/valuation-comparison/valuation-comparison.query';
import { ValuationExplanationQuery } from '../../application/queries/valuation-explanation/valuation-explanation.query';
import { ValuationHistoryQuery } from '../../application/queries/valuation-history/valuation-history.query';
import { AvmController } from '../controllers/avm.controller';

describe('AvmController', () => {
  let controller: AvmController;
  let mockQueryBus: { execute: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockQueryBus = { execute: vi.fn() };
    controller = new AvmController(mockQueryBus as unknown as QueryBus);
  });

  describe('POST /avm/batch', () => {
    it('dispatches BatchValuationQuery with property IDs', async () => {
      const expected = {
        results: [
          { propertyId: 'prop-1', valuation: { estimatedPrice: '5000000000' } },
          { propertyId: 'prop-2', valuation: { estimatedPrice: '6000000000' } },
        ],
      };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.batchValuation({
        propertyIds: ['prop-1', 'prop-2'],
      } as any);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(
        new BatchValuationQuery(['prop-1', 'prop-2']),
      );
      expect(result).toBe(expected);
    });
  });

  describe('GET /avm/history/:propertyId', () => {
    it('dispatches ValuationHistoryQuery with propertyId and limit', async () => {
      const expected = { propertyId: 'prop-1', history: [], totalRecords: 0 };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.getHistory('prop-1', { limit: 25 } as any);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(
        new ValuationHistoryQuery('prop-1', 25),
      );
      expect(result).toBe(expected);
    });

    it('defaults limit to 50 when not provided', async () => {
      const expected = { propertyId: 'prop-1', history: [], totalRecords: 0 };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.getHistory('prop-1', {} as any);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(
        new ValuationHistoryQuery('prop-1', 50),
      );
      expect(result).toBe(expected);
    });
  });

  describe('GET /avm/compare', () => {
    it('dispatches ValuationComparisonQuery with parsed IDs', async () => {
      const expected = {
        properties: [],
        summary: { highestValue: null, lowestValue: null, averagePricePerM2: 0, averageConfidence: 0 },
      };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.compare({
        ids: ['prop-1', 'prop-2', 'prop-3'],
      } as any);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(
        new ValuationComparisonQuery(['prop-1', 'prop-2', 'prop-3']),
      );
      expect(result).toBe(expected);
    });

    it('handles two property IDs (minimum)', async () => {
      const expected = { properties: [], summary: {} };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.compare({
        ids: ['prop-1', 'prop-2'],
      } as any);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(
        new ValuationComparisonQuery(['prop-1', 'prop-2']),
      );
      expect(result).toBe(expected);
    });
  });

  describe('GET /avm/explain', () => {
    it('dispatches ValuationExplanationQuery with valuationId', async () => {
      const expected = {
        valuationId: 'val-1',
        propertyId: 'prop-1',
        modelVersion: 'avm-v2.0',
        confidence: 0.85,
        confidenceExplanation: 'Mức độ tin cậy cao (85%).',
        estimatedPrice: '5000000000',
        pricePerM2: 75000000,
        topDrivers: [{ feature: 'location', importance: 0.45 }],
        comparables: [],
        valuedAt: '2026-04-15T10:00:00.000Z',
      };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.explain({ valuationId: 'val-1' } as any);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(
        new ValuationExplanationQuery('val-1'),
      );
      expect(result).toBe(expected);
    });
  });

  describe('POST /avm/industrial', () => {
    const industrialDto = {
      province: 'Bình Dương',
      region: 'south',
      parkOccupancyRate: 0.85,
      parkAreaHa: 500,
      parkAgeYears: 10,
      distanceToPortKm: 25,
      distanceToAirportKm: 40,
      distanceToHighwayKm: 5,
      propertyType: 'factory',
      areaM2: 5000,
      ceilingHeightM: 10,
      loadingDocks: 4,
      zoning: 'general_industrial',
    };

    it('dispatches IndustrialValuationQuery with all required fields', async () => {
      const expected = {
        estimatedRentUsdM2: 5.2,
        confidence: 0.65,
        rentRangeLowUsdM2: 4.16,
        rentRangeHighUsdM2: 6.24,
        annualRentUsdM2: 62.4,
        totalMonthlyRentUsd: 26000,
        comparables: [],
        drivers: [],
        modelVersion: 'heuristic-v1',
      };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.industrialValuation(industrialDto as any);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(
        new IndustrialValuationQuery(
          'Bình Dương',
          'south',
          0.85,
          500,
          10,
          25,
          40,
          5,
          'factory',
          5000,
          10,
          undefined,
          undefined,
          undefined,
          4,
          'general_industrial',
          undefined,
          undefined,
          undefined,
          undefined,
        ),
      );
      expect(result).toBe(expected);
    });

    it('passes optional fields when provided', async () => {
      const fullDto = {
        ...industrialDto,
        floorLoadTonM2: 3,
        powerCapacityKva: 2000,
        buildingCoverage: 0.6,
        industryDemandIndex: 0.7,
        fdiProvinceMusd: 3000,
        laborCostProvinceVnd: 8000000,
        logisticsConnectivityScore: 0.75,
      };
      const expected = {
        estimatedRentUsdM2: 5.8,
        confidence: 0.72,
        comparables: [],
        drivers: [],
      };
      mockQueryBus.execute.mockResolvedValue(expected);

      const result = await controller.industrialValuation(fullDto as any);

      const call = mockQueryBus.execute.mock.calls[0]![0] as IndustrialValuationQuery;
      expect(call.province).toBe('Bình Dương');
      expect(call.floorLoadTonM2).toBe(3);
      expect(call.powerCapacityKva).toBe(2000);
      expect(call.buildingCoverage).toBe(0.6);
      expect(call.logisticsConnectivityScore).toBe(0.75);
      expect(result).toBe(expected);
    });
  });
});
