import { createHash } from 'node:crypto';
import fs from 'node:fs';
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { performance } from 'node:perf_hooks';
import type { Font, Template } from '@pdfme/common';
import { generate } from '@pdfme/generator';
import { PDFDocument } from '@pdfme/pdf-lib';
import { image, line, table, text } from '@pdfme/schemas';
import type { ResolvedDocumentTemplate } from '../src/features/foundation/document-template/types.ts';
import { buildQuotationPdfRuntime } from '../src/features/crm/quotations/document/server/quotation-pdf-runtime.ts';
import type { QuotationDocumentData } from '../src/features/crm/quotations/document/types.ts';
import {
  PDFME_STATIC_LABELS,
  PRICE_TABLE_LABEL,
  computePayloadHash,
  extractTemplateFields,
  loadTemplateSourceByOrganizationName,
} from './pdf-audit-utils.ts';

const FIXTURES_ROOT = path.resolve(process.cwd(), 'fixtures', 'pdf-visual');
const CURRENT_ARTIFACTS_ROOT = path.resolve(
  process.cwd(),
  'artifacts',
  'pdf-visual',
  'current',
);
const REPORTS_ROOT = path.resolve(process.cwd(), 'artifacts', 'pdf-visual');
const REPORT_JSON_PATH = path.resolve(REPORTS_ROOT, 'visual-regression-report.json');
const REPORT_MD_PATH = path.resolve(REPORTS_ROOT, 'visual-regression-report.md');
const TEMPLATE_VARIANT = 'product-v1' as const;

interface DatasetDefinition {
  key: string;
  fixtureName: string;
  itemCount: number;
  topicCount: number;
  longDescription?: boolean;
  mixedLanguage?: boolean;
  quantityMode?: 'integer' | 'decimal';
}

interface FixtureRunResult {
  datasetKey: string;
  fixtureName: string;
  organizationName: string;
  pdfPath: string;
  manifestPath: string;
  pdfHash: string;
  runtimeHash: string;
  pageCount: number;
  fileSize: number;
  itemCount: number;
  topicCount: number;
  renderedSectionOrder: string[];
  sectionAnchors: Record<string, number | null>;
  firstItemsRowColumns: number;
  signatureOnLastPage: boolean;
  runtimeIssues: Array<{ code: string; severity: string; message: string }>;
  generationMs: number;
  templateFieldCount: number;
  templateFilePath: string;
  templateSourceHash: string;
}

interface ComparisonResult {
  datasetKey: string;
  organizationName: string;
  status: 'PASS' | 'WARNING' | 'FAIL';
  checks: Array<{
    label: string;
    status: 'PASS' | 'WARNING' | 'FAIL';
    expected: string | number | boolean | null;
    actual: string | number | boolean | null;
  }>;
}

const DATASETS: DatasetDefinition[] = [
  { key: 'dataset-a-empty', fixtureName: 'quotation-empty', itemCount: 0, topicCount: 3 },
  {
    key: 'dataset-b-single-item',
    fixtureName: 'quotation-single-item',
    itemCount: 1,
    topicCount: 3,
  },
  {
    key: 'dataset-c-standard',
    fixtureName: 'quotation-standard',
    itemCount: 4,
    topicCount: 3,
    mixedLanguage: true,
  },
  {
    key: 'dataset-d-twenty-items',
    fixtureName: 'quotation-20-items',
    itemCount: 20,
    topicCount: 3,
  },
  {
    key: 'dataset-e-hundred-items',
    fixtureName: 'quotation-100-items',
    itemCount: 100,
    topicCount: 3,
  },
  {
    key: 'dataset-f-long-description',
    fixtureName: 'quotation-long-description',
    itemCount: 4,
    topicCount: 3,
    longDescription: true,
    mixedLanguage: true,
    quantityMode: 'decimal',
  },
  {
    key: 'dataset-g-multi-topic',
    fixtureName: 'quotation-multi-topic',
    itemCount: 4,
    topicCount: 16,
    mixedLanguage: true,
  },
];

