<?php
/**
 * AuctionForge Sync Safety Net v3
 *
 * Runs hourly via external cron. Handles:
 * 1. Reset stuck jobs (processing > 10 min)
 * 2. Spawn worker if pending jobs exist and no active worker
 * 3. Health check: if no webhook received in 24h, enqueue full sync
 * 4. Purge old queue/log data
 */

use auctionforge\inc\sync\SyncQueue;
use auctionforge\inc\sync\SyncLogger;
use auctionforge\inc\model\Auction;

if (!empty($_SERVER['DOCUMENT_ROOT'])) {
    die("Access denied — CLI only\n");
}

define('CRON_TASKS_BASE_PATH', __DIR__);
define('WP_USE_THEMES', false);
require(CRON_TASKS_BASE_PATH . '/../../../../wp-load.php');

if (!is_plugin_active(basename(dirname(__DIR__)) . '/' . basename(dirname(__DIR__)) . '.php')) {
    die("Plugin AuctionForge disabled\n");
}

SyncLogger::log('safety', 'Safety cron started', 'info');

// 1. Reset stuck jobs
$resetCount = SyncQueue::resetStuckJobs();
if ($resetCount > 0) {
    SyncLogger::log('safety', "Reset {$resetCount} stuck job(s)", 'warning');
}

// 2. Check queue status and spawn worker if needed
$counts = SyncQueue::getStatusCounts();
if ($counts['pending'] > 0 && !SyncQueue::hasActiveWorker()) {
    SyncLogger::log('safety', "Found {$counts['pending']} pending job(s), spawning worker", 'info');
    SyncQueue::spawnWorker();
}

// 3. Health check: if no successful webhook-sourced job completed in 24h, enqueue full sync
$healthCheckSeconds = defined('AUCTIONFORGE_SYNC_HEALTH_CHECK') ? AUCTIONFORGE_SYNC_HEALTH_CHECK : 86400;
$db = \auctionforge\inc\InstanceDB::getInstanceDB();
$table = SyncQueue::getTableName();
$cutoff = gmdate('Y-m-d H:i:s', time() - $healthCheckSeconds);

$recentWebhookJob = $db->get_var($db->prepare(
    "SELECT COUNT(*) FROM `{$table}` WHERE source = 'webhook' AND status = 'completed' AND completed_at > %s",
    $cutoff
));

if ((int) $recentWebhookJob === 0) {
    // No webhook jobs completed in the health check window — enqueue full sync
    $auctions = Auction::getActiveAuction();
    $auctions = Auction::getTermFields($auctions, 'auction_id');
    $enqueued = 0;

    foreach ($auctions as $auctionId) {
        $jobId = SyncQueue::enqueue(
            SyncQueue::TYPE_FULL_SYNC,
            $auctionId,
            null,
            5, // Low priority — safety fallback
            'safety'
        );
        if ($jobId) $enqueued++;
    }

    if ($enqueued > 0) {
        SyncLogger::log('safety', "Health check: no webhooks in {$healthCheckSeconds}s, enqueued full sync for {$enqueued} auction(s)", 'warning');
        SyncQueue::spawnWorker();
    } else {
        SyncLogger::log('safety', "Health check: no webhooks in {$healthCheckSeconds}s, but all auctions already queued", 'info');
    }
} else {
    SyncLogger::log('safety', "Health check OK: {$recentWebhookJob} webhook job(s) completed in last " . round($healthCheckSeconds / 3600) . "h", 'info');
}

// 4. Fix orphaned lot posts (created but missing taxonomy assignment due to killed workers)
$metaTable = \auctionforge\inc\model\LotMetaData::getTableName();
$taxName = \auctionforge\inc\controllers\BidspiritLots::TAX_AUCTION_TYPE;
$activeAuctions = Auction::getActiveAuction();
$activeAuctionIds = Auction::getTermFields($activeAuctions, 'auction_id');
$orphansFixed = 0;

foreach ($activeAuctionIds as $auctionBsId) {
    $term = Auction::findAuctionById($auctionBsId);
    if (!$term instanceof \WP_Term) continue;
    $termId = $term->term_id;

    // Find posts that have meta linking them to this auction but no taxonomy assignment
    $orphans = $db->get_col($db->prepare(
        "SELECT DISTINCT m.postId FROM {$metaTable} m
         INNER JOIN {$db->prefix}posts p ON p.ID = m.postId
         LEFT JOIN {$db->prefix}term_relationships tr ON tr.object_id = m.postId AND tr.term_taxonomy_id = %d
         WHERE m.termId = %d AND p.post_type = 'lot' AND tr.object_id IS NULL AND m.postId > 0",
        $termId, $termId
    ));

    if (!empty($orphans)) {
        foreach ($orphans as $postId) {
            wp_set_object_terms((int) $postId, [$termId], $taxName, false);
            $orphansFixed++;
        }
    }
}

if ($orphansFixed > 0) {
    SyncLogger::log('safety', "Fixed {$orphansFixed} orphaned lot post(s) missing taxonomy assignment", 'warning');
}

// 5. Purge old data
$queueRetention = defined('AUCTIONFORGE_SYNC_QUEUE_RETENTION') ? AUCTIONFORGE_SYNC_QUEUE_RETENTION : 30;
$logRetention = defined('AUCTIONFORGE_SYNC_LOG_RETENTION') ? AUCTIONFORGE_SYNC_LOG_RETENTION : 7;

$queuePurged = SyncQueue::purge($queueRetention);
$logPurged = SyncLogger::purge($logRetention);

if ($queuePurged > 0 || $logPurged > 0) {
    SyncLogger::log('safety', "Purged {$queuePurged} old queue rows, {$logPurged} old log rows", 'info');
}

// Summary
SyncLogger::log('safety', sprintf(
    'Safety cron done: %d reset, %d pending, %d processing, %d completed, %d failed',
    $resetCount,
    $counts['pending'],
    $counts['processing'],
    $counts['completed'],
    $counts['failed']
), 'info');
