import {
  PDFME_STATIC_LABEL_FIELD_KEYS,
  createSqlClient,
  extractTemplateFields,
  getActiveTemplateVersions,
  getEffectiveMappedKeys,
  getMappingsForVersion,
  summarizeIssues
} from './pdf-audit-utils.ts';

function classifyToken(token: string, mappedKeys: Set<string>) {
  if (token === 'topic' || token === 'data_topic') {
    return 'dynamic_topic_field';
  }

  if (token === 'item_topic') {
    return 'dynamic_topic_field';
  }

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

  if (mappedKeys.has(token)) {
    return 'mapped_field';
  }

  return 'unmapped_field';
}

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

  try {
    const templates = await getActiveTemplateVersions(sql);
    const results = [];

    for (const template of templates) {
      const fields = extractTemplateFields(template.schemaJson);
      const { mappings, columns } = await getMappingsForVersion(sql, template.versionId);
      const mappedKeys = getEffectiveMappedKeys(fields, mappings);
      const fieldNames = new Set(fields.map((field) => field.name).filter(Boolean));
      const schemaTokens = [...new Set(fields.flatMap((field) => field.tokens))].sort();
      const tableFieldCoverage = fields
        .filter((field) => field.type === 'table' && field.name)
        .map((field) => {
          const mapping = mappings.find((candidate) => candidate.placeholderKey === field.name) ?? null;
          return {
            fieldName: field.name,
            hasMapping: Boolean(mapping),
            sourcePath: mapping?.sourcePath ?? null,
            dataType: mapping?.dataType ?? null
          };
        });
      const coverage = schemaTokens.map((token) => {
        const mapping =
          mappings.find((candidate) => candidate.placeholderKey === token) ??
          mappings.find(
            (candidate) =>
              fields.some(
                (field) =>
                  field.name === candidate.placeholderKey &&
                  field.tokens.length === 1 &&
                  field.tokens[0] === token
              )
          ) ??
          null;
        return {
          token,
          classification: classifyToken(token, mappedKeys),
          sourcePath: mapping?.sourcePath ?? null,
          dataType: mapping?.dataType ?? null
        };
      });
      const unmappedFields = coverage.filter((item) => item.classification === 'unmapped_field');
      const invalidTableMappings = tableFieldCoverage.filter(
        (item) =>
          item.fieldName === 'quotation_price_data' &&
          (!item.hasMapping ||
            item.sourcePath !== 'pdfme.quotation_price_data' ||
            item.dataType !== 'table')
      );
      const orphanMappings = mappings
        .filter(
          (mapping) =>
            !schemaTokens.includes(mapping.placeholderKey) &&
            !fieldNames.has(mapping.placeholderKey) &&
          mapping.placeholderKey !== 'items_table'
        )
        .map((mapping) => ({
          placeholderKey: mapping.placeholderKey,
          sourcePath: mapping.sourcePath
        }));
      const missingStaticLabelMappings = [...PDFME_STATIC_LABEL_FIELD_KEYS].filter((fieldName) => {
        if (!fieldNames.has(fieldName)) {
          return false;
        }

        return !mappings.some((mapping) => mapping.placeholderKey === fieldName);
      });
      const tableMapping = mappings.find((mapping) => mapping.placeholderKey === 'items_table') ?? null;
      const tableColumns = tableMapping
        ? columns
            .filter((column) => column.mappingId === tableMapping.id)
            .map((column) => ({
              columnName: column.columnName,
              sourceField: column.sourceField
            }))
        : [];
      const status = summarizeIssues([
        unmappedFields.length ? 'FAIL' : 'PASS',
        invalidTableMappings.length ? 'FAIL' : 'PASS',
        missingStaticLabelMappings.length ? 'FAIL' : 'PASS',
        orphanMappings.length ? 'WARNING' : 'PASS'
      ]);

      results.push({
        templateName: template.templateName,
        version: template.version,
        organizationId: template.organizationId,
        tokenCount: schemaTokens.length,
        coverage,
        tableFieldCoverage,
        unmappedFields,
        missingStaticLabelMappings,
        orphanMappings,
        tableColumns,
        status
      });
    }

    const overallStatus = summarizeIssues(
      results.map((result) => result.status) as Array<'PASS' | 'WARNING' | 'FAIL'>
    );

    console.log(
      JSON.stringify(
        {
          audit: 'mapping-coverage',
          overallStatus,
          results
        },
        null,
        2
      )
    );
  } finally {
    await sql.end({ timeout: 5 });
  }
}

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