import {
  PDFME_STATIC_LABEL_FIELD_KEYS,
  buildAuditPayload,
  createSqlClient,
  summarizeIssues,
  writeAuditArtifact
} from './pdf-audit-utils.ts';

type PayloadFinding = {
  field: string;
  source: 'documentData' | 'templateInput' | 'topicInputs';
  status: 'PASS' | 'WARNING' | 'FAIL';
  value: unknown;
  message: string;
};

function pushFinding(findings: PayloadFinding[], finding: PayloadFinding) {
  findings.push(finding);
}

function walkValue(
  findings: PayloadFinding[],
  source: PayloadFinding['source'],
  prefix: string,
  value: unknown
) {
  if (value === undefined) {
    pushFinding(findings, {
      field: prefix,
      source,
      status: 'FAIL',
      value,
      message: 'Value is undefined'
    });
    return;
  }

  if (value === null) {
    pushFinding(findings, {
      field: prefix,
      source,
      status: 'WARNING',
      value,
      message: 'Value is null'
    });
    return;
  }

  if (typeof value === 'string') {
    pushFinding(findings, {
      field: prefix,
      source,
      status: value.trim() ? 'PASS' : 'WARNING',
      value,
      message: value.trim() ? 'String is populated' : 'String is empty'
    });
    return;
  }

  if (Array.isArray(value)) {
    if (source === 'documentData' && value.some((row) => !Array.isArray(row))) {
      pushFinding(findings, {
        field: prefix,
        source,
        status: 'PASS',
        value: `rows:${value.length}`,
        message: 'Collection payload is populated'
      });

      for (const [index, item] of value.entries()) {
        walkValue(findings, source, `${prefix}[${index}]`, item);
      }

      return;
    }

    const validTable = value.every(
      (row) => Array.isArray(row) && row.every((cell) => typeof cell === 'string')
    );

    pushFinding(findings, {
      field: prefix,
      source,
      status: validTable ? 'PASS' : 'FAIL',
      value: Array.isArray(value) ? `rows:${value.length}` : value,
      message: validTable ? 'Array shape is valid' : 'Array shape is invalid for PDF payload'
    });

    for (const [index, item] of value.entries()) {
      if (Array.isArray(item)) {
        for (const [cellIndex, cell] of item.entries()) {
          if (typeof cell !== 'string') {
            pushFinding(findings, {
              field: `${prefix}[${index}][${cellIndex}]`,
              source,
              status: 'FAIL',
              value: cell,
              message: 'Table cell is not a string'
            });
          }
        }
      }
    }

    return;
  }

  if (typeof value === 'object') {
    for (const [key, childValue] of Object.entries(value)) {
      walkValue(findings, source, prefix ? `${prefix}.${key}` : key, childValue);
    }
    return;
  }

  pushFinding(findings, {
    field: prefix,
    source,
    status: 'PASS',
    value,
    message: 'Scalar value is populated'
  });
}

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

  try {
    const payload = await buildAuditPayload(sql);
    const findings: PayloadFinding[] = [];

    walkValue(findings, 'documentData', 'documentData', payload.documentData);
    walkValue(findings, 'templateInput', 'templateInput', payload.templateInput);
    walkValue(findings, 'topicInputs', 'topicInputs', payload.topicInputs);

    const priceTable = payload.templateInput.quotation_price_data;
    const priceTableRows = Array.isArray(priceTable) ? priceTable : [];
    const firstPriceRow = Array.isArray(priceTableRows[0]) ? (priceTableRows[0] as unknown[]) : [];
    const itemsTable = payload.templateInput.items_table;
    const itemsTableRows = Array.isArray(itemsTable) ? itemsTable : [];
    const firstItemsRow = Array.isArray(itemsTableRows[0]) ? (itemsTableRows[0] as unknown[]) : [];

    pushFinding(findings, {
      field: 'templateInput.quotation_price_data',
      source: 'templateInput',
      status: Array.isArray(priceTable) ? 'PASS' : 'FAIL',
      value: Array.isArray(priceTable) ? `rows:${priceTable.length}` : priceTable,
      message: Array.isArray(priceTable)
        ? 'Price table input is present'
        : 'Price table input is missing or not a table'
    });
    pushFinding(findings, {
      field: 'templateInput.quotation_price_data[0][0]',
      source: 'templateInput',
      status: firstPriceRow[0] === 'Price (Exclude VAT)' ? 'PASS' : 'FAIL',
      value: firstPriceRow[0] ?? null,
      message: 'Price table label matches canonical text'
    });
    pushFinding(findings, {
      field: 'templateInput.quotation_price_data[0][1]',
      source: 'templateInput',
      status:
        typeof firstPriceRow[1] === 'string' && firstPriceRow[1].trim() && firstPriceRow[1] !== '-'
          ? 'PASS'
          : 'FAIL',
      value: firstPriceRow[1] ?? null,
      message: 'Price table amount cell is formatted'
    });
    pushFinding(findings, {
      field: 'templateInput.quotation_price_data[0][3]',
      source: 'templateInput',
      status:
        typeof firstPriceRow[3] === 'string' && firstPriceRow[3].trim()
          ? 'PASS'
          : 'FAIL',
      value: firstPriceRow[3] ?? null,
      message: 'Price table currency code cell is populated'
    });
    if (itemsTable !== undefined) {
      pushFinding(findings, {
        field: 'templateInput.items_table',
        source: 'templateInput',
        status: Array.isArray(itemsTable) ? 'PASS' : 'FAIL',
        value: Array.isArray(itemsTable) ? `rows:${itemsTable.length}` : itemsTable,
        message: Array.isArray(itemsTable)
          ? 'Product items table input is present'
          : 'Product items table input is missing or not table'
      });
      pushFinding(findings, {
        field: 'templateInput.items_table[0]',
        source: 'templateInput',
        status: firstItemsRow.length === 7 ? 'PASS' : 'FAIL',
        value: firstItemsRow.length,
        message: 'Product items table row shape uses seven columns'
      });
    }

    const staticLabelFindings = [...PDFME_STATIC_LABEL_FIELD_KEYS].map((fieldName) => {
      const value = payload.templateInput[fieldName];
      const valid = typeof value === 'string' && value.trim().length > 0 && value !== '-';
      const finding: PayloadFinding = {
        field: `templateInput.${fieldName}`,
        source: 'templateInput',
        status: valid ? 'PASS' : 'FAIL',
        value: value ?? null,
        message: valid ? 'Static label is populated' : 'Static label is empty'
      };
      pushFinding(findings, finding);
      return finding;
    });

    const status = findings.some((finding) => finding.status === 'FAIL') ? 'FAIL' : 'PASS';
    const report = {
      audit: 'runtime-payload',
      overallStatus: status,
      staticLabelsStatus: summarizeIssues(
        staticLabelFindings.map((finding) => finding.status)
      ),
      quotationCode: payload.quotationCode,
      templateName: payload.templateName,
      templateVersion: payload.templateVersion,
      findingCount: findings.length,
      findings
    };
    const artifactPath = await writeAuditArtifact('runtime-payload-report.json', report);

    console.log(JSON.stringify({ ...report, artifactPath }, null, 2));
  } finally {
    await sql.end({ timeout: 5 });
  }
}

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