import { writeFile } from 'node:fs/promises';
import fs from 'node:fs';
import path from 'node:path';
import {
  AUDIT_ARTIFACTS_DIR,
  AUDIT_QUOTATION_CODE,
  REPORT_JSON_PATH,
  REPORT_MARKDOWN_PATH,
  buildAuditPayload,
  classifyTemplateField,
  computeFieldDiff,
  createSqlClient,
  extractTemplateFields,
  getEffectiveMappedKeys,
  getDuplicateFieldNames,
  getMappingsForVersion,
  getUnknownFieldNames,
  summarizeIssues
} from './pdf-audit-utils.ts';

async function main() {
  const sql = createSqlClient();

  try {
    const payload = await buildAuditPayload(sql);
    const fields = extractTemplateFields(payload.templateSchema);
    const previousVersion =
      (
        await sql`
          select schema_json as "schemaJson", version
          from crm_document_template_versions
          where template_id = (
            select template_id
            from crm_document_template_versions
            where id = ${payload.templateVersionId}
            limit 1
          )
            and id <> ${payload.templateVersionId}
            and deleted_at is null
          order by created_at desc
          limit 1
        `
      )[0] ?? null;
    const previousFields = previousVersion ? extractTemplateFields(previousVersion.schemaJson) : [];
    const { mappings, columns } = await getMappingsForVersion(sql, payload.templateVersionId);
    const fieldNames = new Set(fields.map((field) => field.name).filter(Boolean));
    const placeholderTokens = [...new Set(fields.flatMap((field) => field.tokens))].sort();
    const mappedKeys = getEffectiveMappedKeys(fields, mappings);
    const unmappedTokens = placeholderTokens.filter((token) => {
      if (token === 'item_topic') {
        return false;
      }

      if (token === 'topic' || token === 'data_topic') {
        return false;
      }

      if (token.startsWith('topic_') || token.startsWith('item_topic_')) {
        return false;
      }

      return !mappedKeys.has(token);
    });
    const orphanMappings = mappings
      .filter(
        (mapping) =>
          !placeholderTokens.includes(mapping.placeholderKey) &&
          !fieldNames.has(mapping.placeholderKey) &&
          mapping.placeholderKey !== 'items_table'
      )
      .map((mapping) => mapping.placeholderKey);
    const duplicateFields = getDuplicateFieldNames(fields);
    const unknownFields = getUnknownFieldNames(fields, mappedKeys);
    const fieldDiff = computeFieldDiff(fields, previousFields);
    const classifications = fields.map((field) => ({
      name: field.name,
      classification: classifyTemplateField(field, mappedKeys)
    }));
    const topicRows = Object.keys(payload.topicInputs).filter((key) => key.startsWith('topic_')).length;
    const topicItemRows = Object.keys(payload.topicInputs).filter((key) =>
      key.startsWith('item_topic_')
    ).length;
    const itemTableMapping = mappings.find((mapping) => mapping.placeholderKey === 'items_table') ?? null;
    const priceTableMapping =
      mappings.find((mapping) => mapping.placeholderKey === 'quotation_price_data') ?? null;
    const itemTableColumns = itemTableMapping
      ? columns.filter((column) => column.mappingId === itemTableMapping.id)
      : [];
    const snapshotTemplateInput =
      payload.approvedSnapshot &&
      typeof payload.approvedSnapshot === 'object' &&
      'templateInput' in payload.approvedSnapshot
        ? (payload.approvedSnapshot.templateInput as Record<string, unknown> | null)
        : null;
    const sharedKeys = snapshotTemplateInput
      ? Object.keys(payload.templateInput).filter(
          (key) =>
            !(key.startsWith('topic_') || key.startsWith('item_topic_')) &&
            key in snapshotTemplateInput
        )
      : [];
    const inconsistentKeys = sharedKeys.filter(
      (key) => JSON.stringify(payload.templateInput[key]) !== JSON.stringify(snapshotTemplateInput?.[key])
    );
    const consistencyStatus = snapshotTemplateInput
      ? inconsistentKeys.length
        ? 'FAIL'
        : 'PASS'
      : 'WARNING';
    const overallStatus = summarizeIssues([
      unmappedTokens.length ? 'FAIL' : 'PASS',
      orphanMappings.length ? 'WARNING' : 'PASS',
      duplicateFields.length ? 'FAIL' : 'PASS',
      unknownFields.length ? 'WARNING' : 'PASS',
      consistencyStatus
    ]);

    const report = {
      generatedAt: new Date().toISOString(),
      overallStatus,
      quotationCode: payload.quotationCode,
      expectedFixtureCode: AUDIT_QUOTATION_CODE,
      template: {
        templateName: payload.templateName,
        version: payload.templateVersion,
        versionId: payload.templateVersionId,
        fieldCount: fields.length,
        previousVersion: previousVersion?.version ?? null,
        deletedFields: fieldDiff.deletedFields,
        newFields: fieldDiff.newFields,
        duplicateFields,
        unknownFields,
        classifications
      },
      metrics: {
        topicCount: payload.quotationMetrics.topicCount,
        itemCount: payload.quotationMetrics.itemCount,
        partyCount: payload.quotationMetrics.partyCount,
        dynamicTopicFieldCount: topicRows,
        dynamicTopicItemFieldCount: topicItemRows,
        mappingCount: mappings.length
      },
      mappingCoverage: {
        placeholderTokenCount: placeholderTokens.length,
        unmappedTokens,
        orphanMappings,
        priceTableMapping: priceTableMapping
          ? {
              sourcePath: priceTableMapping.sourcePath,
              dataType: priceTableMapping.dataType
            }
          : null,
        itemTableColumns: itemTableColumns.map((column) => ({
          columnName: column.columnName,
          sourceField: column.sourceField
        }))
      },
      runtimePayload: {
        templateInputKeys: Object.keys(payload.templateInput).length,
        topicInputKeys: Object.keys(payload.topicInputs).length,
        priceTableRows: Array.isArray(payload.templateInput.quotation_price_data)
          ? payload.templateInput.quotation_price_data.length
          : 0
      },
      consistency: {
        status: consistencyStatus,
        approvedSnapshotAvailable: Boolean(snapshotTemplateInput),
        approvedArtifactReference: payload.approvedArtifactReference,
        inconsistentKeys
      },
      integritySummary: {
        available: fs.existsSync(path.resolve(AUDIT_ARTIFACTS_DIR, 'summary.json')),
        artifactDir: AUDIT_ARTIFACTS_DIR
      }
    };

    const markdown = [
      '# PDF Audit Report',
      '',
      `- Generated At: ${report.generatedAt}`,
      `- Overall Status: ${report.overallStatus}`,
      `- Quotation Code: ${report.quotationCode}`,
      `- Template: ${report.template.templateName} v${report.template.version}`,
      `- Previous Version: ${report.template.previousVersion ?? 'None'}`,
      '',
      '## Metrics',
      '',
      `- Topic Count: ${report.metrics.topicCount}`,
      `- Item Count: ${report.metrics.itemCount}`,
      `- Project Party Count: ${report.metrics.partyCount}`,
      `- Mapping Count: ${report.metrics.mappingCount}`,
      '',
      '## Mapping Coverage',
      '',
      `- Placeholder Tokens: ${report.mappingCoverage.placeholderTokenCount}`,
      `- Unmapped Tokens: ${report.mappingCoverage.unmappedTokens.length ? report.mappingCoverage.unmappedTokens.join(', ') : 'None'}`,
      `- Orphan Mappings: ${report.mappingCoverage.orphanMappings.length ? report.mappingCoverage.orphanMappings.join(', ') : 'None'}`,
      '',
      '## Visual / Consistency',
      '',
      `- Approved Snapshot Available: ${report.consistency.approvedSnapshotAvailable ? 'Yes' : 'No'}`,
      `- Consistency Status: ${report.consistency.status}`,
      `- Inconsistent Keys: ${report.consistency.inconsistentKeys.length ? report.consistency.inconsistentKeys.join(', ') : 'None'}`,
      '',
      '## Integrity Layer',
      '',
      `- Integrity Summary Available: ${report.integritySummary.available ? 'Yes' : 'No'}`,
      `- Artifact Directory: ${report.integritySummary.artifactDir}`
    ].join('\n');

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

    console.log(
      JSON.stringify(
        {
          audit: 'pdf-report',
          overallStatus,
          jsonPath: REPORT_JSON_PATH,
          markdownPath: REPORT_MARKDOWN_PATH,
          report
        },
        null,
        2
      )
    );
  } finally {
    await sql.end({ timeout: 5 });
  }
}

main().catch((error) => {
  console.error(`[generate-pdf-audit-report] ${error.message}`);
  process.exit(1);
});