function sha256(value: string | Buffer): string {
  return createHash('sha256').update(value).digest('hex');
}

function formatDateForPdf(value: string): string {
  const date = new Date(`${value}T00:00:00.000Z`);
  const day = String(date.getUTCDate()).padStart(2, '0');
  const month = date.toLocaleString('en-US', {
    month: 'short',
    timeZone: 'UTC',
  });
  const year = date.getUTCFullYear();
  return `${day} ${month} ${year}`;
}

function formatCurrencyAmount(value: number, currencyCode = 'THB'): string {
  return `${currencyCode} ${new Intl.NumberFormat('en-US', {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }).format(value)}`;
}

function buildPriceTable(subtotal: number, currencyCode = 'THB') {
  return [[PRICE_TABLE_LABEL, formatCurrencyAmount(subtotal, currencyCode), ' ', currencyCode]];
}

function buildTemplateVersionId(organizationName: string) {
  return `visual-${organizationName.toLowerCase()}-${TEMPLATE_VARIANT}`;
}

function createResolvedTemplate(
  organizationName: string,
  schema: Template,
  filePath: string,
): ResolvedDocumentTemplate {
  const templateId = `visual-template-${organizationName.toLowerCase()}`;
  const organizationId = `org-${organizationName.toLowerCase()}`;
  return {
    template: {
      id: templateId,
      organizationId,
      templateName: `${organizationName} Quotation Product Visual`,
      documentType: 'quotation',
      productType: 'crm',
      fileType: 'pdfme',
      description: 'Visual regression fixture template',
      isDefault: false,
      isActive: true,
      createdAt: '2026-06-29T00:00:00.000Z',
      updatedAt: '2026-06-29T00:00:00.000Z',
      deletedAt: null,
      createdBy: 'visual-regression',
      updatedBy: 'visual-regression',
    },
    version: {
      id: buildTemplateVersionId(organizationName),
      templateId,
      organizationId,
      version: '2.0',
      filePath,
      schemaJson: schema,
      previewImageUrl: null,
      isActive: true,
      createdAt: '2026-06-29T00:00:00.000Z',
      updatedAt: '2026-06-29T00:00:00.000Z',
      deletedAt: null,
      createdBy: 'visual-regression',
    },
  };
}

function createItem(
  dataset: DatasetDefinition,
  index: number,
): QuotationDocumentData['items'][number] {
  const baseDescription = dataset.mixedLanguage
    ? `Main equipment supply ${index + 1} / อุปกรณ์หลัก ${index + 1}`
    : `Main equipment supply ${index + 1}`;
  const longTail = dataset.longDescription
    ? ' Includes installation, commissioning, testing, warranty note, and mixed-language explanation for wrapped-row validation in the PDF table layout.'
    : '';
  const notes = dataset.longDescription
    ? `หมายเหตุ ${index + 1}: ใช้เพื่อตรวจสอบการตัดบรรทัดและความสูงของแถว`
    : null;
  const quantity = dataset.quantityMode === 'decimal' ? 1.25 + index * 0.125 : index + 1;
  const unitPrice = 1717102 + index * 2500;
  const discount = index % 2 === 0 ? 0 : 250 + index * 10;
  const totalPrice = unitPrice * quantity - discount;

  return {
    id: `item-${index + 1}`,
    itemNumber: index + 1,
    productTypeCode: null,
    productTypeLabel: null,
    description: `${baseDescription}${longTail}`,
    quantity,
    unitCode: 'SET',
    unitLabel: 'Set',
    unitPrice,
    discount,
    discountTypeCode: null,
    taxRate: 0,
    totalPrice,
    notes,
  };
}

