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

type IntegrityIssue = {
  kind:
    | 'DUPLICATE_ACTIVE_TEMPLATE'
    | 'DUPLICATE_ACTIVE_VERSION'
    | 'ACTIVE_VERSION_WITHOUT_TEMPLATE'
    | 'MAPPING_WITHOUT_VERSION'
    | 'INVALID_APPROVED_TEMPLATE_VERSION'
    | 'ACTIVE_VERSION_SCHEMA_DRIFT'
    | 'MISSING_RETAINED_INACTIVE_VERSION';
  message: string;
  context: Record<string, unknown>;
};

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

  try {
    const [
      activeTemplateVersions,
      versionRows,
      orphanMappingRows,
      payload,
      templateRows,
    ] = await Promise.all([
      getActiveTemplateVersions(sql),
      sql`
        select
          v.id,
          v.template_id as "templateId",
          v.organization_id as "organizationId",
          o.name as "organizationName",
          v.version,
          v.file_path as "filePath",
          v.is_active as "isActive",
          v.schema_json as "schemaJson",
          t.id as "resolvedTemplateId"
        from crm_document_template_versions v
        left join crm_document_templates t on t.id = v.template_id
        left join organizations o on o.id = v.organization_id
        where v.deleted_at is null
      `,
      sql`
        select
          m.id,
          m.template_version_id as "templateVersionId",
          m.placeholder_key as "placeholderKey"
        from crm_document_template_mappings m
        left join crm_document_template_versions v on v.id = m.template_version_id
        where m.deleted_at is null
          and (v.id is null or v.deleted_at is not null)
      `,
      buildAuditPayload(sql),
      sql`
        select
          t.id,
          t.organization_id as "organizationId",
          o.name as "organizationName",
          t.template_name as "templateName"
        from crm_document_templates t
        inner join organizations o on o.id = t.organization_id
        where t.deleted_at is null
      `,
    ]);

    const issues: IntegrityIssue[] = [];
    const templateBuckets = new Map<string, Array<Record<string, unknown>>>();

    for (const record of activeTemplateVersions as unknown as Array<Record<string, unknown>>) {
      const bucketKey = `${String(record.organizationId)}:${String(record.templateName)}`;
      const records = templateBuckets.get(bucketKey) ?? [];
      records.push(record);
      templateBuckets.set(bucketKey, records);
    }

    for (const [bucketKey, records] of templateBuckets.entries()) {
      if (records.length <= 1) {
        continue;
      }

      issues.push({
        kind: 'DUPLICATE_ACTIVE_TEMPLATE',
        message: `Multiple active templates found for ${bucketKey}.`,
        context: {
          bucketKey,
          templateIds: records.map((record) => record.templateId),
          templates: records.map((record) => ({
            templateId: record.templateId,
            templateName: record.templateName,
            versionId: record.versionId,
            version: record.version,
          })),
        },
      });
    }

    const versionBuckets = new Map<string, Array<Record<string, unknown>>>();

    for (const row of versionRows as Array<Record<string, unknown>>) {
      if (!row.isActive) {
        continue;
      }

      const items = versionBuckets.get(String(row.templateId)) ?? [];
      items.push(row);
      versionBuckets.set(String(row.templateId), items);

      if (!row.resolvedTemplateId) {
        issues.push({
          kind: 'ACTIVE_VERSION_WITHOUT_TEMPLATE',
          message: `Active version ${String(row.id)} has no parent template.`,
          context: row,
        });
      }
    }

    for (const [templateId, rows] of versionBuckets.entries()) {
      if (rows.length <= 1) {
        continue;
      }

      issues.push({
        kind: 'DUPLICATE_ACTIVE_VERSION',
        message: `Template ${templateId} has more than one active version.`,
        context: {
          templateId,
          versions: rows.map((row) => ({
            id: row.id,
            version: row.version,
          })),
        },
      });
    }

    for (const templateRow of templateRows as Array<Record<string, unknown>>) {
      const templateId = String(templateRow.id);
      const rows = (versionRows as Array<Record<string, unknown>>).filter(
        (row) => String(row.templateId) === templateId,
      );
      const activeRows = rows.filter((row) => Boolean(row.isActive));
      const inactiveRows = rows.filter((row) => !row.isActive);
      const templateSource =
        loadTemplateSourceByRelativePath(
          (activeRows[0]?.filePath as string | null | undefined) ?? null,
        ) ??
        loadTemplateSourceByOrganizationName(String(templateRow.organizationName));

      if (templateSource && activeRows.length === 1) {
        const activeSchema =
          typeof activeRows[0].schemaJson === 'string'
            ? JSON.parse(String(activeRows[0].schemaJson))
            : activeRows[0].schemaJson;

        if (JSON.stringify(activeSchema) !== JSON.stringify(templateSource.schemaJson)) {
          issues.push({
            kind: 'ACTIVE_VERSION_SCHEMA_DRIFT',
            message: `Active template version does not match source JSON for ${String(templateRow.templateName)}.`,
            context: {
              templateId,
              organizationName: templateRow.organizationName,
              activeVersionId: activeRows[0].id,
              activeVersion: activeRows[0].version,
              sourceFile: templateSource.relativePath,
            },
          });
        }
      }

      if (rows.length > 1 && inactiveRows.length === 0) {
        issues.push({
          kind: 'MISSING_RETAINED_INACTIVE_VERSION',
          message: `Template ${String(templateRow.templateName)} has no retained inactive historical version.`,
          context: {
            templateId,
            organizationName: templateRow.organizationName,
          },
        });
      }
    }

    for (const row of orphanMappingRows as Array<Record<string, unknown>>) {
      issues.push({
        kind: 'MAPPING_WITHOUT_VERSION',
        message: `Mapping ${String(row.id)} points to a missing template version.`,
        context: row,
      });
    }

    const approvedTemplateVersionExists = payload.approvedTemplateVersionId
      ? (versionRows as Array<Record<string, unknown>>).some(
          (row) => String(row.id) === payload.approvedTemplateVersionId,
        )
      : true;

    if (payload.approvedTemplateVersionId && !approvedTemplateVersionExists) {
      issues.push({
        kind: 'INVALID_APPROVED_TEMPLATE_VERSION',
        message: 'Approved snapshot references a template version that no longer exists.',
        context: {
          quotationCode: payload.quotationCode,
          approvedTemplateVersionId: payload.approvedTemplateVersionId,
        },
      });
    }

    const overallStatus = summarizeIssues(
      issues.length === 0 ? ['PASS'] : issues.map(() => 'FAIL'),
    );
    const artifactPath = await writeAuditArtifact('template-version-report.json', {
      audit: 'template-version-integrity',
      overallStatus,
      activeTemplateCount: activeTemplateVersions.length,
      quotationCode: payload.quotationCode,
      approvedTemplateVersionId: payload.approvedTemplateVersionId,
      approvedTemplateVersionIsActive: payload.approvedTemplateVersionId
        ? (versionRows as Array<Record<string, unknown>>).some(
            (row) =>
              String(row.id) === payload.approvedTemplateVersionId && Boolean(row.isActive),
          )
        : false,
      issues,
    });

    console.log(
      JSON.stringify(
        {
          audit: 'template-version-integrity',
          overallStatus,
          activeTemplateCount: activeTemplateVersions.length,
          quotationCode: payload.quotationCode,
          approvedTemplateVersionId: payload.approvedTemplateVersionId,
          approvedTemplateVersionIsActive: payload.approvedTemplateVersionId
            ? (versionRows as Array<Record<string, unknown>>).some(
                (row) =>
                  String(row.id) === payload.approvedTemplateVersionId && Boolean(row.isActive),
              )
            : false,
          issues,
          artifactPath,
        },
        null,
        2,
      ),
    );
  } finally {
    await sql.end({ timeout: 5 });
  }
}

main().catch((error) => {
  console.error(`[audit-template-version-integrity] ${error.message}`);
  process.exit(1);
});
