import type { PrismaService } from '@modules/shared';
import { EstimateIndustrialRentHandler } from '../queries/estimate-industrial-rent/estimate-industrial-rent.handler';
import { EstimateIndustrialRentQuery } from '../queries/estimate-industrial-rent/estimate-industrial-rent.query';

describe('EstimateIndustrialRentHandler', () => {
  let handler: EstimateIndustrialRentHandler;
  let mockPrisma: { industrialPark: { findMany: ReturnType<typeof vi.fn> } };

  const sampleParks = [
    {
      name: 'KCN Mỹ Phước',
      landRentUsdM2Year: 100,
      rbfRentUsdM2Month: 5.0,
      rbwRentUsdM2Month: 4.5,
      managementFeeUsd: 0.7,
      occupancyRate: 85,
    },
    {
      name: 'KCN Bàu Bàng',
      landRentUsdM2Year: 130,
      rbfRentUsdM2Month: 6.0,
      rbwRentUsdM2Month: 5.0,
      managementFeeUsd: 0.9,
      occupancyRate: 70,
    },
  ];

  beforeEach(() => {
    mockPrisma = {
      industrialPark: { findMany: vi.fn().mockResolvedValue(sampleParks) },
    };
    handler = new EstimateIndustrialRentHandler(mockPrisma as unknown as PrismaService);
  });

  it('returns rent estimate for industrial land', async () => {
    const query = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 5000, 10);
    const result = await handler.execute(query);

    expect(result.pricing_unit).toBe('USD/m²/year');
    expect(result.estimated_rent_usd_m2).toBeGreaterThan(0);
    expect(result.total_monthly_usd).toBeGreaterThan(0);
    expect(result.total_lease_usd).toBeGreaterThan(0);
    expect(result.breakdown.length).toBeGreaterThanOrEqual(1);
  });

  it('returns rent estimate for ready-built factory', async () => {
    const query = new EstimateIndustrialRentQuery('Bình Dương', 'ready_built_factory', 2000, 5);
    const result = await handler.execute(query);

    expect(result.pricing_unit).toBe('USD/m²/month');
    expect(result.estimated_rent_usd_m2).toBeGreaterThan(0);
  });

  it('applies crane surcharge (+8%)', async () => {
    const baseQuery = new EstimateIndustrialRentQuery('Bình Dương', 'ready_built_factory', 2000, 5);
    const craneQuery = new EstimateIndustrialRentQuery('Bình Dương', 'ready_built_factory', 2000, 5, null, true);

    const baseResult = await handler.execute(baseQuery);
    const craneResult = await handler.execute(craneQuery);

    expect(craneResult.estimated_rent_usd_m2).toBeGreaterThan(baseResult.estimated_rent_usd_m2);
    const craneLine = craneResult.breakdown.find((b) => b.item.includes('crane'));
    expect(craneLine).toBeDefined();
  });

  it('applies high power surcharge for >500 kVA', async () => {
    const baseQuery = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 5);
    const powerQuery = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 5, null, false, 1000);

    const baseResult = await handler.execute(baseQuery);
    const powerResult = await handler.execute(powerQuery);

    expect(powerResult.estimated_rent_usd_m2).toBeGreaterThan(baseResult.estimated_rent_usd_m2);
  });

  it('applies wastewater surcharge (+3%)', async () => {
    const baseQuery = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 5);
    const wwQuery = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 5, null, false, null, true);

    const baseResult = await handler.execute(baseQuery);
    const wwResult = await handler.execute(wwQuery);

    expect(wwResult.estimated_rent_usd_m2).toBeGreaterThan(baseResult.estimated_rent_usd_m2);
  });

  it('applies long-term lease discount (>=20yr: -10%)', async () => {
    const shortQuery = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 5);
    const longQuery = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 20);

    const shortResult = await handler.execute(shortQuery);
    const longResult = await handler.execute(longQuery);

    expect(longResult.estimated_rent_usd_m2).toBeLessThan(shortResult.estimated_rent_usd_m2);
    const discountLine = longResult.breakdown.find((b) => b.item.includes('≥20yr'));
    expect(discountLine).toBeDefined();
  });

  it('applies large area discount (>=10,000m²: -7%)', async () => {
    const smallQuery = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 5);
    const largeQuery = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 10000, 5);

    const smallResult = await handler.execute(smallQuery);
    const largeResult = await handler.execute(largeQuery);

    expect(largeResult.estimated_rent_usd_m2).toBeLessThan(smallResult.estimated_rent_usd_m2);
  });

  it('uses national average when no province data available', async () => {
    mockPrisma.industrialPark.findMany.mockResolvedValue([]);

    const query = new EstimateIndustrialRentQuery('Unknown Province', 'industrial_land', 2000, 5);
    const result = await handler.execute(query);

    expect(result.estimated_rent_usd_m2).toBeGreaterThan(0);
    expect(result.market_comparison.province_avg).toBeNull();
  });

  it('uses specific park rent when parkName matches', async () => {
    const query = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 5, 'Mỹ Phước');
    const result = await handler.execute(query);

    expect(result.estimated_rent_usd_m2).toBeGreaterThan(0);
    expect(result.breakdown[0]!.amount).toBe(100);
  });

  it('returns deposit_months=6 for leases >= 10 years', async () => {
    const query = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 15);
    const result = await handler.execute(query);
    expect(result.deposit_months).toBe(6);
  });

  it('returns deposit_months=3 for leases < 10 years', async () => {
    const query = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 5);
    const result = await handler.execute(query);
    expect(result.deposit_months).toBe(3);
  });

  it('includes market comparison with province low/high/avg', async () => {
    const query = new EstimateIndustrialRentQuery('Bình Dương', 'industrial_land', 2000, 5);
    const result = await handler.execute(query);

    expect(result.market_comparison.province_low).toBe(100);
    expect(result.market_comparison.province_high).toBe(130);
    expect(result.market_comparison.province_avg).toBeCloseTo(115, 0);
  });
});
