#!/usr/bin/env php
<?php
/**
 * ============================================
 * FLOWBOT DCI v3.0 - BACKGROUND WORKER
 * ============================================
 * Processes URL batches in background via cron
 *
 * Usage:
 *   php worker.php              # Process one batch for each active process
 *   php worker.php --daemon     # Run continuously (every 5 seconds)
 *   php worker.php --once       # Process one batch and exit
 *
 * Cron example (every minute):
 *   * * * * * php /path/to/worker.php >> /var/log/flowbot-worker.log 2>&1
 */

declare(strict_types=1);

// Ensure CLI only
if (php_sapi_name() !== 'cli') {
    die("This script must be run from command line\n");
}

// Change to project root
chdir(dirname(__DIR__));

// Load configuration
$configPath = __DIR__ . '/../config/config.php';
if (!file_exists($configPath)) {
    // Try alternative path
    $configPath = __DIR__ . '/../src/config.php';
}

if (file_exists($configPath)) {
    $config = require $configPath;
} else {
    // Load .env file if exists
    $envFile = dirname(__DIR__) . '/.env';
    if (file_exists($envFile)) {
        $envContent = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        foreach ($envContent as $line) {
            if (strpos(trim($line), '#') === 0) continue;
            if (strpos($line, '=') !== false) {
                list($key, $value) = explode('=', $line, 2);
                putenv(trim($key) . '=' . trim($value));
            }
        }
    }

    // Default config - uses environment variables from .env file
    // See .env.example for required variables
    $config = [
        'paths' => [
            'temp' => getenv('TEMP_DIR') ?: sys_get_temp_dir() . '/flowbot',
            'views' => dirname(__DIR__) . '/views',
        ],
        'database' => [
            'host' => getenv('DB_HOST') ?: 'localhost',
            'name' => getenv('DB_NAME') ?: '',
            'user' => getenv('DB_USER') ?: '',
            'password' => getenv('DB_PASSWORD') ?: '',
            'charset' => getenv('DB_CHARSET') ?: 'utf8mb4',
        ],
        'processing' => [
            'batch_size' => (int)(getenv('MAX_LINKS_PER_BATCH') ?: 10),
            'rate_limit' => 2,
        ]
    ];

    // Validate required configuration
    if (empty($config['database']['name']) || empty($config['database']['user'])) {
        fwrite(STDERR, "[ERROR] Database configuration missing. Create .env file from .env.example\n");
        fwrite(STDERR, "        Required: DB_HOST, DB_NAME, DB_USER, DB_PASSWORD\n");
        exit(1);
    }
}

// Autoload classes
spl_autoload_register(function ($class) {
    $prefix = 'FlowbotDCI\\';
    $baseDir = __DIR__ . '/../src/';

    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        return;
    }

    $relativeClass = substr($class, $len);
    $file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';

    if (file_exists($file)) {
        require $file;
    }
});

// Load required classes manually if autoload fails
$requiredFiles = [
    __DIR__ . '/../src/Core/Database.php',
    __DIR__ . '/../src/Services/ProgressTracker.php',
    __DIR__ . '/../src/Services/UrlProcessor.php',
    __DIR__ . '/../src/Services/DashboardService.php',
    __DIR__ . '/../src/Services/ProcessManager.php',
    __DIR__ . '/../src/Services/UrlPreProcessor.php',
    __DIR__ . '/../src/Services/WebScraper.php',
    __DIR__ . '/../src/Models/PinFeedModel.php',
];

foreach ($requiredFiles as $file) {
    if (file_exists($file)) {
        require_once $file;
    }
}

use FlowbotDCI\Core\Database;
use FlowbotDCI\Services\ProgressTracker;
use FlowbotDCI\Services\UrlProcessor;
use FlowbotDCI\Services\DashboardService;
use FlowbotDCI\Services\ProcessManager;

/**
 * Background Worker Class
 */
class FlowbotWorker
{
    private $config;
    private $db;
    private $dashboardService;
    private $processManager;
    private $verbose = false;
    private $maxRuntime = 55; // Max runtime in seconds (less than cron interval)
    private $startTime;

    public function __construct(array $config, bool $verbose = false)
    {
        $this->config = $config;
        $this->verbose = $verbose;
        $this->startTime = time();

        // Initialize database
        $dbConfig = $config['database'] ?? $config;
        $this->db = new Database($dbConfig);

        $tempDir = $config['paths']['temp'] ?? sys_get_temp_dir() . '/flowbot';
        $this->dashboardService = new DashboardService($this->db, $tempDir);
        $this->processManager = new ProcessManager($this->db, $tempDir, $this->dashboardService);
    }

    /**
     * Log message
     */
    private function log(string $message, string $level = 'INFO'): void
    {
        $timestamp = date('Y-m-d H:i:s');
        $output = "[{$timestamp}] [{$level}] {$message}\n";

        if ($this->verbose || $level === 'ERROR') {
            echo $output;
        }

        // Also log to file
        $logDir = $this->config['paths']['temp'] ?? sys_get_temp_dir();
        $logFile = "{$logDir}/worker.log";
        @file_put_contents($logFile, $output, FILE_APPEND);
    }

    /**
     * Check if we should continue running
     */
    private function shouldContinue(): bool
    {
        return (time() - $this->startTime) < $this->maxRuntime;
    }