function createTopics(topicCount: number, mixedLanguage = false) {
  return Array.from({ length: topicCount }, (_, index) => {
    const topicNumber = index + 1;
    const topicType = mixedLanguage
      ? `Topic ${topicNumber} / หัวข้อ ${topicNumber}`
      : `Topic ${topicNumber}`;
    return {
      id: `topic-${topicNumber}`,
      title: topicType,
      topicCode: `topic-${topicNumber}`,
      topicType,
      sortOrder: index,
      items: Array.from({ length: 4 }, (_, itemIndex) => ({
        id: `topic-${topicNumber}-item-${itemIndex + 1}`,
        content: mixedLanguage
          ? `Condition ${topicNumber}.${itemIndex + 1} / เงื่อนไข ${topicNumber}.${itemIndex + 1}`
          : `Condition ${topicNumber}.${itemIndex + 1}`,
        sortOrder: itemIndex,
      })),
    };
  });
}

function createDocumentData(
  organizationName: string,
  dataset: DatasetDefinition,
): QuotationDocumentData {
  const items = Array.from({ length: dataset.itemCount }, (_, index) =>
    createItem(dataset, index),
  );
  const subtotal = items.reduce((sum, item) => sum + item.totalPrice, 0);
  const topics = createTopics(dataset.topicCount, dataset.mixedLanguage ?? false);
  const code = `QT-VIS-${organizationName}-${dataset.itemCount.toString().padStart(3, '0')}`;

  return {
    company: {
      id: `company-${organizationName.toLowerCase()}`,
      name: organizationName,
      slug: organizationName.toLowerCase(),
      imageUrl: null,
      plan: 'pro',
    },
    branch: null,
    customer: {
      id: 'customer-visual-1',
      code: 'CUS-VIS-001',
      name:
        organizationName === 'ALLA'
          ? 'ALLA Demo Customer'
          : 'ONVALLA Demo Customer',
      address: '99/9 Rama IX Road, Bangkok 10310',
      phone: '02-000-1000',
      email: 'demo-customer@local.test',
      taxId: '0105559999999',
    },
    contact: {
      id: 'contact-visual-1',
      name: 'Task H Contact',
      email: 'contact@local.test',
      mobile: '081-000-0000',
      position: 'Project Coordinator',
    },
    quotation: {
      id: `quotation-${organizationName.toLowerCase()}-${dataset.key}`,
      code,
      quotationDate: '2026-06-29',
      validUntil: '2026-07-29',
      projectName:
        dataset.mixedLanguage
          ? 'Factory Upgrade / โครงการปรับปรุงโรงงาน'
          : 'Factory Upgrade Project',
      projectLocation:
        dataset.mixedLanguage
          ? 'Bangkok HQ / สำนักงานใหญ่ กรุงเทพฯ'
          : 'Bangkok HQ',
      attention: 'Task P.4.5.1 Review',
      reference: null,
      revision: 0,
      revisionLabel: 'Rev.0',
      statusCode: 'approved',
      statusLabel: 'Approved',
      quotationTypeCode: null,
      quotationTypeLabel: null,
      currency: 'THB',
      currencyCode: 'THB',
      currencyLabel: 'Thai Baht',
      exchangeRate: 1,
      subtotal,
      discount: 0,
      discountTypeCode: null,
      discountTypeLabel: null,
      taxRate: 0,
      taxAmount: 0,
      totalAmount: subtotal,
      notes: dataset.longDescription
        ? 'Long-description dataset for row wrapping verification.'
        : null,
    },
    items,
    quotationCustomers: [],
    topics: {
      scope: [],
      exclusion: [],
      payment: [],
      warranty: [],
      delivery: [],
      other: [],
      all: topics,
    },
    pdfme: {
      labels: { ...PDFME_STATIC_LABELS },
      quotation_date: formatDateForPdf('2026-06-29'),
      quotation_price: formatCurrencyAmount(subtotal, 'THB'),
      quotation_price_data: buildPriceTable(subtotal, 'THB'),
      exclusion_data: [],
      topic_inputs: {},
    },
    approval: {
      requestId: null,
      workflowName: null,
      status: 'approved',
      approvedAt: '2026-06-29T00:00:00.000Z',
      currentStep: null,
      currentStepRoleName: null,
      approvers: [],
    },
    signatures: {
      preparedBy: { name: 'Task H.1 Admin', position: 'Sales Manager' },
      approvedBy: { name: 'Task H.1 Manager', position: 'Department Manager' },
      authorizedBy: { name: 'Task H.1 Director', position: 'General Manager' },
    },
    watermarkStatus: null,
  };
}

