#!/usr/bin/env php
<?php

/**
 * Notifier Scheduler - Cron Job Entry Point
 *
 * Usage:
 *   php scheduler.php           Run the scheduler
 *   php scheduler.php --dry-run Show what would be processed
 *   php scheduler.php --status  Show scheduler status
 *   php scheduler.php --help    Show this help
 *
 * Crontab example (run every minute):
 *   * * * * * php /path/to/notifier/scheduler.php >> /var/log/notifier.log 2>&1
 */

declare(strict_types=1);

// Ensure CLI only
if (php_sapi_name() !== 'cli') {
    http_response_code(403);
    exit('CLI only');
}

// Bootstrap
require_once __DIR__ . '/src/database.php';
require_once __DIR__ . '/src/services/recurrence-calculator.php';
require_once __DIR__ . '/src/services/telegram-service.php';
require_once __DIR__ . '/src/services/job-service.php';
require_once __DIR__ . '/src/services/notification-service.php';
require_once __DIR__ . '/src/services/scheduler-service.php';

use Notifier\Database;
use Notifier\Services\RecurrenceCalculator;
use Notifier\Services\TelegramService;
use Notifier\Services\JobService;
use Notifier\Services\SchedulerService;

// Parse arguments
$args = getopt('', ['dry-run', 'status', 'help', 'verbose']);
$dryRun = isset($args['dry-run']);
$status = isset($args['status']);
$help = isset($args['help']);
$verbose = isset($args['verbose']);

// Show help
if ($help) {
    echo <<<HELP
Notifier Scheduler - Cron Job Entry Point

Usage:
  php scheduler.php           Run the scheduler
  php scheduler.php --dry-run Show what would be processed (no changes)
  php scheduler.php --status  Show scheduler status
  php scheduler.php --verbose Show detailed output
  php scheduler.php --help    Show this help

Exit codes:
  0  Success
  1  Error (check output)
  2  Lock held by another process

Crontab example (run every minute):
  * * * * * php /path/to/notifier/scheduler.php >> /var/log/notifier.log 2>&1

HELP;
    exit(0);
}

// Load config
$configPath = __DIR__ . '/config.php';
if (!file_exists($configPath)) {
    fwrite(STDERR, "Error: config.php not found. Copy config.example.php to config.php\n");
    exit(1);
}
$config = require $configPath;

// Initialize database
try {
    $database = new Database($config['database']['path'] ?? __DIR__ . '/database.sqlite');
    $pdo = $database->getPdo();
} catch (Exception $e) {
    fwrite(STDERR, "Database error: " . $e->getMessage() . "\n");
    exit(1);
}

