<?php
/**
 * EONSR AEO Agent - Single Tab Enforcement System
 *
 * Implements a locking mechanism using WordPress Transients to prevent
 * users from running multiple generation jobs simultaneously.
 *
 * @package EONSR_AEO_Agent
 */

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Generate the lock key specific to the current logged-in user.
 *
 * @return string The transient key (e.g., 'eonsr_aeo_agent_active_job_123').
 */
function eonsr_aeo_agent_get_user_lock_key()
{
    $user_id = get_current_user_id();
    // Updated Transient Key Prefix
    return 'eonsr_aeo_agent_active_job_' . $user_id;
}

/**
 * Check if the current user has an active job running.
 *
 * @return array|false Returns job details array if locked, false otherwise.
 */
function eonsr_aeo_agent_user_has_active_job()
{
    $lock_key   = eonsr_aeo_agent_get_user_lock_key();
    $active_job = get_transient($lock_key);

    if ($active_job) {
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Lock debugging for concurrent job tracking
        error_log(sprintf(
            '[EONSR AEO Lock] 🔒 Active job found for user. Details: %s',
            json_encode($active_job)
        ));
        return $active_job;
    }

    return false;
}

/**
 * Apply a lock to the current user to prevent concurrent jobs.
 *
 * @param string $job_id       Unique Identifier for the job.
 * @param string $primary_term The primary keyword/topic being generated.
 * @return bool True if lock set successfully, false otherwise.
 */
function eonsr_aeo_agent_lock_user_job($job_id, $primary_term)
{
    $lock_key = eonsr_aeo_agent_get_user_lock_key();

    $lock_data = [
        'job_id'       => $job_id,
        'primary_term' => $primary_term,
        'started_at'   => current_time('mysql'),
        'timestamp'    => time()
    ];

    // Lock duration: 5 minutes (300 seconds)
    // Acts as a safety timeout in case the process crashes and doesn't unlock.
    $result = set_transient($lock_key, $lock_data, 300);

    if ($result) {
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Lock creation tracking
        error_log(sprintf(
            '[EONSR AEO Lock] ✅ Lock set. JobID: %s | Term: %s',
            $job_id,
            $primary_term
        ));
    } else {
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Lock failure alert
        error_log(sprintf(
            '[EONSR AEO Lock] ❌ Failed to set lock. JobID: %s',
            $job_id
        ));
    }

    return $result;
}

/**
 * Remove the active job lock for the user.
 *
 * @param string|null $job_id Optional. If provided, ensures we only unlock the specific job.
 * @return bool True if deleted successfully.
 */
function eonsr_aeo_agent_unlock_user_job($job_id = null)
{
    $lock_key = eonsr_aeo_agent_get_user_lock_key();

    // Verification: If job_id is provided, check consistency before unlocking
    if ($job_id) {
        $active_job = get_transient($lock_key);
        if ($active_job && isset($active_job['job_id']) && $active_job['job_id'] !== $job_id) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Unlock rejection warning for security
            error_log(sprintf(
                '[EONSR AEO Lock] ⚠️ Unlock Rejected: ID mismatch. Active: %s, Requested: %s',
                $active_job['job_id'],
                $job_id
            ));
            return false;
        }
    }

    $result = delete_transient($lock_key);

    if ($result) {
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Unlock confirmation
        error_log(sprintf(
            '[EONSR AEO Lock] 🔓 Unlocked successfully. %s',
            $job_id ? "JobID: $job_id" : "(No specific ID)"
        ));
    }

    return $result;
}

/**
 * Forcefully unlock a specific user (Admin utility).
 *
 * @param int $user_id The user ID to unlock.
 * @return bool
 */
function eonsr_aeo_agent_force_unlock_user($user_id)
{
    if (!current_user_can('manage_options')) {
        return false;
    }

    $lock_key = 'eonsr_aeo_agent_active_job_' . $user_id;
    delete_transient($lock_key);

    // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Admin force unlock audit trail
    error_log(sprintf('[EONSR AEO Lock] 🛡️ Force unlocked user ID: %d', $user_id));
    return true;
}

/**
 * Calculate remaining time for the current lock.
 *
 * @return int|false Remaining seconds, or false if not locked.
 */