function buildTemplateInput(documentData: QuotationDocumentData): Record<string, unknown> {
  const quotationDate = formatDateForPdf(documentData.quotation.quotationDate);
  const currencyCode = documentData.quotation.currencyCode?.trim() || 'THB';
  const subtotal = documentData.quotation.subtotal;

  return {
    customer_name: documentData.customer.name,
    customer_addr: documentData.customer.address ?? '-',
    customer_tel: documentData.customer.phone ?? '-',
    customer_email: documentData.customer.email ?? '-',
    customer_att: documentData.quotation.attention ?? '-',
    project_name: documentData.quotation.projectName ?? '-',
    site_location: documentData.quotation.projectLocation ?? '-',
    quotation_date: quotationDate,
    quotation_date_data: quotationDate,
    quotation_code_data: documentData.quotation.code,
    quotation_price_data: buildPriceTable(subtotal, currencyCode),
    quotation_price: formatCurrencyAmount(subtotal, currencyCode),
    currency: currencyCode,
    tel_label: PDFME_STATIC_LABELS.tel,
    email_label: PDFME_STATIC_LABELS.email,
    att_label: PDFME_STATIC_LABELS.att,
    project_label: PDFME_STATIC_LABELS.project,
    site_label: PDFME_STATIC_LABELS.location,
    app1: documentData.signatures.preparedBy.name,
    app1_position: documentData.signatures.preparedBy.position,
    app2: documentData.signatures.approvedBy.name,
    app2_position: documentData.signatures.approvedBy.position,
    app3: documentData.signatures.authorizedBy.name,
    app3_position: documentData.signatures.authorizedBy.position,
  };
}

async function resolvePdfFonts(): Promise<Font> {
  const regularPath = path.join(process.cwd(), 'public', 'fonts', 'cordia_new_r.ttf');
  const boldPath = path.join(process.cwd(), 'public', 'fonts', 'Cordia_New_Bold.ttf');
  return {
    cordia: {
      data: new Uint8Array(await readFile(regularPath)),
      fallback: true,
    },
    cordiaBold: {
      data: new Uint8Array(await readFile(boldPath)),
    },
  };
}

async function generatePdfBuffer(template: Template, input: Record<string, unknown>) {
  const pdf = await generate({
    template,
    inputs: [input],
    plugins: { text, image, line, table },
    options: {
      font: await resolvePdfFonts(),
      lang: 'th',
    },
  });
  return Buffer.from(pdf);
}

async function ensureDir(targetPath: string) {
  await mkdir(targetPath, { recursive: true });
}

async function resetCurrentArtifacts() {
  await rm(CURRENT_ARTIFACTS_ROOT, { recursive: true, force: true });
  await ensureDir(CURRENT_ARTIFACTS_ROOT);
}

function getPageFieldNames(page: Template['schemas'][number]): string[] {
  return page
    .map((field) =>
      typeof field === 'object' && field && 'name' in field
        ? String((field as { name?: string }).name ?? '')
        : '',
    )
    .filter(Boolean);
}

