#!/usr/bin/env php
<?php
/**
 * WHP delete-user worker.
 *
 * Usage: php run-delete-user.php <job_id>
 *
 * Reads the job spec from /docker/whp/sql/delete-user-jobs/<job_id>.json,
 * calls usermgmt::delete_user() with a JSONL-writing logger, and writes a
 * terminal `{__done__: true, summary: {...}}` row when complete.
 *
 * The HTTP endpoint (api/delete-user-ajax.php) returns 202 immediately
 * after writing the spec + params and invoking `systemctl start --no-block`.
 * The panel polls api/delete-user-ajax.php?action=status&job_id=X to render
 * the progress modal.
 *
 * Spec JSON shape (written by api/delete-user-ajax.php):
 *   {
 *     "job_id":    "delete-user-<username>-YYYYMMDD-HHMMSS-XXXXXXXX",
 *     "username":  "<panel-username>",
 *     "actor":     "<AUTH_USER who initiated>",
 *     "created_at": 1717000000
 *   }
 */

require_once '/docker/whp/web/includes/safety-net.php';

if ($argc < 2) {
    fwrite(STDERR, "Usage: php run-delete-user.php <job_id>\n");
    exit(2);
}

$jobId = $argv[1];
if (!preg_match('/^[A-Za-z0-9_\-]{8,160}$/', $jobId)) {
    fwrite(STDERR, "Invalid job_id (regex)\n");
    exit(2);
}

$jobsDir   = '/docker/whp/sql/delete-user-jobs';
$specPath  = $jobsDir . '/' . $jobId . '.json';
$jsonlPath = $jobsDir . '/' . $jobId . '.jsonl';

if (!is_file($specPath)) {
    fwrite(STDERR, "Spec file not found: $specPath\n");
    exit(2);
}

$rawSpec = @file_get_contents($specPath);
if ($rawSpec === false) {
    fwrite(STDERR, "Failed to read spec: $specPath\n");
    exit(2);
}
$spec = json_decode($rawSpec, true);
if (!is_array($spec)) {
    fwrite(STDERR, "Spec is not valid JSON\n");
    exit(2);
}

$username = (string)($spec['username'] ?? '');
$actor    = (string)($spec['actor']    ?? 'unknown');
if ($username === '') {
    fwrite(STDERR, "Spec missing username\n");
    exit(2);
}

function appendJsonl(string $path, array $row): void {
    $row['at'] = $row['at'] ?? microtime(true);
    @file_put_contents($path, json_encode($row) . "\n", FILE_APPEND | LOCK_EX);
}

appendJsonl($jsonlPath, [
    '__queued__' => true,
    'job_id'     => $jobId,
    'username'   => $username,
    'actor'      => $actor,
]);

// Graceful cancel via SIGTERM (systemctl stop). TimeoutStopSec=30 gives us
// a window to write a cancellation row before SIGKILL escalation.
$cancelHandler = function (int $signo) use ($jsonlPath) {
    appendJsonl($jsonlPath, [
        '__cancelling__' => true,
        'reason'         => $signo === SIGTERM ? 'SIGTERM' : 'SIGINT',
    ]);
};
if (function_exists('pcntl_signal') && function_exists('pcntl_async_signals')) {
    pcntl_async_signals(true);
    pcntl_signal(SIGTERM, $cancelHandler);
    pcntl_signal(SIGINT,  $cancelHandler);
}

require_once '/docker/whp/web/configs/config.php';
require_once '/docker/whp/web/libs/usermgmt.php';

$logger = function (string $step, string $status, array $meta = []) use ($jsonlPath) {
    appendJsonl($jsonlPath, array_merge(
        ['step' => $step, 'status' => $status],
        $meta
    ));
};

$startedAt = microtime(true);
appendJsonl($jsonlPath, ['__started__' => true]);

try {
    $um = new usermgmt();
    $result = $um->delete_user($username, ['logger' => $logger]);

    appendJsonl($jsonlPath, [
        '__done__'    => true,
        'status'      => $result['status'] ?? '0',
        'msg'         => $result['msg']    ?? '',
        'summary'     => isset($result['teardown']) ? [
            'sites'       => count($result['teardown']['sites']     ?? []),
            'databases'   => count($result['teardown']['databases'] ?? []),
            'db_users'    => count($result['teardown']['db_users']  ?? []),
            'pg_user'     => $result['teardown']['pg_user'],
            'pg_databases'=> count($result['teardown']['pg_databases'] ?? []),
            'domains'     => (int)($result['teardown']['rows']['domains'] ?? 0),
            'errors'      => count($result['teardown']['errors']    ?? []),
        ] : null,
        'teardown'    => $result['teardown'] ?? null,
        'duration_s'  => round(microtime(true) - $startedAt, 3),
        'finished_at' => time(),
    ]);
    exit(($result['status'] ?? '0') === '0' ? 0 : 1);
} catch (Throwable $e) {
    appendJsonl($jsonlPath, [
        '__done__'    => true,
        'status'      => '1',
        'msg'         => 'worker_exception: ' . $e->getMessage(),
        'trace'       => $e->getTraceAsString(),
        'duration_s'  => round(microtime(true) - $startedAt, 3),
        'finished_at' => time(),
    ]);
    fwrite(STDERR, "Worker exception: " . $e->getMessage() . "\n");
    exit(1);
}
