<?php
/**
 * Batchs / Ordonnancement — gestion des tâches planifiées (réservé app_admin).
 *
 *  - Affiche le planning proposé (cron Linux + schtasks Windows).
 *  - Montre l'état de la crontab courante (utilisateur apache).
 *  - Active / désactive les entrées de l'application dans la crontab.
 *  - Liste les dernières exécutions des jobs (ASH, RMAN, sauvegardes, app.log).
 *
 * NB : la logique cron est volontairement autonome (pas de dépendance à
 * exploitation/core/schedule.php) mais partage le même marqueur pour rester
 * cohérente avec le CLI.
 */
require_once dirname(__DIR__) . '/bootstrap.php';

$auth->requireRole(ROLE_APP_ADMIN);

if (!Session::has('csrf_token')) {
    Session::set('csrf_token', bin2hex(random_bytes(32)));
}
$csrfToken = Session::get('csrf_token');

$error   = null;
$success = null;

$isWindows  = PHP_OS_FAMILY === 'Windows';
$hasShell   = function_exists('shell_exec') && function_exists('exec');
$appDir     = realpath(ROOT_PATH) ?: ROOT_PATH;
$phpBin     = PHP_BINARY;
$MARKER     = '# sw-app-exploitation';

/* ── Liste des jobs : cœur + modules actifs (source unique ScheduleRegistry).
   Ne contient que les scripts présents sur ce déploiement. ─────────────── */
$jobs = ScheduleRegistry::jobs($moduleRegistry);

$cronLine = static fn(array $j): string =>
    "{$j['cron']} cd {$appDir} && {$phpBin} {$j['script']} {$j['args']}";
$schtasksLine = static function (array $j) use ($appDir, $phpBin): string {
    $cmd = "{$phpBin} {$appDir}\\" . str_replace('/', '\\', $j['script']) . " {$j['args']}";
    return "schtasks /create /tn \"{$j['task']}\" /tr \"{$cmd}\" /sc {$j['win_sc']} /st {$j['win_st']} /f";
};

$readCrontab = static function () use ($hasShell): string {
    if (!$hasShell) return '';
    return (string)(shell_exec('crontab -l 2>/dev/null') ?? '');
};
$writeCrontab = static function (string $content) use ($hasShell): bool {
    if (!$hasShell) return false;
    $tmp = tempnam(sys_get_temp_dir(), 'cron_');
    file_put_contents($tmp, rtrim($content, "\n") . "\n");
    exec('crontab ' . escapeshellarg($tmp), $o, $rc);
    @unlink($tmp);
    return $rc === 0;
};

/* ── POST : activer / désactiver ────────────────────────────────────────── */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!hash_equals(Session::get('csrf_token', ''), $_POST['csrf_token'] ?? '')) {
        $error = t('msg.error');
    } elseif (!$hasShell || $isWindows) {
        $error = t('batch.shell_unavailable');
    } else {
        $action   = $_POST['action'] ?? '';
        $existing = $readCrontab();

        if ($action === 'install') {
            $toAdd = [];
            foreach ($jobs as $j) {
                if (!str_contains($existing, $j['script'])) {
                    $toAdd[] = $cronLine($j) . '  ' . $MARKER;
                }
            }
            if (empty($toAdd)) {
                $success = t('batch.already_installed');
            } else {
                $new = ($existing !== '' ? rtrim($existing, "\n") . "\n" : '') . implode("\n", $toAdd) . "\n";
                if ($writeCrontab($new)) {
                    $success = t('batch.install_ok', ['n' => count($toAdd)]);
                } else {
                    $error = t('batch.crontab_error');
                }
            }
        } elseif ($action === 'uninstall') {
            $knownPaths = array_map(fn($j) => $j['script'], $jobs);
            $kept = array_filter(explode("\n", $existing), function ($line) use ($MARKER, $knownPaths) {
                if (str_contains($line, $MARKER)) return false;
                foreach ($knownPaths as $p) {
                    if (str_contains($line, $p)) return false;
                }
                return true;
            });
            if ($writeCrontab(implode("\n", $kept))) {
                $success = t('batch.uninstall_ok');
            } else {
                $error = t('batch.crontab_error');
            }
        }
    }
}

/* ── État courant ───────────────────────────────────────────────────────── */
$currentCrontab = $readCrontab();
$installed = [];
foreach ($jobs as $j) {
    $installed[$j['key']] = str_contains($currentCrontab, $j['script']);
}

/* ── Dernières exécutions ──────────────────────────────────────────────── */
$ashRuns  = [];
$rmanRuns = [];
$backups  = [];
$appLog   = '';

$ashPath = SECURITE_PATH . '/ash_history.sqlite';
if (is_file($ashPath)) {
    $sq = new SQLite3($ashPath, SQLITE3_OPEN_READONLY);
    if ($sq->querySingle("SELECT 1 FROM sqlite_master WHERE type='table' AND name='collect_log'")) {
        $res = $sq->query("SELECT db_unique_name, source, sample_date, collected_at, row_count
                           FROM collect_log ORDER BY collected_at DESC LIMIT 15");
        while ($r = $res->fetchArray(SQLITE3_ASSOC)) $ashRuns[] = $r;
    }
    $sq->close();
}

$rmanPath = SECURITE_PATH . '/rman_backups.sqlite';
if (is_file($rmanPath)) {
    $sq = new SQLite3($rmanPath, SQLITE3_OPEN_READONLY);
    if ($sq->querySingle("SELECT 1 FROM sqlite_master WHERE type='table' AND name='rman_collect_log'")) {
        $res = $sq->query("SELECT label, collected_at, ok, msg, db_count
                           FROM rman_collect_log ORDER BY collected_at DESC");
        while ($r = $res->fetchArray(SQLITE3_ASSOC)) $rmanRuns[] = $r;
    }
    $sq->close();
}

$backupDir = SECURITE_PATH . '/backups';
if (is_dir($backupDir)) {
    foreach (scandir($backupDir, SCANDIR_SORT_DESCENDING) as $f) {
        if (!str_ends_with($f, '.zip')) continue;
        $full = $backupDir . '/' . $f;
        $backups[] = ['name' => $f, 'size' => filesize($full), 'date' => date('Y-m-d H:i:s', filemtime($full))];
        if (count($backups) >= 10) break;
    }
}

if (defined('LOG_APP_FILE') && is_file(LOG_APP_FILE)) {
    $lines = @file(LOG_APP_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
    $appLog = implode("\n", array_slice($lines, -50));
}

$pageTitle = t('batch.title');

View::display(__DIR__ . '/views/batch.php', [
    'pageTitle'      => $pageTitle,
    'error'          => $error,
    'success'        => $success,
    'isWindows'      => $isWindows,
    'hasShell'       => $hasShell,
    'jobs'           => $jobs,
    'cronLine'       => $cronLine,
    'schtasksLine'   => $schtasksLine,
    'installed'      => $installed,
    'currentCrontab' => $currentCrontab,
    'marker'         => $MARKER,
    'ashRuns'        => $ashRuns,
    'rmanRuns'       => $rmanRuns,
    'backups'        => $backups,
    'appLog'         => $appLog,
    'csrfToken'      => $csrfToken,
]);