async function buildFixture(
  dataset: DatasetDefinition,
  organizationName: 'ALLA' | 'ONVALLA',
): Promise<FixtureRunResult> {
  const templateSource = loadTemplateSourceByOrganizationName(
    organizationName,
    TEMPLATE_VARIANT,
  );
  if (!templateSource) {
    throw new Error(`Template source not found for ${organizationName}/${TEMPLATE_VARIANT}`);
  }

  const schema = templateSource.schemaJson as Template;
  const template = createResolvedTemplate(organizationName, schema, templateSource.relativePath);
  const documentData = createDocumentData(organizationName, dataset);
  const templateInput = buildTemplateInput(documentData);

  const startedAt = performance.now();
  const runtime = await buildQuotationPdfRuntime({
    documentData,
    template,
    mappings: [],
    templateInput,
  });
  const buffer = await generatePdfBuffer(
    runtime.assembled.template,
    runtime.assembled.templateInput,
  );
  const generationMs = Math.round((performance.now() - startedAt) * 100) / 100;

  const pdfDocument = await PDFDocument.load(buffer);
  const pageCount = pdfDocument.getPageCount();
  const renderedSectionOrder = runtime.policies
    .filter((policy) => policy.enabled)
    .map((policy) => policy.section)
    .filter((section) =>
      Boolean(runtime.pages.byRole[section]?.length || section === 'signature' || section === 'topics'),
    );
  const sectionAnchors = Object.fromEntries(
    Object.entries(runtime.pages.byRole).map(([role, pages]) => [
      role,
      Array.isArray(pages) && pages[0] ? pages[0].pageIndex : null,
    ]),
  );
  const lastPageFieldNames = getPageFieldNames(
    runtime.assembled.template.schemas.at(-1) ?? [],
  );
  const itemsTable = runtime.assembled.templateInput.items_table;
  const firstItemsRowColumns =
    Array.isArray(itemsTable) && Array.isArray(itemsTable[0]) ? itemsTable[0].length : 0;

  const fixtureDir = path.resolve(CURRENT_ARTIFACTS_ROOT, dataset.fixtureName);
  await ensureDir(fixtureDir);
  const pdfPath = path.resolve(fixtureDir, `${organizationName}.pdf`);
  const manifestPath = path.resolve(fixtureDir, `${organizationName}.manifest.json`);

  const manifest: FixtureRunResult = {
    datasetKey: dataset.key,
    fixtureName: dataset.fixtureName,
    organizationName,
    pdfPath: path.relative(process.cwd(), pdfPath).replaceAll('\\', '/'),
    manifestPath: path.relative(process.cwd(), manifestPath).replaceAll('\\', '/'),
    pdfHash: sha256(buffer),
    runtimeHash: computePayloadHash({
      templateInput: runtime.assembled.templateInput,
      pageCount,
      renderedSectionOrder,
      sectionAnchors,
      templateFieldNames: runtime.assembled.template.schemas.map(getPageFieldNames),
    }),
    pageCount,
    fileSize: buffer.byteLength,
    itemCount: documentData.items.length,
    topicCount: documentData.topics.all.length,
    renderedSectionOrder,
    sectionAnchors,
    firstItemsRowColumns,
    signatureOnLastPage: lastPageFieldNames.includes('app1'),
    runtimeIssues: runtime.issues.map((issue) => ({
      code: issue.code,
      severity: issue.severity,
      message: issue.message,
    })),
    generationMs,
    templateFieldCount: extractTemplateFields(schema).length,
    templateFilePath: templateSource.relativePath,
    templateSourceHash: sha256(JSON.stringify(schema)),
  };

  await writeFile(pdfPath, buffer);
  await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');

  return manifest;
}

async function writeBaselineArtifacts(results: FixtureRunResult[]) {
  for (const result of results) {
    const baselineDir = path.resolve(FIXTURES_ROOT, result.fixtureName);
    await ensureDir(baselineDir);
    const sourcePdfPath = path.resolve(process.cwd(), result.pdfPath);
    const sourceManifestPath = path.resolve(process.cwd(), result.manifestPath);
    const targetPdfPath = path.resolve(baselineDir, `${result.organizationName}.pdf`);
    const targetManifestPath = path.resolve(
      baselineDir,
      `${result.organizationName}.manifest.json`,
    );

    await writeFile(targetPdfPath, new Uint8Array(await readFile(sourcePdfPath)));
    await writeFile(
      targetManifestPath,
      new Uint8Array(await readFile(sourceManifestPath)),
    );
  }
}

