<?php
/**
 * TraderAlign — Hourly Cron
 * Schedule: Every hour
 * Tasks: Verification deadline check, session cleanup, expired token cleanup
 */

require_once __DIR__ . '/../config/config.php';

// Fail closed if this deployment never set a real CRON_SECRET — see
// cron/daily.php's identical guard for the full explanation.
if (CRON_SECRET === 'change-this-to-a-random-string-in-production') {
    http_response_code(403);
    die('CRON_SECRET has not been configured for this environment. See config/config.local.example.php.');
}

// Verify cron secret with timing-safe comparison
$secret = $_GET['secret'] ?? '';
if (!hash_equals(CRON_SECRET, $secret)) {
    http_response_code(403);
    die('Unauthorized');
}

// Set execution time limit
set_time_limit(120);

try {
    $dsn = "mysql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME . ";charset=utf8mb4";
    $pdo = new PDO($dsn, DB_USER, DB_PASS, [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4",
    ]);
} catch (PDOException $e) {
    error_log("Hourly cron DB connection failed: " . $e->getMessage());
    http_response_code(500);
    die('Internal server error');
}

$log = [];
$startTime = microtime(true);
$now = date('Y-m-d H:i:s');

// ── 1. Verification Sweep ───────────────────────────────
// Users past their verification deadline get moved to grace_expired
try {
    // First, check if there are users to update (avoid unnecessary write locks)
    $stmt = $pdo->prepare(
        "SELECT COUNT(*) FROM user_profiles 
         WHERE verification_state = 'pending' 
         AND verification_required_at <= NOW()
         AND verification_required_at IS NOT NULL"
    );
    $stmt->execute();
    $pendingCount = $stmt->fetchColumn();

    if ($pendingCount > 0) {
        $stmt = $pdo->prepare(
            "UPDATE user_profiles 
             SET verification_state = 'grace_expired', 
                 updated_at = NOW() 
             WHERE verification_state = 'pending' 
             AND verification_required_at <= NOW()
             AND verification_required_at IS NOT NULL"
        );
        $stmt->execute();
        $updatedCount = $stmt->rowCount();
        
        if ($updatedCount > 0) {
            $log[] = "Set {$updatedCount} users to grace_expired (verification deadline passed)";
            
            // Optional: Log the affected user IDs for audit trail
            $stmt = $pdo->prepare(
                "SELECT user_id FROM user_profiles 
                 WHERE verification_state = 'grace_expired' 
                 AND updated_at >= DATE_SUB(NOW(), INTERVAL 1 MINUTE)"
            );
            $stmt->execute();
            $affectedUsers = $stmt->fetchAll(PDO::FETCH_COLUMN);
            error_log("Users moved to grace_expired: " . implode(', ', $affectedUsers));
        }
    }
} catch (PDOException $e) {
    error_log("Verification sweep failed: " . $e->getMessage());
    $log[] = "ERROR: Verification sweep failed";
}

// ── 2. Session Cleanup ──────────────────────────────────
// Remove expired PHP sessions from database if using database sessions
try {
    $stmt = $pdo->prepare(
        "DELETE FROM user_sessions 
         WHERE expires_at < NOW() 
         AND expires_at IS NOT NULL"
    );
    $stmt->execute();
    $count = $stmt->rowCount();
    if ($count > 0) {
        $log[] = "Cleaned up {$count} expired sessions";
    }
} catch (PDOException $e) {
    // Table might not exist yet, don't log as error
    if ($e->getCode() !== '42S02') { // Base table not found
        error_log("Session cleanup failed: " . $e->getMessage());
        $log[] = "ERROR: Session cleanup failed";
    }
}

// ── 3. Expired Token Cleanup ────────────────────────────
// Clean up expired password reset and verification tokens
try {
    $stmt = $pdo->prepare(
        "DELETE FROM auth_tokens 
         WHERE expires_at < NOW() 
         AND expires_at IS NOT NULL"
    );
    $stmt->execute();
    $count = $stmt->rowCount();
    if ($count > 0) {
        $log[] = "Cleaned up {$count} expired auth tokens";
    }
} catch (PDOException $e) {
    // Table might not exist yet
    if ($e->getCode() !== '42S02') {
        error_log("Token cleanup failed: " . $e->getMessage());
        $log[] = "ERROR: Token cleanup failed";
    }
}

// ── 4. Rate Limit Cleanup ───────────────────────────────
// Clean up old rate limit entries
try {
    $stmt = $pdo->prepare(
        "DELETE FROM rate_limits 
         WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 HOUR)"
    );
    $stmt->execute();
    $count = $stmt->rowCount();
    if ($count > 0) {
        $log[] = "Cleaned up {$count} expired rate limits";
    }
} catch (PDOException $e) {
    if ($e->getCode() !== '42S02') {
        error_log("Rate limit cleanup failed: " . $e->getMessage());
    }
}

// ── 5. Temporary File Cleanup ───────────────────────────
// Clean up old temporary files (exports, uploads, etc.)
try {
    $tempDirs = [
        sys_get_temp_dir() . '/traderalign_exports',
        sys_get_temp_dir() . '/traderalign_uploads',
    ];
    
    $deletedFiles = 0;
    foreach ($tempDirs as $dir) {
        if (is_dir($dir)) {
            $files = glob($dir . '/*');
            foreach ($files as $file) {
                if (is_file($file) && filemtime($file) < time() - 3600) { // 1 hour
                    if (unlink($file)) {
                        $deletedFiles++;
                    }
                }
            }
        }
    }
    
    if ($deletedFiles > 0) {
        $log[] = "Cleaned up {$deletedFiles} temporary files";
    }
} catch (\Throwable $e) {
    error_log("Temp file cleanup failed: " . $e->getMessage());
}

// ── 6. Log Execution (Optional) ─────────────────────────
try {
    $executionTime = round(microtime(true) - $startTime, 2);
    $logEntry = implode('; ', $log);
    
    $stmt = $pdo->prepare(
        "INSERT INTO cron_logs (job_name, execution_time, actions_taken, status, created_at) 
         VALUES (:job, :exec_time, :actions, :status, NOW())"
    );
    $stmt->execute([
        ':job' => 'hourly_maintenance',
        ':exec_time' => $executionTime,
        ':actions' => $logEntry ?: 'No actions taken',
        ':status' => empty(array_filter($log, fn($l) => str_starts_with($l, 'ERROR'))) ? 'success' : 'partial_failure'
    ]);
} catch (PDOException $e) {
    // Silently fail - logs table might not exist yet
}

// ── Output ──────────────────────────────────────────────
$executionTime = round(microtime(true) - $startTime, 2);

echo "Hourly Cron — " . $now . "\n";
echo str_repeat('-', 50) . "\n";
if (empty($log)) {
    echo "No actions taken.\n";
} else {
    echo implode("\n", $log) . "\n";
}
echo str_repeat('-', 50) . "\n";
echo "Execution time: {$executionTime}s\n";
echo "Done.\n";