import { promises as fs } from 'node:fs';
import path from 'node:path';
import os from 'node:os';

import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { PDFDocument } from 'pdf-lib';

import { fillEoiFormFields, loadEoiTemplatePdf } from '@/lib/pdf/fill-eoi-form';
import type { EoiContext } from '@/lib/services/eoi-context';

// ─── Test PDF builder (synthetic source PDF with the same field names) ───────

async function buildSyntheticEoiPdf(): Promise<Uint8Array> {
  const doc = await PDFDocument.create();
  const page = doc.addPage([600, 800]);
  const form = doc.getForm();

  const textFieldNames = [
    'Name',
    'Email',
    'Address',
    'Yacht Name',
    'Length',
    'Width',
    'Draft',
    'Berth Number',
  ];
  textFieldNames.forEach((name, i) => {
    const f = form.createTextField(name);
    f.addToPage(page, { x: 50, y: 700 - i * 40, width: 300, height: 24 });
  });

  for (const name of ['Lease_10', 'Purchase']) {
    const cb = form.createCheckBox(name);
    cb.addToPage(page, { x: 400, y: 700 - (name === 'Purchase' ? 0 : 40), width: 12, height: 12 });
  }

  return doc.save();
}

function makeContext(overrides: Partial<EoiContext> = {}): EoiContext {
  return {
    client: {
      fullName: 'Alice Smith',
      nationality: 'US',
      primaryEmail: 'alice@example.com',
      primaryPhone: '+1-555-0100',
      address: { street: '123 Main St', city: 'Austin', country: 'USA' },
    },
    yacht: {
      name: 'Sea Breeze',
      lengthFt: '45',
      widthFt: '14',
      draftFt: '6',
      lengthM: null,
      widthM: null,
      draftM: null,
      hullNumber: 'HN-1',
      flag: 'US',
      yearBuilt: 2020,
    },
    company: null,
    owner: { type: 'client', name: 'Alice Smith' },
    berth: {
      mooringNumber: 'A-12',
      area: 'North',
      lengthFt: '50',
      price: '1000',
      priceCurrency: 'USD',
      tenureType: 'permanent',
    },
    interest: { stage: 'open', leadCategory: null, dateFirstContact: null, notes: null },
    port: { name: 'Port Nimara', defaultCurrency: 'USD' },
    date: { today: '2026-04-26', year: '2026' },
    ...overrides,
  };
}

// ─── fillEoiFormFields ────────────────────────────────────────────────────────

describe('fillEoiFormFields', () => {
  it('populates every text field and checkbox using EoiContext', async () => {
    const sourcePdf = await buildSyntheticEoiPdf();
    const filled = await fillEoiFormFields(sourcePdf, makeContext());

    const out = await PDFDocument.load(filled);
    const form = out.getForm();

    expect(form.getTextField('Name').getText()).toBe('Alice Smith');
    expect(form.getTextField('Email').getText()).toBe('alice@example.com');
    expect(form.getTextField('Address').getText()).toBe('123 Main St, Austin, USA');
    expect(form.getTextField('Yacht Name').getText()).toBe('Sea Breeze');
    expect(form.getTextField('Length').getText()).toBe('45');
    expect(form.getTextField('Width').getText()).toBe('14');
    expect(form.getTextField('Draft').getText()).toBe('6');
    expect(form.getTextField('Berth Number').getText()).toBe('A-12');

    expect(form.getCheckBox('Purchase').isChecked()).toBe(true);
    expect(form.getCheckBox('Lease_10').isChecked()).toBe(false);
  });

  it('handles null primary email and null address gracefully', async () => {
    const sourcePdf = await buildSyntheticEoiPdf();
    const filled = await fillEoiFormFields(
      sourcePdf,
      makeContext({
        client: {
          fullName: 'Bob',
          nationality: null,
          primaryEmail: null,
          primaryPhone: null,
          address: null,
        },
      }),
    );

    const out = await PDFDocument.load(filled);
    const form = out.getForm();
    expect(form.getTextField('Email').getText()).toBe(undefined);
    expect(form.getTextField('Address').getText()).toBe(undefined);
    expect(form.getTextField('Name').getText()).toBe('Bob');
  });

  it('leaves the form interactive (not flattened) so values can be edited', async () => {
    const sourcePdf = await buildSyntheticEoiPdf();
    const filled = await fillEoiFormFields(sourcePdf, makeContext());

    const out = await PDFDocument.load(filled);
    // Field still present and reachable as a TextField → not flattened.
    expect(() => out.getForm().getTextField('Name')).not.toThrow();
  });

  it('skips fields silently if the source PDF lacks them', async () => {
    // Build a PDF with only a subset of fields and ensure no error.
    const doc = await PDFDocument.create();
    const page = doc.addPage([600, 800]);
    const form = doc.getForm();
    const f = form.createTextField('Name');
    f.addToPage(page, { x: 50, y: 700, width: 300, height: 24 });
    const sparse = await doc.save();

    await expect(fillEoiFormFields(sparse, makeContext())).resolves.toBeInstanceOf(Uint8Array);
  });
});

// ─── loadEoiTemplatePdf ───────────────────────────────────────────────────────

describe('loadEoiTemplatePdf', () => {
  let tmpFile: string;
  const originalEnv = process.env.EOI_TEMPLATE_PDF_PATH;

  beforeAll(async () => {
    const sourcePdf = await buildSyntheticEoiPdf();
    tmpFile = path.join(os.tmpdir(), `eoi-template-${Date.now()}.pdf`);
    await fs.writeFile(tmpFile, sourcePdf);
  });

  afterAll(async () => {
    if (originalEnv === undefined) delete process.env.EOI_TEMPLATE_PDF_PATH;
    else process.env.EOI_TEMPLATE_PDF_PATH = originalEnv;
    await fs.unlink(tmpFile).catch(() => undefined);
  });

  it('reads the PDF from EOI_TEMPLATE_PDF_PATH override', async () => {
    process.env.EOI_TEMPLATE_PDF_PATH = tmpFile;
    const bytes = await loadEoiTemplatePdf();
    expect(bytes.byteLength).toBeGreaterThan(100);
    // Round-trip: should re-load as a valid PDF.
    await expect(PDFDocument.load(bytes)).resolves.toBeInstanceOf(PDFDocument);
  });

  it('throws a clear error with instructions when the file is missing', async () => {
    process.env.EOI_TEMPLATE_PDF_PATH = '/nope/does-not-exist.pdf';
    await expect(loadEoiTemplatePdf()).rejects.toThrow(/EOI source PDF not found/);
  });
});