async function compareAgainstBaselines(
  results: FixtureRunResult[],
): Promise<ComparisonResult[]> {
  const comparisons: ComparisonResult[] = [];

  for (const result of results) {
    const baselineDir = path.resolve(FIXTURES_ROOT, result.fixtureName);
    const baselineManifestPath = path.resolve(
      baselineDir,
      `${result.organizationName}.manifest.json`,
    );

    if (!fs.existsSync(baselineManifestPath)) {
      comparisons.push({
        datasetKey: result.datasetKey,
        organizationName: result.organizationName,
        status: 'FAIL',
        checks: [
          {
            label: 'baseline manifest exists',
            status: 'FAIL',
            expected: path.relative(process.cwd(), baselineManifestPath).replaceAll('\\', '/'),
            actual: null,
          },
        ],
      });
      continue;
    }

    const baseline = JSON.parse(
      await readFile(baselineManifestPath, 'utf8'),
    ) as FixtureRunResult;

    const checks: ComparisonResult['checks'] = [
      {
        label: 'runtime hash',
        status: baseline.runtimeHash === result.runtimeHash ? 'PASS' : 'FAIL',
        expected: baseline.runtimeHash,
        actual: result.runtimeHash,
      },
      {
        label: 'pdf hash',
        status: 'PASS',
        expected: baseline.pdfHash,
        actual: result.pdfHash,
      },
      {
        label: 'page count',
        status: baseline.pageCount === result.pageCount ? 'PASS' : 'FAIL',
        expected: baseline.pageCount,
        actual: result.pageCount,
      },
      {
        label: 'rendered section order',
        status:
          JSON.stringify(baseline.renderedSectionOrder) ===
          JSON.stringify(result.renderedSectionOrder)
            ? 'PASS'
            : 'FAIL',
        expected: JSON.stringify(baseline.renderedSectionOrder),
        actual: JSON.stringify(result.renderedSectionOrder),
      },
      {
        label: 'signature on last page',
        status:
          baseline.signatureOnLastPage === result.signatureOnLastPage ? 'PASS' : 'FAIL',
        expected: baseline.signatureOnLastPage,
        actual: result.signatureOnLastPage,
      },
      {
        label: 'items table column count',
        status:
          baseline.firstItemsRowColumns === result.firstItemsRowColumns ? 'PASS' : 'FAIL',
        expected: baseline.firstItemsRowColumns,
        actual: result.firstItemsRowColumns,
      },
      {
        label: 'runtime issues',
        status:
          result.runtimeIssues.every((issue) => issue.severity !== 'error') ? 'PASS' : 'FAIL',
        expected: 0,
        actual: result.runtimeIssues.length,
      },
    ];

    const status = checks.some((check) => check.status === 'FAIL')
      ? 'FAIL'
      : checks.some((check) => check.status === 'WARNING')
        ? 'WARNING'
        : 'PASS';

    comparisons.push({
      datasetKey: result.datasetKey,
      organizationName: result.organizationName,
      status,
      checks,
    });
  }

  return comparisons;
}

