#!/usr/bin/env php
<?php
/**
 * Daily report orchestrator — checks which reports are due today and
 * schedules their execution at the configured time via the `at` command.
 *
 * Runs daily at 00:05 via cron.
 *
 * Usage:
 *   php exploitation/schedule_reports.php [options]
 *
 * Options:
 *   -q, --quiet   Suppress output
 *   -h, --help    Show this help
 *
 * Cron:
 *   5 0 * * * cd /opt/app && php exploitation/schedule_reports.php --quiet
 */

require_once dirname(__DIR__) . '/shared/cli_bootstrap.php';

$opts  = getopt('qh', ['quiet', 'help']);
$quiet = isset($opts['q']) || isset($opts['quiet']);

if (isset($opts['h']) || isset($opts['help'])) cli_show_help(__FILE__);

function out(string $msg): void {
    global $quiet;
    if (!$quiet) echo date('Y-m-d H:i:s') . '  ' . $msg . PHP_EOL;
}

/* ── Vérifier que `at` est disponible ──────────────────────────────────── */
$atPath = trim(shell_exec('which at 2>/dev/null') ?? '');
if ($atPath === '') {
    out('ERROR: `at` command not found. Install the `at` package (yum install at / apt install at).');
    exit(1);
}

/* ── Charger les planifications activées ───────────────────────────────── */
$schedules = $db->fetchAll("SELECT * FROM report_schedules WHERE enabled = 1");
if (empty($schedules)) {
    out('No enabled report schedules. Nothing to do.');
    exit(0);
}

$phpBin = PHP_BINARY;
$appDir = realpath(ROOT_PATH);
$today  = new \DateTime('today');
$scheduled = 0;

foreach ($schedules as $sched) {
    $rk   = $sched['report_key'];
    $freq = $sched['frequency'];
    $dc   = $sched['day_config'] ? json_decode($sched['day_config'], true) : [];
    $time = $sched['time_of_day'] ?: '08:00';

    if (!isDueToday($freq, $dc, $today)) {
        out("  SKIP: {$rk} ({$freq}) — not due today.");
        continue;
    }

    // Planifier via `at`
    $cmd = "cd {$appDir} && {$phpBin} exploitation/core/send_report.php --report " . escapeshellarg($rk) . " --quiet";
    $atCmd = "echo " . escapeshellarg($cmd) . " | at {$time} 2>&1";
    $output = trim(shell_exec($atCmd) ?? '');
    out("  SCHEDULED: {$rk} at {$time} — {$output}");
    $scheduled++;
}

out("Done: {$scheduled} report(s) scheduled for today.");
exit(0);

/* ══════════════════════════════════════════════════════════════════════════
 *  Date matching helpers
 * ══════════════════════════════════════════════════════════════════════════ */

function isDueToday(string $freq, array $dc, \DateTime $today): bool
{
    return match ($freq) {
        'daily'   => true,
        'weekly'  => matchWeekly($dc, $today),
        'monthly' => matchMonthly($dc, $today),
        default   => false,
    };
}

function matchWeekly(array $dc, \DateTime $today): bool
{
    $dow = (int)($dc['dow'] ?? 1); // 1=Mon, 7=Sun (ISO 8601)
    return (int)$today->format('N') === $dow;
}

function matchMonthly(array $dc, \DateTime $today): bool
{
    if (isset($dc['dom'])) {
        return matchMonthlyNumeric((int)$dc['dom'], $today);
    }
    if (isset($dc['logical'])) {
        return matchMonthlyLogical($dc['logical'], $today);
    }
    return false;
}

function matchMonthlyNumeric(int $dom, \DateTime $today): bool
{
    $todayDom   = (int)$today->format('j');
    $lastDay    = (int)$today->format('t');
    // Si dom > jours du mois, clamp au dernier jour
    $effectiveDom = min($dom, $lastDay);
    return $todayDom === $effectiveDom;
}

function matchMonthlyLogical(string $logical, \DateTime $today): bool
{
    $year  = (int)$today->format('Y');
    $month = (int)$today->format('n');

    if ($logical === 'last_day') {
        return (int)$today->format('j') === (int)$today->format('t');
    }

    // Parse "first_monday", "last_friday", etc.
    if (preg_match('/^(first|last)_(monday|tuesday|wednesday|thursday|friday)$/', $logical, $m)) {
        $which   = $m[1];
        $dayName = $m[2];

        if ($which === 'first') {
            // 1er jour du mois, avancer au bon jour de semaine
            $d = new \DateTime("{$year}-{$month}-01");
            $target = ucfirst($dayName);
            if ($d->format('l') !== $target) {
                $d->modify("next {$dayName}");
            }
        } else {
            // Dernier jour du mois, reculer au bon jour de semaine
            $lastDay = (int)(new \DateTime("{$year}-{$month}-01"))->format('t');
            $d = new \DateTime("{$year}-{$month}-{$lastDay}");
            $target = ucfirst($dayName);
            if ($d->format('l') !== $target) {
                $d->modify("last {$dayName}");
            }
        }

        return $today->format('Y-m-d') === $d->format('Y-m-d');
    }

    return false;
}
