import { describe, it, expect, vi, beforeEach } from 'vitest';
import { createIndustrialParksServer } from '../industrial-parks/industrial-parks.server';
import type { IndustrialParksDeps } from '../shared/types';

type ToolResult = { content: { type: string; text: string }[]; isError?: boolean };

function createMockClient(searchResult: unknown = { hits: [], found: 0 }) {
  const search = vi.fn().mockResolvedValue(searchResult);
  return {
    collections: vi.fn().mockReturnValue({
      documents: vi.fn().mockReturnValue({ search }),
    }),
    _search: search,
  };
}

function makeDeps(client: ReturnType<typeof createMockClient>): IndustrialParksDeps {
  return {
    typesenseClient: client as unknown as IndustrialParksDeps['typesenseClient'],
    collectionName: 'test-industrial-parks',
    aiServiceBaseUrl: 'http://localhost:8000',
  };
}

function makeHits(docs: Record<string, unknown>[]) {
  return { hits: docs.map((d) => ({ document: d })), found: docs.length, search_time_ms: 3 };
}

function getToolHandler(server: ReturnType<typeof createIndustrialParksServer>, name: string) {
  const tools = (server as unknown as { _registeredTools: Record<string, { handler: (p: unknown) => Promise<ToolResult> }> })._registeredTools;
  const entry = tools[name];
  if (!entry) throw new Error(`Tool "${name}" not found`);
  return entry.handler;
}

function parseResult(result: ToolResult) {
  return JSON.parse(result.content[0].text) as Record<string, unknown>;
}

const samplePark = {
  parkId: 'park-001',
  name: 'KCN VSIP Bình Dương',
  nameEn: 'VSIP Binh Duong',
  developer: 'VSIP Group',
  province: 'Bình Dương',
  region: 'south',
  status: 'operational',
  totalAreaHa: 500,
  remainingAreaHa: 80,
  occupancyRate: 84,
  landRentUsdM2Year: 85,
  rbfRentUsdM2Month: 4.5,
  rbwRentUsdM2Month: 3.8,
  targetIndustries: ['electronics', 'logistics'],
  tenantCount: 120,
};

