<?php
declare(strict_types=1);
ob_start();

header('Content-Type: application/json');
header('Cache-Control: no-store');

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

$action = $_GET['action'] ?? '';

try {
    switch ($action) {

        // ── process: scrape a batch of URLs then return progress ──────────────
        case 'process':
            $token = trim($_GET['token'] ?? '');
            if (!$token) throw new InvalidArgumentException('Missing token');

            $job = Db::getJobByToken($token);
            if (!$job) throw new RuntimeException('Job not found');

            $jobId     = (int)$job['id'];
            $batchSize = 5;

            // Already finished — just return current state
            if (!in_array($job['status'], ['pending', 'running'])) {
                ob_clean();
                echo json_encode([
                    'status'         => $job['status'],
                    'total_rows'     => (int)$job['total_rows'],
                    'processed_rows' => (int)$job['processed_rows'],
                    'success_rows'   => (int)$job['success_rows'],
                    'failed_rows'    => (int)$job['failed_rows'],
                    'token'          => $job['token'],
                    'filename'       => $job['filename'],
                ]);
                break;
            }

            // First call — start the job
            if ($job['status'] === 'pending') {
                Db::updateJobStatus($jobId, 'running');
                Db::addLog($jobId, 'info', 'Job started');
            }

            // Get next batch of pending URLs
            $stmt = Db::get()->prepare(
                "SELECT * FROM results WHERE job_id = ? AND status = 'pending' ORDER BY row_number ASC LIMIT ?"
            );
            $stmt->execute([$jobId, $batchSize]);
            $batch = $stmt->fetchAll();

            if (empty($batch)) {
                // All URLs done — generate export file
                require_once __DIR__ . '/includes/logger.php';
                $logger     = new Logger($jobId);
                $allResults = Db::getResultsForJob($jobId);

                if (version_compare(PHP_VERSION, '8.1.0', '>=')) {
                    // Full XLSX export
                    ob_start();
                    require_once __DIR__ . '/includes/excel_writer.php';
                    $filename = 'results_' . date('Ymd_His') . '_job' . $jobId . '.xlsx';
                    $outPath  = EXPORTS_DIR . '/' . $filename;
                    ExcelWriter::write($allResults, $outPath);
                    ob_clean();
                } else {
                    // CSV fallback for PHP < 8.1
                    $filename = 'results_' . date('Ymd_His') . '_job' . $jobId . '.csv';
                    $outPath  = EXPORTS_DIR . '/' . $filename;
                    $fp = fopen($outPath, 'w');
                    fputs($fp, "\xEF\xBB\xBF"); // UTF-8 BOM for Excel
                    fputcsv($fp, ['#', 'Product URL', 'Extracted Image URL', 'Status', 'Error', 'Fallback Level']);
                    foreach ($allResults as $row) {
                        fputcsv($fp, [
                            $row['row_number'],
                            $row['product_url'],
                            $row['image_url'] ?? '',
                            ucfirst($row['status']),
                            $row['error_message'] ?? '',
                            $row['fallback_level'] ?? '',
                        ]);
                    }
                    fclose($fp);
                }

                Db::get()->prepare("UPDATE jobs SET filename = ? WHERE id = ?")->execute([$filename, $jobId]);
                Db::updateJobStatus($jobId, 'done');
                $logger->info('Job completed — export: ' . $filename);

                $done = Db::getJob($jobId);
                ob_clean();
                echo json_encode([
                    'status'         => 'done',
                    'total_rows'     => (int)$done['total_rows'],
                    'processed_rows' => (int)$done['processed_rows'],
                    'success_rows'   => (int)$done['success_rows'],
                    'failed_rows'    => (int)$done['failed_rows'],
                    'token'          => $done['token'],
                    'filename'       => $filename,
                ]);
                break;
            }

            // Process the batch
            require_once __DIR__ . '/includes/scraper.php';
            require_once __DIR__ . '/includes/logger.php';

            $scraper = new Scraper(
                min((int)$job['timeout_s'], 15),
                $job['user_agent'] ?: DEFAULT_USER_AGENT,
                1,
                0
            );
            $logger = new Logger($jobId);

            foreach ($batch as $row) {
                $currentJob = Db::getJob($jobId);
                if ($currentJob && $currentJob['status'] === 'stopped') break;

                $logger->info("Row {$row['row_number']}: {$row['product_url']}");
                $extracted = $scraper->extract($row['product_url']);

                if (!empty($extracted['image_url'])) {
                    Db::updateResult($jobId, (int)$row['row_number'], $extracted['image_url'], 'success', null, $extracted['fallback_level']);
                    $logger->info("✓ Row {$row['row_number']} → " . $extracted['image_url']);
                } else {
                    $err = $extracted['error'] ?? 'No image found';
                    Db::updateResult($jobId, (int)$row['row_number'], '', 'failed', $err, null);
                    $logger->info("✗ Row {$row['row_number']}: $err");
                }
            }

            // Recount progress
            $counts = Db::get()->prepare("
                SELECT
                    SUM(CASE WHEN status != 'pending' THEN 1 ELSE 0 END) AS processed,
                    SUM(CASE WHEN status = 'success'  THEN 1 ELSE 0 END) AS success,
                    SUM(CASE WHEN status = 'failed'   THEN 1 ELSE 0 END) AS failed
                FROM results WHERE job_id = ?
            ");
            $counts->execute([$jobId]);
            $c = $counts->fetch();
            Db::updateJobProgress($jobId, (int)$c['processed'], (int)$c['success'], (int)$c['failed']);

            $refreshed = Db::getJob($jobId);
            ob_clean();
            echo json_encode([
                'status'         => $refreshed['status'],
                'total_rows'     => (int)$refreshed['total_rows'],
                'processed_rows' => (int)$c['processed'],
                'success_rows'   => (int)$c['success'],
                'failed_rows'    => (int)$c['failed'],
                'token'          => $refreshed['token'],
                'filename'       => $refreshed['filename'],
            ]);
            break;

        // ── progress: read-only status ────────────────────────────────────────
        case 'progress':
            $token = trim($_GET['token'] ?? '');
            if (!$token) throw new InvalidArgumentException('Missing token');

            $job = Db::getJobByToken($token);
            if (!$job) throw new RuntimeException('Job not found');

            ob_clean();
            echo json_encode([
                'status'         => $job['status'],
                'total_rows'     => (int)$job['total_rows'],
                'processed_rows' => (int)$job['processed_rows'],
                'success_rows'   => (int)$job['success_rows'],
                'failed_rows'    => (int)$job['failed_rows'],
                'filename'       => $job['filename'],
                'token'          => $job['token'],
            ]);
            break;

        // ── logs ──────────────────────────────────────────────────────────────
        case 'logs':
            $token = trim($_GET['token'] ?? '');
            if (!$token) throw new InvalidArgumentException('Missing token');

            $job = Db::getJobByToken($token);
            if (!$job) throw new RuntimeException('Job not found');

            $logs = Db::getLogsForJob((int)$job['id'], 100);
            ob_clean();
            echo json_encode(['logs' => $logs]);
            break;

        // ── stop ──────────────────────────────────────────────────────────────
        case 'stop':
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') throw new RuntimeException('POST required');
            $data  = json_decode(file_get_contents('php://input'), true) ?? [];
            $token = trim($data['token'] ?? '');
            if (!$token) throw new InvalidArgumentException('Missing token');

            $job = Db::getJobByToken($token);
            if (!$job) throw new RuntimeException('Job not found');

            if (in_array($job['status'], ['running', 'pending'])) {
                Db::updateJobStatus((int)$job['id'], 'stopped');
            }
            ob_clean();
            echo json_encode(['success' => true, 'status' => 'stopped']);
            break;

        default:
            throw new InvalidArgumentException("Unknown action: $action");
    }

} catch (Throwable $e) {
    ob_clean();
    http_response_code(400);
    echo json_encode(['error' => $e->getMessage()]);
}