    /**
     * Get active processes (pending or running)
     */
    private function getActiveProcesses(): array
    {
        $pdo = $this->db->getConnection();

        try {
            $stmt = $pdo->prepare("
                SELECT id, status, mode, total_urls, processed_urls
                FROM process_history
                WHERE status IN ('pending', 'running')
                ORDER BY created_at ASC
                LIMIT 10
            ");
            $stmt->execute();
            return $stmt->fetchAll(\PDO::FETCH_ASSOC);
        } catch (\PDOException $e) {
            $this->log("Database error: " . $e->getMessage(), 'ERROR');
            return [];
        }
    }

    /**
     * Process a single batch for a process
     */
    private function processBatch(string $processId): array
    {
        $this->log("Processing batch for: {$processId}");

        try {
            // Check process state
            $state = $this->processManager->getState($processId);

            if ($state['status'] === 'paused') {
                $this->log("Process {$processId} is paused, skipping");
                return ['success' => true, 'status' => 'paused', 'complete' => false];
            }

            if ($state['status'] === 'cancelled') {
                $this->log("Process {$processId} is cancelled, cleaning up");
                return ['success' => true, 'status' => 'cancelled', 'complete' => true];
            }

            // Initialize tracker and processor
            $tracker = new ProgressTracker($this->config, $processId);

            if (!$tracker->hasProgress()) {
                $this->log("No progress data for {$processId}, skipping", 'WARN');
                // Mark as failed if no progress data
                $this->dashboardService->updateProcess($processId, ['status' => 'failed']);
                return ['success' => false, 'error' => 'No progress data'];
            }

            $processor = new UrlProcessor($this->config, $this->db, $tracker);

            // Update status to running if pending
            $this->dashboardService->updateProcess($processId, ['status' => 'running']);

            // Process one batch
            $result = $processor->processBatch();

            // v4.4: Check if cancelled mid-batch
            $wasCancelled = $result['cancelled'] ?? false;

            // Update database with progress
            $data = $result['data'] ?? [];

            // v4.4: Determine correct status - cancelled takes priority over completed
            $dbStatus = 'running';
            if ($wasCancelled) {
                $dbStatus = 'cancelled';
            } elseif ($result['complete']) {
                $dbStatus = 'completed';
            }

            $this->dashboardService->updateProcess($processId, [
                'processed_urls' => $data['processed_links'] ?? 0,
                'success_count' => $data['imported_links'] ?? 0,
                'failed_count' => $data['error_links'] ?? 0,
                'current_phase' => $data['phase_index'] ?? 0,
                'status' => $dbStatus,
            ]);

            // v4.4: Handle cancelled status with priority
            if ($wasCancelled) {
                $this->log("Process {$processId} was cancelled mid-batch");
                $tracker->cleanup();
                return ['success' => true, 'status' => 'cancelled', 'complete' => true];
            }

            if ($result['complete']) {
                $this->log("Process {$processId} completed!");
                $tracker->cleanup();
            } else {
                $processed = $data['processed_links'] ?? 0;
                $total = $data['total_links'] ?? 0;
                $this->log("Process {$processId}: {$processed}/{$total} URLs processed");
            }

            return [
                'success' => true,
                'status' => $result['complete'] ? 'completed' : 'running',
                'complete' => $result['complete'] || $wasCancelled,
                'data' => $data
            ];

        } catch (\Exception $e) {
            $this->log("Error processing {$processId}: " . $e->getMessage(), 'ERROR');

            // Mark process as failed
            $this->dashboardService->updateProcess($processId, [
                'status' => 'failed',
                'error_message' => $e->getMessage()
            ]);

            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    /**
     * Run the worker (process all active batches once)
     */
    public function run(): int
    {
        $this->log("Worker started");

        $processes = $this->getActiveProcesses();

        if (empty($processes)) {
            $this->log("No active processes found");
            return 0;
        }

        $this->log("Found " . count($processes) . " active process(es)");

        $processed = 0;

        foreach ($processes as $process) {
            if (!$this->shouldContinue()) {
                $this->log("Max runtime reached, stopping");
                break;
            }

            $result = $this->processBatch($process['id']);

            if ($result['success']) {
                $processed++;
            }

            // Small delay between processes
            usleep(100000); // 100ms
        }

        $this->log("Worker finished. Processed {$processed} batch(es)");

        return $processed;
    }

    /**
     * Run in daemon mode (continuous)
     */
    public function runDaemon(int $interval = 5): void
    {
        $this->log("Worker starting in daemon mode (interval: {$interval}s)");
        $this->maxRuntime = PHP_INT_MAX; // No timeout in daemon mode

        while (true) {
            $this->run();
            sleep($interval);
        }
    }

    /**
     * Process a single batch and exit
     */
    public function runOnce(): int
    {
        $this->log("Worker running once");
        $this->maxRuntime = 30;

        $processes = $this->getActiveProcesses();

        if (empty($processes)) {
            $this->log("No active processes found");
            return 0;
        }

        // Process just one batch from the first active process
        $result = $this->processBatch($processes[0]['id']);

        return $result['success'] ? 1 : 0;
    }
}

// Parse arguments
$options = getopt('', ['daemon', 'once', 'verbose', 'help']);

if (isset($options['help'])) {
    echo "Flowbot DCI Background Worker\n";
    echo "Usage: php worker.php [options]\n\n";
    echo "Options:\n";
    echo "  --daemon    Run continuously (every 5 seconds)\n";
    echo "  --once      Process one batch and exit\n";
    echo "  --verbose   Show detailed output\n";
    echo "  --help      Show this help\n";
    exit(0);
}

$verbose = isset($options['verbose']);

// Create and run worker
$worker = new FlowbotWorker($config, $verbose);

if (isset($options['daemon'])) {
    $worker->runDaemon();
} elseif (isset($options['once'])) {
    exit($worker->runOnce() > 0 ? 0 : 1);
} else {
    // Default: process all active batches once
    $processed = $worker->run();
    exit($processed > 0 ? 0 : 1);
}
