<?php
/**
 * cron_supervisor · Slice 8Z-v — automation supervisor.
 *
 * Runs the three CronSweeps daily:
 *   1. Auto-confirm receipt on PAID/ISSUED internal-cash LPOs after N days
 *   2. Notify Procurement when RETENTION terms become claimable
 *   3. Nudge raisers about overdue retirements (with re-nudge throttle)
 *
 * Schedule via Windows Task Scheduler:
 *   Program:   c:\wamp64\bin\php\php8.2.0\php.exe
 *   Args:      -f c:\wamp64\www\rsflow.dspera\scripts\cron_supervisor.php
 *   Frequency: daily, e.g. 07:00 local
 *
 * Idempotent — running multiple times per day won't double-confirm, double-notify, or
 * double-nudge (each sweep tracks its own state).
 *
 * Exit codes:
 *   0 — all sweeps ran; some may have errors (surfaced in output but not fatal)
 *   1 — bootstrap or fatal DB failure
 */
define('DS', DIRECTORY_SEPARATOR);
define('ROOT', dirname(__DIR__));
require_once ROOT . DS . 'config' . DS . 'config.php';
require_once ROOT . DS . 'core' . DS . 'Application.php';
require_once ROOT . DS . 'core' . DS . 'Model.php';
require_once ROOT . DS . 'core' . DS . 'DB.php';
$_SESSION = $_SESSION ?? [];
foreach (['CronSweeps.php','Lpos.php','LpoRetirements.php','LpoPaymentTerms.php','Notifications.php'] as $m) {
    require_once ROOT . DS . 'app' . DS . 'models' . DS . $m;
}

function h(string $t): void { echo "\n==== $t ====\n"; }
function p(string $t): void { echo "  $t\n"; }

$startedAt = date('Y-m-d H:i:s');
echo "cron_supervisor · rsflow.dspera · started $startedAt\n";
echo str_repeat('-', 60) . "\n";

try {
    $sweeps = new CronSweeps();

    /* ── 1. Receipt auto-confirm ──────────────────────────────────────── */
    h('1. Receipt auto-confirm sweep');
    $r1 = $sweeps->receiptAutoConfirm();
    p("scanned=$r1[scanned]  confirmed=$r1[confirmed]  errors=$r1[errors]");
    foreach ($r1['done'] as $d)         p("  ✓ auto-confirmed {$d['lpo_no']} · retirement due {$d['due']}");
    foreach ($r1['error_rows'] as $er)  p("  ✗ {$er['lpo_no']} — {$er['msg']}");

    /* ── 2. Retention timer elapsed ───────────────────────────────────── */
    h('2. Retention timer sweep');
    $r2 = $sweeps->retentionTimerElapsed();
    p("scanned=$r2[scanned]  notified=$r2[notified]  skipped=$r2[skipped]");
    foreach ($r2['done'] as $d)         p("  ✓ retention claimable · {$d['lpo_no']} · " . number_format((float)$d['amount']));

    /* ── 3. Retirement overdue nudges ─────────────────────────────────── */
    h('3. Retirement overdue sweep');
    $r3 = $sweeps->retirementOverdueSweep();
    p("scanned=$r3[scanned]  notified=$r3[notified]  skipped=$r3[skipped]");
    foreach ($r3['done'] as $d)         p("  ✓ nudged {$d['lpo_no']} · {$d['days_overdue']}d overdue" . ($d['first_ping'] ? ' (first ping)' : ''));

    /* ── 4. Sales aging refresh (recompute outstanding + days_overdue) ─── */
    h('4. Sales aging refresh');
    $r4 = $sweeps->refreshSalesAging();
    p("scanned=$r4[scanned]  refreshed=$r4[refreshed]");

    /* ── 5. Dunning nudges (soft/firm/FM/Exec/legal) ──────────────────── */
    h('5. Dunning nudges');
    $r5 = $sweeps->dunningNudges();
    p("scanned=$r5[scanned]  skipped=$r5[skipped]");
    p("  soft={$r5['done']['soft']}  firm={$r5['done']['firm']}  fm={$r5['done']['fm']}  exec={$r5['done']['exec']}  legal={$r5['done']['legal']}");

    /* ── 6. PM daily spawn (Services) ─────────────────────────────────── */
    h('6. PM daily spawn');
    $r6 = $sweeps->pmDailySpawn(7);
    p("scanned=$r6[scanned]  spawned=$r6[spawned]  errors=$r6[errors]");

    /* ── 7. Monthly service invoice draft ─────────────────────────────── */
    h('7. Monthly service invoice draft');
    // Only actually drafts if we're on day 1 of month (idempotent guard still runs otherwise)
    $r7 = $sweeps->monthlyServiceInvoiceDraft();
    p("scanned=$r7[scanned]  drafted=$r7[drafted]  skipped=$r7[skipped]  errors=$r7[errors]");

    $endedAt = date('Y-m-d H:i:s');
    echo "\n" . str_repeat('-', 60) . "\n";
    echo "cron_supervisor · finished $endedAt\n";
    $dunTotal = array_sum($r5['done']);
    echo "summary: {$r1['confirmed']} confirmed · {$r2['notified']} retention · {$r3['notified']} overdue · {$r4['refreshed']} aging · {$dunTotal} dunning · {$r6['spawned']} PMs · {$r7['drafted']} invoices\n";
    exit(0);
} catch (\Throwable $e) {
    echo "\nFATAL: " . $e->getMessage() . "\n";
    echo $e->getTraceAsString() . "\n";
    exit(1);
}
