<?php
declare(strict_types=1);

/**
 * YachtMemory - short-lived anchorage geocoding runner, Phase 2C.2b.5 Stage 2.
 *
 * Manual invocation runs immediately, preserving the Stage 1 diagnostic path.
 * systemd invokes this script with --scheduled. In scheduled mode a technical
 * two-minute timer tick is reduced to the configurable application cadence by a
 * timestamp file in /tmp. flock prevents overlapping worker processes.
 *
 * Usage:
 *   php shared_dashboard/scripts/run_anchorage_geocoding_worker.php
 *   php shared_dashboard/scripts/run_anchorage_geocoding_worker.php --cache-only
 *   php shared_dashboard/scripts/run_anchorage_geocoding_worker.php --scheduled
 *   php shared_dashboard/scripts/run_anchorage_geocoding_worker.php --cache-limit=100
 */

require dirname(__DIR__, 2) . '/bootstrap.php';

ym_require('src/lib/DerivedDb.php');
ym_require('src/services/SystemSettingsService.php');
ym_require('src/services/AnchorageGeocoder.php');
ym_require('src/services/AnchorageGeocodingWorker.php');

$cacheOnly = in_array('--cache-only', $argv, true);
$scheduled = in_array('--scheduled', $argv, true);
$cacheLimit = 100;

foreach ($argv as $arg) {
    if (preg_match('/^--cache-limit=(\d+)$/', $arg, $m)) {
        $cacheLimit = max(1, min(1000, (int)$m[1]));
    }
}

$config = SystemSettingsService::getGeocodingConfig();
$workerIntervalMin = max(2, min(60, (int)($config['worker_interval_min'] ?? 5)));
$retryIntervalMin = max(10, min(360, (int)($config['retry_interval_min'] ?? 30)));

$lockHandle = null;
$stateFile = '/tmp/yachtmemory_anchorage_geocoding_worker.last_run';

if ($scheduled) {
    $lockHandle = fopen('/tmp/yachtmemory_anchorage_geocoding_worker.lock', 'c');
    if ($lockHandle === false) {
        fwrite(STDERR, "WORKER FAILED: cannot open scheduler lock file.\n");
        exit(1);
    }
    if (!flock($lockHandle, LOCK_EX | LOCK_NB)) {
        echo "YachtMemory Anchorage Geocoding Worker - another scheduled run is active; exiting.\n";
        exit(0);
    }

    $lastRun = is_file($stateFile) ? (int)trim((string)file_get_contents($stateFile)) : 0;
    $nextDue = $lastRun + ($workerIntervalMin * 60);
    if ($lastRun > 0 && time() < $nextDue) {
        printf(
            "YachtMemory Anchorage Geocoding Worker - scheduled tick skipped; next application run due in %d second(s).\n",
            max(1, $nextDue - time())
        );
        exit(0);
    }

    // Mark the application cycle before work starts. This prevents rapid retries
    // after an abnormal process exit; individual jobs additionally carry their
    // own retry/backoff timestamps in the Derived DB.
    if (file_put_contents($stateFile, (string)time(), LOCK_EX) === false) {
        fwrite(STDERR, "WORKER FAILED: cannot update scheduler state file.\n");
        exit(1);
    }
}

$pdo = DerivedDb::getPdo();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

echo "YachtMemory Anchorage Geocoding Worker - Phase 2C.2b.5 Stage 2\n";
echo "Invocation: " . ($scheduled ? "SCHEDULED" : "MANUAL") . "\n";
echo "Mode: " . ($cacheOnly ? "CACHE ONLY" : "CACHE + max. one network job") . "\n";
echo "Cache job limit: {$cacheLimit}\n";
echo "Worker interval: {$workerIntervalMin} min\n";
echo "Retry interval: {$retryIntervalMin} min\n\n";

$worker = new AnchorageGeocodingWorker(
    $pdo,
    $config,
    static function (string $message): void {
        echo '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL;
    }
);

try {
    $summary = $worker->run($cacheLimit, !$cacheOnly);
} catch (Throwable $e) {
    fwrite(STDERR, "WORKER FAILED: " . $e->getMessage() . PHP_EOL);
    exit(1);
}

echo "\n=== SUMMARY ===\n";
foreach ($summary as $key => $value) {
    printf("%-20s %d\n", $key . ':', $value);
}