// Ensure confirmation links can be generated
$appUrl = $config['app_url'] ?? null;
if (!is_string($appUrl) || trim($appUrl) === '') {
    fwrite(STDERR, "Error: app_url is required for confirmation links. Set app_url in config.php.\n");
    exit(1);
}
$columns = $pdo->query('PRAGMA table_info(notification_history)')->fetchAll(PDO::FETCH_ASSOC);
$columnNames = array_column($columns, 'name');
$missingColumns = array_diff(['confirm_token', 'confirmed_at'], $columnNames);
if (!empty($missingColumns)) {
    fwrite(
        STDERR,
        "Error: notification_history missing columns: " . implode(', ', $missingColumns) . ". Run php scripts/migrate.php.\n"
    );
    exit(1);
}
$stateTable = $pdo->query("SELECT name FROM sqlite_master WHERE type='table' AND name='scheduler_state'")->fetchColumn();
if (!$stateTable) {
    fwrite(STDERR, "Error: scheduler_state table missing. Run php scripts/migrate.php.\n");
    exit(1);
}
$pdo->exec("
    INSERT OR IGNORE INTO scheduler_state
    (id, last_run_at, last_success_at, last_error, last_alert_at, updated_at)
    VALUES (1, datetime('now'), NULL, NULL, NULL, datetime('now'))
");

// Initialize services
$jobService = new JobService($pdo);
$telegram = TelegramService::fromConfig($config['telegram']);
$recurrenceCalc = new RecurrenceCalculator();
$timezone = $config['display_timezone'] ?? 'UTC';
$schedulerConfig = $config['scheduler'] ?? [];
$schedulerConfig['upload_path'] = $config['upload_path'] ?? (__DIR__ . '/uploads');
$schedulerConfig['app_url'] = $config['app_url'] ?? null;

$scheduler = new SchedulerService(
    $pdo,
    $jobService,
    $telegram,
    $recurrenceCalc,
    $timezone,
    $schedulerConfig
);

// Status check
if ($status) {
    $lockFile = $schedulerConfig['lock_file'] ?? '/tmp/notifier.lock';
    $isLocked = file_exists($lockFile);
    $pid = $isLocked ? (int) @file_get_contents($lockFile) : 0;
    $lockAge = $isLocked ? (time() - filemtime($lockFile)) : 0;

    echo "Scheduler Status\n";
    echo "================\n";
    echo "Lock file: {$lockFile}\n";
    echo "Lock held: " . ($isLocked ? "Yes (PID: {$pid}, age: {$lockAge}s)" : "No") . "\n";

    // Get job stats
    $stats = $jobService->getStats();
    echo "\nJob Statistics:\n";
    if (empty($stats)) {
        echo "  No jobs in queue\n";
    } else {
        foreach ($stats as $status => $count) {
            echo "  {$status}: {$count}\n";
        }
    }

    // Dry run preview
    $preview = $scheduler->dryRun();
    echo "\nPending Work:\n";
    echo "  Stuck jobs to reset: {$preview['stuck_jobs']}\n";
    echo "  Date jobs to create: {$preview['date_jobs_to_create']}\n";
    echo "  Recurrence jobs to create: {$preview['recurrence_jobs_to_create']}\n";
    echo "  Jobs ready to send: " . count($preview['pending_jobs']) . "\n";

    exit(0);
}

// Dry run
if ($dryRun) {
    echo "Dry Run - No changes will be made\n";
    echo "==================================\n\n";

    $preview = $scheduler->dryRun();

    echo "Stuck jobs to reset: {$preview['stuck_jobs']}\n";
    echo "Date jobs to create: {$preview['date_jobs_to_create']}\n";
    echo "Recurrence jobs to create: {$preview['recurrence_jobs_to_create']}\n";
    echo "\nPending jobs to process (" . count($preview['pending_jobs']) . "):\n";

    if (empty($preview['pending_jobs'])) {
        echo "  None\n";
    } else {
        foreach ($preview['pending_jobs'] as $job) {
            echo "  [{$job['id']}] {$job['title']} ({$job['job_type']}) - scheduled: {$job['scheduled_for']}\n";
        }
    }

    exit(0);
}

// Run the scheduler
$startTime = microtime(true);

if ($verbose) {
    echo "[" . date('Y-m-d H:i:s') . "] Starting scheduler...\n";
}

$lockAcquired = $scheduler->acquireLock();
if (!$lockAcquired) {
    fwrite(STDERR, "[" . date('Y-m-d H:i:s') . "] Scheduler lock held by another process\n");
    exit(2);
}

$success = false;
$stats = [];

try {
    $pdo->exec("
        UPDATE scheduler_state
        SET last_run_at = datetime('now'),
            last_error = NULL,
            updated_at = datetime('now')
        WHERE id = 1
    ");

    $success = $scheduler->runWithoutLock();
    $stats = $scheduler->getStats();
} finally {
    $scheduler->releaseLock();
}

$duration = round((microtime(true) - $startTime) * 1000);

if ($success) {
    $pdo->exec("
        UPDATE scheduler_state
        SET last_success_at = datetime('now'),
            last_error = NULL,
            updated_at = datetime('now')
        WHERE id = 1
    ");
    if ($verbose || $stats['jobs_processed'] > 0) {
        echo "[" . date('Y-m-d H:i:s') . "] Scheduler completed in {$duration}ms\n";
        echo "  Jobs created: {$stats['jobs_created']}\n";
        echo "  Jobs processed: {$stats['jobs_processed']}\n";
        echo "  Jobs sent: {$stats['jobs_sent']}\n";
        echo "  Jobs failed: {$stats['jobs_failed']}\n";
        echo "  Stuck reset: {$stats['stuck_reset']}\n";
        echo "  Recurrences updated: {$stats['recurrences_updated']}\n";
    }
    exit(0);
} else {
    $errorText = 'Unknown error';
    if (!empty($stats['errors'])) {
        $errorText = implode(' | ', $stats['errors']);
    }
    $stmt = $pdo->prepare("
        UPDATE scheduler_state
        SET last_error = ?,
            updated_at = datetime('now')
        WHERE id = 1
    ");
    $stmt->execute([$errorText]);

    fwrite(STDERR, "[" . date('Y-m-d H:i:s') . "] Scheduler failed\n");
    foreach ($stats['errors'] ?? [] as $error) {
        fwrite(STDERR, "  Error: {$error}\n");
    }

    exit(1);
}
