import { type QueryBus } from '@nestjs/cqrs';
import { BatchValuationQuery } from '../../application/queries/batch-valuation/batch-valuation.query';
import { GetDistrictStatsQuery } from '../../application/queries/get-district-stats/get-district-stats.query';
import { GetHeatmapQuery } from '../../application/queries/get-heatmap/get-heatmap.query';
import { GetMarketReportQuery } from '../../application/queries/get-market-report/get-market-report.query';
import { GetPriceTrendQuery } from '../../application/queries/get-price-trend/get-price-trend.query';
import { GetValuationQuery } from '../../application/queries/get-valuation/get-valuation.query';
import { ValuationComparisonQuery } from '../../application/queries/valuation-comparison/valuation-comparison.query';
import { ValuationHistoryQuery } from '../../application/queries/valuation-history/valuation-history.query';
import { AnalyticsController } from '../controllers/analytics.controller';

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

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

  it('getMarketReport executes GetMarketReportQuery with correct params', async () => {
    const expected = { city: 'Hồ Chí Minh', period: '2026-Q1', districts: [] };
    mockQueryBus.execute.mockResolvedValue(expected);

    const result = await controller.getMarketReport({
      city: 'Hồ Chí Minh',
      period: '2026-Q1',
      propertyType: 'APARTMENT',
    } as any);

    expect(mockQueryBus.execute).toHaveBeenCalledWith(
      new GetMarketReportQuery('Hồ Chí Minh', '2026-Q1', 'APARTMENT'),
    );
    expect(result).toBe(expected);
  });

  it('getPriceTrend executes GetPriceTrendQuery with correct params', async () => {
    const expected = { district: 'Quận 1', city: 'Hồ Chí Minh', trend: [] };
    mockQueryBus.execute.mockResolvedValue(expected);

    const result = await controller.getPriceTrend({
      district: 'Quận 1',
      city: 'Hồ Chí Minh',
      propertyType: 'APARTMENT',
      periods: ['2025-Q4', '2026-Q1'],
    } as any);

    expect(mockQueryBus.execute).toHaveBeenCalledWith(
      new GetPriceTrendQuery('Quận 1', 'Hồ Chí Minh', 'APARTMENT', ['2025-Q4', '2026-Q1']),
    );
    expect(result).toBe(expected);
  });

  it('getHeatmap executes GetHeatmapQuery with correct params', async () => {
    const expected = { city: 'Hồ Chí Minh', data: [] };
    mockQueryBus.execute.mockResolvedValue(expected);

    const result = await controller.getHeatmap({
      city: 'Hồ Chí Minh',
      period: '2026-Q1',
    } as any);

    expect(mockQueryBus.execute).toHaveBeenCalledWith(
      new GetHeatmapQuery('Hồ Chí Minh', '2026-Q1'),
    );
    expect(result).toBe(expected);
  });

  it('getDistrictStats executes GetDistrictStatsQuery with correct params', async () => {
    const expected = { city: 'Hà Nội', stats: [] };
    mockQueryBus.execute.mockResolvedValue(expected);

    const result = await controller.getDistrictStats({
      city: 'Hà Nội',
      period: '2026-Q1',
    } as any);

    expect(mockQueryBus.execute).toHaveBeenCalledWith(
      new GetDistrictStatsQuery('Hà Nội', '2026-Q1'),
    );
    expect(result).toBe(expected);
  });

  it('getValuation executes GetValuationQuery with correct params', async () => {
    const expected = { estimatedPrice: '5000000000', confidence: 0.85 };
    mockQueryBus.execute.mockResolvedValue(expected);

    const result = await controller.getValuation({
      propertyId: 'prop-123',
      latitude: undefined,
      longitude: undefined,
      areaM2: undefined,
      propertyType: undefined,
    } as any);

    expect(mockQueryBus.execute).toHaveBeenCalledWith(
      new GetValuationQuery('prop-123', undefined, undefined, undefined, undefined),
    );
    expect(result).toBe(expected);
  });

  it('batchValuation executes BatchValuationQuery with correct params', async () => {
    const expected = [
      { 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);
  });

  it('getValuationHistory executes ValuationHistoryQuery with correct params', async () => {
    const expected = { propertyId: 'prop-1', history: [], totalRecords: 0 };
    mockQueryBus.execute.mockResolvedValue(expected);

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

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

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

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

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

  it('compareValuations executes ValuationComparisonQuery with correct params', async () => {
    const expected = {
      properties: [],
      summary: { highestValue: null, lowestValue: null, averagePricePerM2: 0, averageConfidence: 0 },
    };
    mockQueryBus.execute.mockResolvedValue(expected);

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

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