import { PuppeteerPdfGeneratorService } from '../services/pdf-generator.service';

const { mockPdf, mockSetContent, mockNewPage, mockClose } = vi.hoisted(() => {
  const mockPdf = vi.fn();
  const mockSetContent = vi.fn();
  const mockNewPage = vi.fn().mockResolvedValue({
    setContent: mockSetContent,
    pdf: mockPdf,
  });
  const mockClose = vi.fn();
  return { mockPdf, mockSetContent, mockNewPage, mockClose };
});

vi.mock('puppeteer', () => ({
  default: {
    launch: vi.fn().mockResolvedValue({
      newPage: mockNewPage,
      close: mockClose,
    }),
  },
}));

vi.mock('fs', () => ({
  writeFileSync: vi.fn(),
}));

describe('PuppeteerPdfGeneratorService', () => {
  let service: PuppeteerPdfGeneratorService;

  beforeEach(() => {
    vi.clearAllMocks();
    service = new PuppeteerPdfGeneratorService();
  });

  const buildContent = (overrides: Record<string, unknown> = {}): Record<string, unknown> => ({
    reportType: 'INDUSTRIAL_LOCATION',
    province: 'Bình Dương',
    generatedAt: '2026-04-01T00:00:00.000Z',
    sections: {
      executive_summary: {
        title: 'Tóm tắt',
        content: 'Báo cáo tổng quan thị trường KCN Bình Dương.',
      },
      economic_indicators: {
        title: 'Chỉ số kinh tế',
        data: {
          gdp: [
            { period: '2024', value: 150000, unit: 'tỷ VND' },
            { period: '2025', value: 165000, unit: 'tỷ VND' },
          ],
        },
        charts: {
          gdp_trend: [
            { period: '2024', value: 150000, unit: 'tỷ VND' },
            { period: '2025', value: 165000, unit: 'tỷ VND' },
          ],
        },
      },
      infrastructure: {
        title: 'Hạ tầng',
        projects: [
          { name: 'KCN VSIP III', category: 'industrial_park', status: 'under_construction', investmentVND: 5000000000000 },
        ],
        summary: {
          total: 1,
          byCategory: { industrial_park: 1 },
        },
      },
    },
    ...overrides,
  });

  it('generates a PDF and returns the file path', async () => {
    const pdfBuffer = Buffer.from('fake-pdf-content');
    mockPdf.mockResolvedValue(pdfBuffer);

    const result = await service.generatePdf('report-123', buildContent());

    expect(result).toMatch(/goodgo-report-report-123-\d+\.pdf$/);
    expect(mockNewPage).toHaveBeenCalledOnce();
    expect(mockSetContent).toHaveBeenCalledOnce();
    expect(mockPdf).toHaveBeenCalledWith(
      expect.objectContaining({
        format: 'A4',
        printBackground: true,
        displayHeaderFooter: true,
      }),
    );
    expect(mockClose).toHaveBeenCalledOnce();
  });

  it('sets page content with waitUntil networkidle0', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-456', buildContent());

    expect(mockSetContent).toHaveBeenCalledWith(
      expect.any(String),
      { waitUntil: 'networkidle0' },
    );
  });

  it('includes cover page with title, type label, and date', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-789', buildContent());

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).toContain('Bình Dương');
    expect(html).toContain('Vị trí khu công nghiệp');
    expect(html).toContain('class="cover"');
    expect(html).toContain('GoodGo');
  });

  it('includes table of contents', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-toc', buildContent());

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).toContain('Mục lục');
    expect(html).toContain('class="toc"');
    expect(html).toContain('Tóm tắt');
    expect(html).toContain('Chỉ số kinh tế');
    expect(html).toContain('Hạ tầng');
  });

  it('renders SVG charts from chart data', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-charts', buildContent());

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).toContain('<svg');
    expect(html).toContain('chart-container');
    expect(html).toContain('Gdp Trend');
  });

  it('renders data tables', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-tables', buildContent());

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).toContain('data-table');
    expect(html).toContain('Kỳ');
    expect(html).toContain('Giá trị');
  });

  it('renders infrastructure projects table', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-infra', buildContent());

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).toContain('KCN VSIP III');
    expect(html).toContain('Dự án');
    expect(html).toContain('Vốn đầu tư (VND)');
  });

  it('includes Be Vietnam Pro font import', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-font', buildContent());

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).toContain('Be+Vietnam+Pro');
    expect(html).toContain("font-family: 'Be Vietnam Pro'");
  });

  it('includes methodology and disclaimer section', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-method', buildContent());

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).toContain('Phương pháp');
    expect(html).toContain('Miễn trừ trách nhiệm');
    expect(html).toContain('research@goodgo.vn');
  });

  it('includes page number footer', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-footer', buildContent());

    const pdfOptions = mockPdf.mock.calls[0][0];
    expect(pdfOptions.footerTemplate).toContain('pageNumber');
    expect(pdfOptions.footerTemplate).toContain('totalPages');
    expect(pdfOptions.footerTemplate).toContain('GoodGo AI Report');
  });

  it('escapes HTML in user-provided content', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    const content = buildContent({
      province: '<script>alert("xss")</script>',
    });

    await service.generatePdf('report-xss', content);

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).not.toContain('<script>');
    expect(html).toContain('&lt;script&gt;');
  });

  it('closes browser even if PDF generation fails', async () => {
    mockPdf.mockRejectedValue(new Error('Render failed'));

    await expect(service.generatePdf('report-fail', buildContent())).rejects.toThrow('Render failed');

    expect(mockClose).toHaveBeenCalledOnce();
  });

  it('handles empty sections gracefully', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    const content = buildContent({ sections: {} });
    await service.generatePdf('report-empty', content);

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).toContain('class="cover"');
    expect(html).toContain('class="toc"');
  });

  it('handles missing content fields with defaults', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-defaults', {});

    const html = mockSetContent.mock.calls[0][0] as string;
    expect(html).toContain('class="cover"');
  });

  it('uses A4 format with 2cm margins', async () => {
    mockPdf.mockResolvedValue(Buffer.from('pdf'));

    await service.generatePdf('report-margins', buildContent());

    expect(mockPdf).toHaveBeenCalledWith(
      expect.objectContaining({
        format: 'A4',
        margin: { top: '2cm', right: '2cm', bottom: '2cm', left: '2cm' },
      }),
    );
  });
});