describe('IndustrialParksServer', () => {
  let client: ReturnType<typeof createMockClient>;
  let server: ReturnType<typeof createIndustrialParksServer>;

  beforeEach(() => {
    client = createMockClient(makeHits([samplePark]));
    server = createIndustrialParksServer(makeDeps(client));
    vi.restoreAllMocks();
  });

  it('creates server with correct name', () => {
    expect(server).toBeDefined();
  });

  describe('search_industrial_parks', () => {
    it('searches with basic query', async () => {
      const handler = getToolHandler(server, 'search_industrial_parks');
      const result = await handler({ query: 'KCN Bình Dương', page: 1, perPage: 20 });

      expect(client.collections).toHaveBeenCalledWith('test-industrial-parks');
      expect(client._search).toHaveBeenCalledWith(
        expect.objectContaining({
          q: 'KCN Bình Dương',
          query_by: 'name,nameEn,developer,operator,province,targetIndustries',
        }),
      );

      const data = parseResult(result);
      expect(data.totalFound).toBe(1);
      expect(data.results).toHaveLength(1);
      expect((data.results as Record<string, unknown>[])[0].parkId).toBe('park-001');
    });

    it('applies province and status filters', async () => {
      client._search.mockResolvedValue(makeHits([]));
      const handler = getToolHandler(server, 'search_industrial_parks');

      await handler({
        query: '*',
        province: 'Bình Dương',
        status: 'operational',
        page: 1,
        perPage: 20,
      });

      const filterBy = client._search.mock.calls[0][0].filter_by as string;
      expect(filterBy).toContain('province:=Bình Dương');
      expect(filterBy).toContain('status:=operational');
    });

    it('applies area and rent filters', async () => {
      client._search.mockResolvedValue(makeHits([]));
      const handler = getToolHandler(server, 'search_industrial_parks');

      await handler({
        query: '*',
        minAreaHa: 10,
        maxRentUsdM2: 100,
        page: 1,
        perPage: 20,
      });

      const filterBy = client._search.mock.calls[0][0].filter_by as string;
      expect(filterBy).toContain('remainingAreaHa:>=10');
      expect(filterBy).toContain('landRentUsdM2Year:<=100');
    });

    it('applies geo filter when coordinates provided', async () => {
      client._search.mockResolvedValue(makeHits([]));
      const handler = getToolHandler(server, 'search_industrial_parks');

      await handler({
        query: '*',
        latitude: 11.0,
        longitude: 106.6,
        radiusKm: 30,
        page: 1,
        perPage: 20,
      });

      const filterBy = client._search.mock.calls[0][0].filter_by as string;
      expect(filterBy).toContain('location:(11, 106.6, 30 km)');
    });

    it('sorts by rent ascending', async () => {
      client._search.mockResolvedValue(makeHits([]));
      const handler = getToolHandler(server, 'search_industrial_parks');

      await handler({ query: '*', sortBy: 'rent_asc', page: 1, perPage: 20 });
      expect(client._search.mock.calls[0][0].sort_by).toBe('landRentUsdM2Year:asc');
    });

    it('sorts by remaining area descending', async () => {
      client._search.mockResolvedValue(makeHits([]));
      const handler = getToolHandler(server, 'search_industrial_parks');

      await handler({ query: '*', sortBy: 'area_desc', page: 1, perPage: 20 });
      expect(client._search.mock.calls[0][0].sort_by).toBe('remainingAreaHa:desc');
    });

    it('returns empty results gracefully', async () => {
      client._search.mockResolvedValue(makeHits([]));
      const handler = getToolHandler(server, 'search_industrial_parks');
      const result = await handler({ query: 'nonexistent', page: 1, perPage: 20 });
      const data = parseResult(result);

      expect(data.totalFound).toBe(0);
      expect(data.results).toHaveLength(0);
    });
  });

  describe('analyze_industrial_location', () => {
    it('calls AI service and returns analysis', async () => {
      const mockResponse = {
        overall_score: 8.5,
        connectivity: {
          nearest_port: { name: 'Cảng Cát Lái', distanceKm: 25 },
          nearest_airport: { name: 'Tân Sơn Nhất', distanceKm: 30 },
          nearest_highway: { name: 'QL1A', distanceKm: 2 },
        },
        infrastructure: {
          power_availability: '110kV substation on-site',
          water_supply: 'Municipal + on-site treatment',
          wastewater_treatment: 'Central WWTP 5000m³/day',
          telecom: 'Fiber optic available',
        },
        labor_market: {
          worker_pool_radius_30km: 500000,
          average_wage_usd: 280,
          nearby_universities: ['ĐH Bình Dương', 'ĐH Thủ Dầu Một'],
        },
        incentives: ['CIT 10% for 15 years', 'Import duty exemption'],
        risks: ['Flooding risk in rainy season'],
      };

      vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
        ok: true,
        json: async () => mockResponse,
      } as Response);

      const handler = getToolHandler(server, 'analyze_industrial_location');
      const result = await handler({ latitude: 11.0, longitude: 106.6 });
      const data = parseResult(result);

      expect(data.overallScore).toBe(8.5);
      expect((data.connectivity as Record<string, unknown>).nearestPort).toEqual({ name: 'Cảng Cát Lái', distanceKm: 25 });
      expect((data.laborMarket as Record<string, unknown>).nearbyUniversities).toHaveLength(2);
      expect(data.incentives).toHaveLength(2);
    });

    it('returns error on AI service failure', async () => {
      vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
        ok: false,
        status: 500,
        text: async () => 'Internal Server Error',
      } as Response);

      const handler = getToolHandler(server, 'analyze_industrial_location');
      const result = await handler({ latitude: 11.0, longitude: 106.6 });

      expect(result.isError).toBe(true);
      const data = parseResult(result);
      expect(data.error).toContain('Location analysis error');
    });
  });

  describe('estimate_industrial_rent', () => {
    it('calls AI service and returns rent estimate', async () => {
      const mockResponse = {
        estimated_rent_usd_m2: 4.2,
        pricing_unit: 'USD/m²/month',
        total_monthly_usd: 42000,
        total_lease_usd: 5040000,
        management_fee_usd_m2: 0.5,
        deposit_months: 3,
        market_comparison: {
          province_low: 3.0,
          province_high: 6.5,
          province_avg: 4.5,
        },
        breakdown: [
          { item: 'Base rent', amount: 35000 },
          { item: 'Management fee', amount: 5000 },
          { item: 'Utilities surcharge', amount: 2000 },
        ],
      };

      vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
        ok: true,
        json: async () => mockResponse,
      } as Response);

      const handler = getToolHandler(server, 'estimate_industrial_rent');
      const result = await handler({
        province: 'Bình Dương',
        propertyType: 'ready_built_factory',
        areaM2: 10000,
        leaseDurationYears: 10,
      });
      const data = parseResult(result);

      expect(data.estimatedRentUsdM2).toBe(4.2);
      expect(data.totalMonthlyUsd).toBe(42000);
      expect((data.marketComparison as Record<string, unknown>).provinceAvg).toBe(4.5);
      expect(data.breakdown).toHaveLength(3);
    });

    it('returns error on AI service failure', async () => {
      vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
        ok: false,
        status: 400,
        text: async () => 'Invalid province',
      } as Response);

      const handler = getToolHandler(server, 'estimate_industrial_rent');
      const result = await handler({
        province: 'Invalid',
        propertyType: 'industrial_land',
        areaM2: 5000,
        leaseDurationYears: 10,
      });

      expect(result.isError).toBe(true);
      const data = parseResult(result);
      expect(data.error).toContain('Rent estimation error');
    });
  });
});