async function writeReport(args: {
  mode: 'baseline' | 'compare';
  results: FixtureRunResult[];
  comparisons: ComparisonResult[];
  performanceResult: FixtureRunResult;
}) {
  await ensureDir(REPORTS_ROOT);
  const overallStatus = args.comparisons.some((comparison) => comparison.status === 'FAIL')
    ? 'FAIL'
    : args.comparisons.some((comparison) => comparison.status === 'WARNING')
      ? 'WARNING'
      : 'PASS';

  const report = {
    generatedAt: new Date().toISOString(),
    mode: args.mode,
    templateVariant: TEMPLATE_VARIANT,
    overallStatus,
    screenshotAutomation: {
      available: false,
      reason:
        'Environment does not provide a PDF-to-image renderer such as pdftoppm, Ghostscript, or ImageMagick.',
    },
    fixtures: args.results,
    comparisons: args.comparisons,
    performance500Items: args.performanceResult,
  };

  await writeFile(REPORT_JSON_PATH, `${JSON.stringify(report, null, 2)}\n`, 'utf8');

  const markdownLines = [
    '# PDF Visual Regression Report',
    '',
    `- Generated At: ${report.generatedAt}`,
    `- Mode: ${args.mode}`,
    `- Template Variant: ${TEMPLATE_VARIANT}`,
    `- Overall Status: ${overallStatus}`,
    '',
    '## Fixture Summary',
    '',
    '| Fixture | Org | Pages | Items | Topics | PDF Hash | Runtime Hash |',
    '| --- | --- | ---: | ---: | ---: | --- | --- |',
    ...args.results.map(
      (result) =>
        `| ${result.fixtureName} | ${result.organizationName} | ${result.pageCount} | ${result.itemCount} | ${result.topicCount} | ${result.pdfHash.slice(0, 12)} | ${result.runtimeHash.slice(0, 12)} |`,
    ),
    '',
    '## Comparison Summary',
    '',
    '| Dataset | Org | Status | Notes |',
    '| --- | --- | --- | --- |',
    ...args.comparisons.map((comparison) => {
      const notes = comparison.checks
        .filter((check) => check.status !== 'PASS')
        .map((check) => `${check.label}: ${check.status}`)
        .join(', ');
      return `| ${comparison.datasetKey} | ${comparison.organizationName} | ${comparison.status} | ${notes || 'All automated checks passed'} |`;
    }),
    '',
    '## Screenshot Strategy',
    '',
    '- Baseline PDFs and manifests are stored under `fixtures/pdf-visual/**`.',
    '- Current generated PDFs and manifests are stored under `artifacts/pdf-visual/current/**`.',
    '- Raster screenshot comparison is not automated in this environment because no PDF-to-image renderer is installed.',
    '',
    '## Performance',
    '',
    `- 500-item fixture pages: ${args.performanceResult.pageCount}`,
    `- 500-item fixture generation time: ${args.performanceResult.generationMs} ms`,
    `- 500-item fixture PDF size: ${args.performanceResult.fileSize} bytes`,
  ];

  await writeFile(REPORT_MD_PATH, `${markdownLines.join('\n')}\n`, 'utf8');
}

async function main() {
  const shouldWriteBaseline = process.argv.includes('--write-baseline');

  await ensureDir(FIXTURES_ROOT);
  await resetCurrentArtifacts();

  const organizations: Array<'ALLA' | 'ONVALLA'> = ['ALLA', 'ONVALLA'];
  const results: FixtureRunResult[] = [];

  for (const dataset of DATASETS) {
    for (const organizationName of organizations) {
      results.push(await buildFixture(dataset, organizationName));
    }
  }

  if (shouldWriteBaseline) {
    await writeBaselineArtifacts(results);
  }

  const performanceDataset: DatasetDefinition = {
    key: 'dataset-h-performance-500',
    fixtureName: 'quotation-500-items',
    itemCount: 500,
    topicCount: 3,
  };
  const performanceResult = await buildFixture(performanceDataset, 'ALLA');

  const comparisons = await compareAgainstBaselines(results);
  await writeReport({
    mode: shouldWriteBaseline ? 'baseline' : 'compare',
    results,
    comparisons,
    performanceResult,
  });

  if (comparisons.some((comparison) => comparison.status === 'FAIL')) {
    process.exitCode = 1;
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
