/**
 * Orphan Dispute Reconciler
 *
 * Detects PaymentDispute rows with payment_id IS NULL that are older than 24h.
 * These are "permanent orphans" — disputes for transactions whose payment webhook
 * never arrived (PayPal anomaly, missed delivery, or adversarial activity).
 *
 * Idempotency: each dispute is only flagged once. An AuditLog row with
 * action='dispute.permanent_orphan' acts as a sentinel — if it already exists
 * for a given dispute, that dispute is skipped in subsequent passes.
 *
 * Surfaces alerts via three already-deployed mechanisms:
 *   1. logger.error — picked up by log-based alerts (grep target: 'Permanent orphan dispute detected')
 *   2. permanentOrphanDisputesTotal Prometheus counter — Grafana / Alertmanager
 *   3. AuditLog row — queryable by admin panel / SQL at any time
 */

import { PrismaClient } from '@prisma/client';
import { logger } from '../utils/logger';
import { permanentOrphanDisputesTotal } from '../utils/metrics';

const prisma = new PrismaClient();

/**
 * Summary returned by each reconciliation pass.
 */
export interface ReconciliationSummary {
  /** Total rows matched by the SQL filter (payment_id IS NULL AND opened_at < now-24h). */
  scanned: number;
  /** Disputes newly flagged this pass (logged + counted + audit-logged). */
  newlyFlagged: number;
  /** Disputes skipped because an AuditLog sentinel already existed. */
  reFlagged: number;
}

const ORPHAN_THRESHOLD_HOURS = 24;

/**
 * Run one reconciliation pass.
 *
 * @returns A summary object with scanned / newlyFlagged / reFlagged counts.
 */
export async function reconcilePermanentOrphanDisputes(): Promise<ReconciliationSummary> {
  const cutoff = new Date(Date.now() - ORPHAN_THRESHOLD_HOURS * 60 * 60 * 1000);

  // Fetch all disputes with no payment link that are older than 24h.
  const orphans = await prisma.paymentDispute.findMany({
    where: {
      payment_id: null,
      opened_at: { lt: cutoff },
    },
    select: {
      id: true,
      paypal_dispute_id: true,
      paypal_sale_id: true,
      reason: true,
      amount: true,
      currency: true,
      opened_at: true,
    },
  });

  const scanned = orphans.length;
  let newlyFlagged = 0;
  let reFlagged = 0;

  for (const dispute of orphans) {
    // Idempotency check: has this dispute already been flagged in a previous pass?
    const existingSentinel = await prisma.auditLog.findFirst({
      where: {
        action: 'dispute.permanent_orphan',
        resource_type: 'payment_dispute',
        resource_id: dispute.id,
      },
      select: { id: true },
    });

    if (existingSentinel) {
      reFlagged++;
      continue;
    }

    // Compute age in whole hours for structured logging and audit details.
    const ageHours = Math.floor(
      (Date.now() - dispute.opened_at.getTime()) / (60 * 60 * 1000)
    );

    const disputeId = dispute.id;
    const paypalDisputeId = dispute.paypal_dispute_id;
    const paypalSaleId = dispute.paypal_sale_id ?? null;
    const openedAt = dispute.opened_at;
    const amount = dispute.amount.toString();
    const currency = dispute.currency;
    const reason = dispute.reason;

    // 1. Structured error log — stable message string is the grep target for
    //    future log-based alert rules.
    logger.error('Permanent orphan dispute detected', {
      disputeId,
      paypalDisputeId,
      paypalSaleId,
      openedAt,
      amount,
      currency,
      ageHours,
    });

    // 2. Prometheus counter — increment per reason label so dashboards can
    //    distinguish chargeback floods from isolated unrecognized disputes.
    permanentOrphanDisputesTotal.inc({ reason });

    // 3. AuditLog sentinel — prevents re-alerting on subsequent passes and
    //    makes the event queryable by the admin panel.
    //    user_id is omitted (nullable in schema) because this is a system-generated
    //    event with no acting user — same pattern used by background audit writes.
    await prisma.auditLog.create({
      data: {
        action: 'dispute.permanent_orphan',
        resource_type: 'payment_dispute',
        resource_id: dispute.id,
        details: {
          paypalDisputeId,
          paypalSaleId,
          amount,
          currency,
          ageHours,
          detectedAt: new Date(),
        },
      },
    });

    newlyFlagged++;
  }

  return { scanned, newlyFlagged, reFlagged };
}

// ---------------------------------------------------------------------------
// One-shot entry point for manual runs:
//   npx tsx src/workers/orphanDisputeReconciler.ts
// ---------------------------------------------------------------------------
if (require.main === module) {
  reconcilePermanentOrphanDisputes()
    .then((summary) => {
      console.log('Reconciliation complete:', summary);
      process.exit(0);
    })
    .catch((err) => {
      console.error('Reconciliation failed:', err);
      process.exit(1);
    });
}
