import { describe, it, expect, vi, beforeEach } from 'vitest';
import { createReportsServer } from '../reports/reports.server';
import type { ReportsDeps } from '../shared/types';

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

function makeDeps(): ReportsDeps {
  return { apiBaseUrl: 'http://localhost:3001/api/v1' };
}

function getToolHandler(server: ReturnType<typeof createReportsServer>, 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>;
}

describe('ReportsServer', () => {
  let server: ReturnType<typeof createReportsServer>;

  beforeEach(() => {
    server = createReportsServer(makeDeps());
    vi.restoreAllMocks();
  });

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

  describe('generate_report', () => {
    it('generates a market overview report', async () => {
      const mockResponse = {
        report_id: 'rpt-001',
        report_type: 'market_overview',
        title: 'Báo cáo thị trường BĐS Quận 7',
        location: 'Quận 7, Hồ Chí Minh',
        generated_at: '2026-04-16T10:00:00Z',
        summary: 'Thị trường Q7 tiếp tục tăng trưởng ổn định...',
        sections: [
          {
            title: 'Tổng quan',
            content: 'Quận 7 ghi nhận 1,200 giao dịch...',
            charts: [{ type: 'line', title: 'Giá trung bình', data: [] }],
          },
        ],
        key_metrics: {
          avgPriceVND: 4_500_000_000,
          totalListings: 1200,
          avgPricePerM2: 55_000_000,
        },
      };

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

      const handler = getToolHandler(server, 'generate_report');
      const result = await handler({
        reportType: 'market_overview',
        location: 'Quận 7, Hồ Chí Minh',
        period: '1y',
        includeForecasts: false,
        includeMacro: false,
        language: 'vi',
      });
      const data = parseResult(result);

      expect(data.reportId).toBe('rpt-001');
      expect(data.reportType).toBe('market_overview');
      expect(data.title).toContain('Quận 7');
      expect(data.sections).toHaveLength(1);
      expect((data.keyMetrics as Record<string, unknown>).totalListings).toBe(1200);
    });

    it('generates report with forecasts', async () => {
      const mockResponse = {
        report_id: 'rpt-002',
        report_type: 'price_forecast',
        title: 'Price Forecast - District 2',
        location: 'Quận 2',
        generated_at: '2026-04-16T10:00:00Z',
        summary: 'Prices expected to increase 5-8% over next 12 months.',
        sections: [],
        key_metrics: {},
        forecasts: {
          price_trend: [
            { period: 'Q3 2026', predicted_change_pct: 2.5 },
            { period: 'Q4 2026', predicted_change_pct: 3.1 },
          ],
          demand_trend: [
            { period: 'Q3 2026', predicted_change_pct: 1.8 },
          ],
          confidence: 0.82,
        },
      };

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

      const handler = getToolHandler(server, 'generate_report');
      const result = await handler({
        reportType: 'price_forecast',
        location: 'Quận 2',
        includeForecasts: true,
        includeMacro: false,
        period: '1y',
        language: 'en',
      });
      const data = parseResult(result);

      expect(data.forecasts).toBeDefined();
      const forecasts = data.forecasts as Record<string, unknown>;
      expect(forecasts.confidence).toBe(0.82);
      expect((forecasts.price_trend as unknown[]).length).toBe(2);
    });

    it('sends correct request body to AI service', async () => {
      const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
        ok: true,
        json: async () => ({
          report_id: 'rpt-003',
          report_type: 'industrial_zone',
          title: 'Test',
          location: 'Bình Dương',
          generated_at: '2026-04-16T10:00:00Z',
          summary: 'Test',
        }),
      } as Response);

      const handler = getToolHandler(server, 'generate_report');
      await handler({
        reportType: 'industrial_zone',
        location: 'Bình Dương',
        propertyType: 'industrial',
        period: '6m',
        includeForecasts: true,
        includeMacro: true,
        language: 'vi',
      });

      expect(fetchSpy).toHaveBeenCalledWith(
        'http://localhost:3001/api/v1/reports/generate',
        expect.objectContaining({
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
        }),
      );

      const body = JSON.parse(fetchSpy.mock.calls[0][1]!.body as string) as Record<string, unknown>;
      expect(body.report_type).toBe('industrial_zone');
      expect(body.location).toBe('Bình Dương');
      expect(body.property_type).toBe('industrial');
      expect(body.period).toBe('6m');
      expect(body.include_forecasts).toBe(true);
      expect(body.include_macro).toBe(true);
      expect(body.language).toBe('vi');
    });

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

      const handler = getToolHandler(server, 'generate_report');
      const result = await handler({
        reportType: 'market_overview',
        location: 'Test',
        period: '1y',
        includeForecasts: false,
        includeMacro: false,
        language: 'vi',
      });

      expect(result.isError).toBe(true);
      const data = parseResult(result);
      expect(data.error).toContain('Report generation error (503)');
    });
  });

  describe('get_macro_data', () => {
    it('retrieves GDP and population data', async () => {
      const mockResponse = {
        province: 'Bình Dương',
        data: {
          gdp: [
            { year: 2023, value: 18.5, unit: 'billion USD', yoy_change: 7.2 },
            { year: 2024, value: 20.1, unit: 'billion USD', yoy_change: 8.6 },
          ],
          population: [
            { year: 2023, value: 2800000, unit: 'people', yoy_change: 3.1 },
            { year: 2024, value: 2890000, unit: 'people', yoy_change: 3.2 },
          ],
        },
        highlights: [
          'GDP growth exceeds national average',
          'Strong FDI inflow from electronics sector',
        ],
      };

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

      const handler = getToolHandler(server, 'get_macro_data');
      const result = await handler({
        province: 'Bình Dương',
        categories: ['gdp', 'population'],
        fromYear: 2023,
        toYear: 2024,
      });
      const data = parseResult(result);

      expect(data.province).toBe('Bình Dương');
      const macroData = data.data as Record<string, unknown[]>;
      expect(macroData.gdp).toHaveLength(2);
      expect(macroData.population).toHaveLength(2);
      expect((macroData.gdp[0] as Record<string, unknown>).year).toBe(2023);
      expect((macroData.gdp[0] as Record<string, unknown>).yoyChange).toBe(7.2);
      expect(data.highlights).toHaveLength(2);
    });

    it('sends correct GET request with query params', async () => {
      const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
        ok: true,
        json: async () => ({ province: 'Hồ Chí Minh', data: {}, highlights: [] }),
      } as Response);

      const handler = getToolHandler(server, 'get_macro_data');
      await handler({
        province: 'Hồ Chí Minh',
        categories: ['fdi', 'infrastructure'],
        fromYear: 2020,
        toYear: 2025,
      });

      const calledUrl = fetchSpy.mock.calls[0][0] as string;
      expect(calledUrl).toContain('http://localhost:3001/api/v1/reports/macro-data?');
      const url = new URL(calledUrl);
      expect(url.searchParams.get('province')).toBe('Hồ Chí Minh');
      expect(url.searchParams.getAll('categories')).toEqual(['fdi', 'infrastructure']);
      expect(url.searchParams.get('fromYear')).toBe('2020');
      expect(url.searchParams.get('toYear')).toBe('2025');

      expect(fetchSpy.mock.calls[0][1]).toEqual(
        expect.objectContaining({ method: 'GET' }),
      );
    });

    it('returns error on service failure', async () => {
      vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
        ok: false,
        status: 404,
        text: async () => 'Province not found',
      } as Response);

      const handler = getToolHandler(server, 'get_macro_data');
      const result = await handler({
        province: 'Unknown',
        categories: ['gdp'],
        fromYear: 2020,
        toYear: 2025,
      });

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