function eonsr_aeo_agent_get_lock_time_remaining()
{
    $active_job = eonsr_aeo_agent_user_has_active_job();

    if (!$active_job || !isset($active_job['timestamp'])) {
        return false;
    }

    $started_at = $active_job['timestamp'];
    $elapsed    = time() - $started_at;
    $timeout    = 300; // 5 minutes constraint

    $remaining  = $timeout - $elapsed;

    return max(0, $remaining);
}

/**
 * Garbage Collector: Clean up stale locks via Cron.
 * Handles cases where transient expiration might not trigger automatically.
 *
 * @return int Number of locks cleaned.
 */
function eonsr_aeo_agent_cleanup_stale_locks()
{
    global $wpdb;

    // Direct DB query to find relevant transients
    // Updated to match new transient key format
    // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Required for transient cleanup as WP doesn't provide native API for bulk transient search
    $transients = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT option_name, option_value 
            FROM {$wpdb->options} 
            WHERE option_name LIKE %s",
            '_transient_eonsr_aeo_agent_active_job_%'
        )
    );
    // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

    $cleaned = 0;

    foreach ($transients as $transient) {
        $lock_data = maybe_unserialize($transient->option_value);

        if (isset($lock_data['timestamp'])) {
            $age = time() - $lock_data['timestamp'];

            // Prune locks older than 300 seconds (5 minutes)
            if ($age > 300) {
                $transient_key = str_replace('_transient_', '', $transient->option_name);
                delete_transient($transient_key);
                
                $cleaned++;
                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Stale lock cleanup tracking
                error_log(sprintf(
                    '[EONSR AEO Lock] 🧹 Stale lock cleaned: %s (Age: %ds)',
                    $transient_key,
                    $age
                ));
            }
        }
    }

    if ($cleaned > 0) {
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Cleanup summary
        error_log(sprintf('[EONSR AEO Lock] 🏁 Cleanup finished. Removed %d locks.', $cleaned));
    }

    return $cleaned;
}

/* ==========================================================================
   CRON SCHEDULING
   ========================================================================== */

add_action('wp', function () {
    if (!wp_next_scheduled('eonsr_aeo_agent_cleanup_locks_cron')) {
        wp_schedule_event(time(), 'hourly', 'eonsr_aeo_agent_cleanup_locks_cron');
    }
});

add_action('eonsr_aeo_agent_cleanup_locks_cron', 'eonsr_aeo_agent_cleanup_stale_locks');

/* ==========================================================================
   AJAX HANDLERS
   ========================================================================== */

/**
 * AJAX: Check Active Job Status.
 * Used by frontend to determine if the UI should be locked/restored.
 */
add_action('wp_ajax_eonsr_aeo_agent_check_active_job', function () {
    if (!current_user_can('edit_posts')) {
        wp_send_json_error(['message' => 'Permission denied'], 403);
    }

    // Note: Nonce check name should match frontend (admin-script.js)
    check_ajax_referer('eonsr_aeo_agent_check_active_ajax', 'nonce');

    $active_job = eonsr_aeo_agent_user_has_active_job();

    if ($active_job) {
        wp_send_json_success([
            'has_active_job' => true,
            'job_id'         => $active_job['job_id'],
            'primary_term'   => $active_job['primary_term'],
            'started_at'     => $active_job['started_at'],
            'time_remaining' => eonsr_aeo_agent_get_lock_time_remaining()
        ]);
    } else {
        wp_send_json_success([
            'has_active_job' => false
        ]);
    }
});

/**
 * AJAX: Force Unlock.
 * Emergency endpoint to clear lock if UI gets stuck.
 */
add_action('wp_ajax_eonsr_aeo_agent_force_unlock', function () {
    if (!current_user_can('edit_posts')) {
        wp_send_json_error(['message' => 'Permission denied'], 403);
    }

    check_ajax_referer('eonsr_aeo_agent_force_unlock_ajax', 'nonce');

    $result = eonsr_aeo_agent_unlock_user_job();

    if ($result) {
        wp_send_json_success(['message' => 'Đã mở khóa thành công']);
    } else {
        wp_send_json_error(['message' => 'Không thể mở khóa']);
    }
});