<?php
/**
 * ===========================================
 * FLOWBOT DCI - MAIN APPLICATION CLASS v7.0
 * ===========================================
 * Professional routing with Dashboard + 3-Stage Workflow
 * + Unified Crawler System v7.0
 *
 * Routes:
 * - GET  /                  → Dashboard home
 * - GET  /new               → Stage 1: URL input form
 * - POST /new               → Process URLs (go to analyze)
 * - GET  /new/analyze       → Stage 2: Analysis preview
 * - POST /new/analyze       → Start processing (go to progress)
 * - GET  /process/{id}      → Stage 3: Progress view
 * - GET  /process/{id}/domains → Domain stats
 * - GET  /process/{id}/logs → Process logs
 * - GET  /process/{id}/export → Export options
 * - GET  /history           → Process history
 * - GET  /settings          → Settings page
 * - GET  /crawler           → Deep Crawler v6.0
 * - GET  /crawler/unified   → Unified Crawler v7.0
 * - GET  /api/v1/*          → API endpoints
 *
 * Unified Crawler API (v7.0):
 * - GET  /api/v1/crawler/unified/start      → Start SSE crawl stream
 * - GET  /api/v1/crawler/unified/search     → Start SSE search stream
 * - POST /api/v1/crawler/unified/batch      → Batch URL processing
 * - GET  /api/v1/crawler/unified/status/{id} → Get job status
 * - POST /api/v1/crawler/unified/pause/{id}  → Pause job
 * - POST /api/v1/crawler/unified/resume/{id} → Resume job
 * - POST /api/v1/crawler/unified/stop/{id}   → Stop job
 * - DELETE /api/v1/crawler/unified/{id}      → Delete job
 * - GET  /api/v1/crawler/unified/jobs        → List jobs
 * - GET  /api/v1/crawler/unified/progress/{id} → Get progress
 * - GET  /api/v1/crawler/unified/logs/{id}   → Get logs
 * - GET  /api/v1/crawler/unified/domains     → Get domain stats
 * - GET  /api/v1/crawler/unified/analytics   → Get analytics
 *
 * Analytics API (v7.0):
 * - GET  /api/v1/analytics/overview          → Dashboard overview
 * - GET  /api/v1/analytics/domains           → Domain statistics
 * - GET  /api/v1/analytics/performance       → Performance metrics
 * - GET  /api/v1/analytics/timeline          → Timeline data
 * - GET  /api/v1/analytics/errors            → Error analysis
 * - GET  /api/v1/analytics/trends            → Trend analysis
 */

declare(strict_types=1);

namespace FlowbotDCI\Core;

use FlowbotDCI\Services\UrlProcessor;
use FlowbotDCI\Services\ProgressTracker;
use FlowbotDCI\Services\DashboardService;
use FlowbotDCI\Services\ProcessManager;
use FlowbotDCI\Services\RedisService;
use FlowbotDCI\Api\v1\DashboardController;
use FlowbotDCI\Utils\UrlPreProcessor;

class Application
{
    private array $config;
    private ?Database $database = null;
    private ?UrlProcessor $processor = null;
    private ?ProgressTracker $tracker = null;
    private ?DashboardService $dashboardService = null;
    private ?ProcessManager $processManager = null;
    private ?RedisService $redis = null;
    private string $processId = '';

    // Rate limiting defaults (can be overridden in config)
    private int $rateLimitRequests;
    private int $rateLimitWindow;

    const VERSION = '7.0';

    public function __construct(array $config)
    {
        $this->config = $config;

        // Set timezone
        $timezone = $config['app']['timezone'] ?? 'America/Sao_Paulo';
        if (!empty($timezone)) {
            date_default_timezone_set($timezone);
        }

        // Error reporting
        $debug = $config['app']['debug'] ?? false;
        if ($debug) {
            error_reporting(E_ALL);
            ini_set('display_errors', '1');
        } else {
            error_reporting(0);
            ini_set('display_errors', '0');
        }

        set_time_limit(0);

        // Initialize database
        try {
            $this->database = new Database($config['database']);
        } catch (\Exception $e) {
            throw new \Exception("Database connection failed: " . $e->getMessage());
        }

        // Initialize v3.0 services
        $tempDir = $config['paths']['temp'] ?? sys_get_temp_dir() . '/flowbot';
        $this->dashboardService = new DashboardService($this->database, $tempDir);
        $this->processManager = new ProcessManager($this->database, $tempDir, $this->dashboardService);

        // Initialize Redis service (with automatic fallback to file-based)
        $redisConfig = $config['redis'] ?? [];
        $redisConfig['temp_dir'] = $tempDir;
        $this->redis = new RedisService($redisConfig);

        // Load rate limiting config
        $rateLimitConfig = $config['rate_limiting'] ?? [];
        $this->rateLimitRequests = $rateLimitConfig['requests_per_minute'] ?? 600;
        $this->rateLimitWindow = $rateLimitConfig['window_seconds'] ?? 60;

        $this->setSecurityHeaders();
    }

    /**
     * Set security headers
     */
    private function setSecurityHeaders(): void
    {
        header('X-Frame-Options: SAMEORIGIN');
        header('X-XSS-Protection: 1; mode=block');
        header('X-Content-Type-Options: nosniff');
        header('Referrer-Policy: strict-origin-when-cross-origin');
        header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self';");
    }

    /**
     * Sanitize input string
     */
    private function sanitizeInput(string $input): string
    {
        $input = str_replace("\0", '', $input);
        $input = trim($input);
        return htmlspecialchars($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    }

    /**
     * Sanitize URLs input
     */
    private function sanitizeUrlsInput(string $input): string
    {
        $input = str_replace("\0", '', $input);
        $input = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $input);
        return trim($input);
    }

    /**
     * Check rate limit - uses Redis if available, falls back to file-based
     */
    private function checkRateLimit(): bool
    {
        $ip = $this->getClientIp();

        // Use Redis service (automatically falls back to file-based if unavailable)
        return $this->redis->checkRateLimit(
            "ip:{$ip}",
            $this->rateLimitRequests,
            $this->rateLimitWindow
        );
    }

    /**
     * Get client IP
     */
    private function getClientIp(): string
    {
        $ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';

        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $ip = trim($ips[0]);
        }

        if (!filter_var($ip, FILTER_VALIDATE_IP)) {
            $ip = '127.0.0.1';
        }

        return $ip;
    }

    /**
     * Generate CSRF token
     */
    private function generateCsrfToken(): string
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        if (empty($_SESSION['csrf_token'])) {
            $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
        }

        return $_SESSION['csrf_token'];
    }

    /**
     * Validate CSRF token
     */
    private function validateCsrfToken(): bool
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        $token = $_POST['csrf_token'] ?? '';

        if (empty($_SESSION['csrf_token']) || empty($token)) {
            return false;
        }

        return hash_equals($_SESSION['csrf_token'], $token);
    }

    /**
     * Send rate limit response
     */
    private function sendRateLimitResponse(): void
    {
        $ip = $this->getClientIp();
        $remaining = $this->redis->getRateLimitRemaining(
            "ip:{$ip}",
            $this->rateLimitRequests,
            $this->rateLimitWindow
        );

        http_response_code(429);
        header('Content-Type: application/json');
        header('Retry-After: ' . $this->rateLimitWindow);
        header('X-RateLimit-Limit: ' . $this->rateLimitRequests);
        header('X-RateLimit-Remaining: ' . $remaining);
        header('X-RateLimit-Reset: ' . (time() + $this->rateLimitWindow));
        echo json_encode([
            'success' => false,
            'error' => 'Rate limit exceeded. Please try again later.',
            'retry_after' => $this->rateLimitWindow,
            'limit' => $this->rateLimitRequests,
            'remaining' => $remaining,
        ]);
        exit;
    }

    /**
     * Get or generate process ID
     */
    public function getProcessId(): string
    {
        if (!empty($_REQUEST['process_id'])) {
            $id = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', $_REQUEST['process_id']);
            if (!empty($id)) {
                return $id;
            }
        }
        return uniqid('batch_', true);
    }

    /**
     * Initialize for specific process
     */
    public function initialize(): self
    {
        // Only get new processId if not already set (e.g., from URL path)
        if (empty($this->processId)) {
            $this->processId = $this->getProcessId();
        }

        // v3.0: Pass Redis service to ProgressTracker for caching
        $this->tracker = new ProgressTracker($this->config, $this->processId, $this->redis);
        $this->processor = new UrlProcessor($this->config, $this->database, $this->tracker);

        $this->cleanOldFolders();

        return $this;
    }

    /**
     * Parse request path
     */
    private function getRequestPath(): string
    {
        $requestUri = $_SERVER['REQUEST_URI'] ?? '/';
        $scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
        $basePath = dirname($scriptName);

        // Remove base path and query string
        $path = parse_url($requestUri, PHP_URL_PATH);
        if ($basePath !== '/' && strpos($path, $basePath) === 0) {
            $path = substr($path, strlen($basePath));
        }

        return '/' . trim($path, '/');
    }

    /**
     * Main request handler with v3.0 routing
     */
    public function handleRequest(): void
    {
        // Check rate limit
        if (!$this->checkRateLimit()) {
            $this->sendRateLimitResponse();
            return;
        }

        $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
        $path = $this->getRequestPath();

        // API routing (takes priority)
        if (preg_match('#^/api/v1/(.+)$#', $path, $matches)) {
            $this->handleApiV1($matches[1], $method);
            return;
        }

        // Legacy API route
        if (isset($_GET['page']) && $_GET['page'] === 'api') {
            $this->handleLegacyApi();
            return;
        }

        // Page routing
        switch (true) {
            // Dashboard home
            case $path === '/' && !isset($_GET['process_id']):
                $this->showDashboard();
                break;

            // New process - Stage 1
            case $path === '/new' && $method === 'GET':
                $this->showForm();
                break;

            // Submit URLs - Go to Stage 2
            case $path === '/new' && $method === 'POST':
                $this->handleUrlSubmit();
                break;

            // Analysis preview - Stage 2
            case $path === '/new/analyze' && $method === 'GET':
                $this->showAnalyze();
                break;

            // Start processing - Go to Stage 3
            case $path === '/new/analyze' && $method === 'POST':
                $this->startProcessing();
                break;

            // Process progress - Stage 3
            case preg_match('#^/process/([a-zA-Z0-9_\-\.]+)$#', $path, $m):
                $this->processId = $m[1];
                $this->initialize();
                $page = $_GET['page'] ?? 'progress';
                $this->handleProcessPage($page);
                break;

            // History page
            case $path === '/history':
                $this->showHistory();
                break;

            // Settings page
            case $path === '/settings':
                $this->showSettings();
                break;

            // Deep Crawler page (v6.0)
            case $path === '/crawler':
                $this->showCrawler();
                break;

            // Unified Crawler page (v7.0)
            case $path === '/crawler/unified':
                $this->showUnifiedCrawler();
                break;

            // Legacy route: process_id in query string
            case isset($_GET['process_id']):
                $this->initialize();
                $page = $_GET['page'] ?? 'progress';
                $this->handleProcessPage($page);
                break;

            // Legacy: POST with URLs
            case $method === 'POST' && isset($_POST['urls']) && !empty(trim($_POST['urls'])):
                $this->handleUrlSubmitLegacy();
                break;

            // Default: Show form or dashboard
            default:
                $this->showDashboard();
        }
    }

    /**
     * Handle v3.0 API requests
     */
    private function handleApiV1(string $endpoint, string $method): void
    {
        header('Content-Type: application/json');
        header('Cache-Control: no-cache, must-revalidate');

        $controller = new DashboardController($this->dashboardService, $this->processManager);
        $controller->setUrlProcessor(new UrlPreProcessor());

        // Parse endpoint and params
        $parts = explode('/', trim($endpoint, '/'));
        $action = $parts[0] ?? '';
        $id = $parts[1] ?? '';
        $subAction = $parts[2] ?? '';

        // Special handling for /api/v1/process/{id}/run - AJAX batch processing
        if ($action === 'process' && $id && $subAction === 'run' && $method === 'POST') {
            $this->processId = $id;
            $this->initialize();
            $result = $this->runBatchApi();
            echo json_encode($result);
            exit;
        }

        // v3.1: Special handling for /api/v1/process/{id}/initialize - Deferred initialization
        if ($action === 'process' && $id && $subAction === 'initialize' && $method === 'POST') {
            $result = $this->initializePendingProcess($id);
            http_response_code($result['http_code'] ?? 200);
            echo json_encode($result);
            exit;
        }

        // v7.0: Analytics API endpoints
        if ($action === 'analytics') {
            $this->handleAnalyticsApi($id, $method, $params);
            exit;
        }

        // v6.0: Deep Crawler API endpoints (SSE-based)
        if ($action === 'crawler') {
            // v7.0: Unified Crawler API (handles /api/v1/crawler/unified/*)
            if ($id === 'unified') {
                $this->handleUnifiedCrawlerApi($subAction, $method, $parts);
                exit;
            }
            $this->handleCrawlerApi($id, $subAction, $method);
            exit;
        }

        // Build action string
        if ($action === 'process' && $id) {
            $actionStr = $subAction ? "process/{$subAction}" : 'process/live';
            $params = ['id' => $id] + $_GET + ($method === 'POST' ? $this->getJsonInput() : []);
        } elseif ($action === 'dashboard') {
            $actionStr = "dashboard/{$id}";
            $params = $_GET;
        } else {
            $actionStr = $action;
            $params = $_GET + ($method === 'POST' ? $this->getJsonInput() : []);
        }

        $result = $controller->handle($actionStr, $params);

        http_response_code($result['http_code'] ?? 200);
        echo json_encode($result);
        exit;
    }

    /**
     * Handle Deep Crawler API endpoints (v6.0)
     *
     * Endpoints:
     * - GET  /api/v1/crawler/start     - Start SSE crawl stream
     * - GET  /api/v1/crawler/search    - Start SSE search stream
     * - POST /api/v1/crawler/sitemap   - Import from sitemap
     * - GET  /api/v1/crawler/status/{id} - Get job status
     * - POST /api/v1/crawler/stop/{id}   - Stop job
     */
    private function handleCrawlerApi(string $action, string $subAction, string $method): void
    {
        // Import the CrawlerController
        $controllerPath = __DIR__ . '/../Api/v1/CrawlerController.php';
        if (!file_exists($controllerPath)) {
            header('Content-Type: application/json');
            http_response_code(500);
            echo json_encode([
                'success' => false,
                'error' => 'Crawler controller not found',
            ]);
            return;
        }

        require_once $controllerPath;

        // Get crawler config and database config
        $crawlerConfig = $this->config['crawler'] ?? [];
        $dbConfig = $this->config['database'] ?? [];

        $controller = new \FlowbotDCI\Api\v1\CrawlerController($crawlerConfig, $dbConfig);

        // Merge GET and POST params
        $params = $_GET;
        if ($method === 'POST') {
            $params = array_merge($params, $this->getJsonInput());
        }

        switch ($action) {
            case 'start':
                // SSE stream for deep crawl - GET /api/v1/crawler/start?url=...
                $controller->streamCrawl($params);
                break;

            case 'search':
                // SSE stream for search - GET /api/v1/crawler/search?query=...
                $controller->streamSearch($params);
                break;

            case 'sitemap':
                // Import from sitemap - POST /api/v1/crawler/sitemap
                header('Content-Type: application/json');
                $result = $controller->importSitemap($params);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'status':
                // Get job status - GET /api/v1/crawler/status/{id}
                header('Content-Type: application/json');
                $result = $controller->getStatus($subAction);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'stop':
                // Stop job - POST /api/v1/crawler/stop/{id}
                header('Content-Type: application/json');
                $result = $controller->stopJob($subAction);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'engines':
                // List available search engines - GET /api/v1/crawler/engines
                header('Content-Type: application/json');
                $result = $controller->getEngines();
                echo json_encode($result);
                break;

            default:
                header('Content-Type: application/json');
                http_response_code(404);
                echo json_encode([
                    'success' => false,
                    'error' => 'Unknown crawler endpoint: ' . $action,
                ]);
        }
    }

    /**
     * Get JSON input from request body
     */
    private function getJsonInput(): array
    {
        $input = file_get_contents('php://input');
        if ($input) {
            $data = json_decode($input, true);
            if (is_array($data)) {
                return $data;
            }
        }
        return $_POST;
    }

    /**
     * Handle legacy API requests
     */
    private function handleLegacyApi(): void
    {
        header('Content-Type: application/json');
        header('Cache-Control: no-cache, must-revalidate');

        $action = $this->sanitizeInput($_GET['action'] ?? '');

        switch ($action) {
            case 'progress':
                echo json_encode([
                    'success' => true,
                    'data' => $this->tracker->loadProgress(),
                ]);
                break;

            case 'domains':
                $sortBy = $this->sanitizeInput($_GET['sort'] ?? 'total');
                $direction = $this->sanitizeInput($_GET['dir'] ?? 'desc');
                $allowedSortBy = ['total', 'success', 'error', 'ignored', 'rate', 'domain', 'avg_time'];
                $allowedDir = ['asc', 'desc'];
                $sortBy = in_array($sortBy, $allowedSortBy) ? $sortBy : 'total';
                $direction = in_array($direction, $allowedDir) ? $direction : 'desc';
                echo json_encode([
                    'success' => true,
                    'data' => $this->tracker->getDomainStatsSorted($sortBy, $direction),
                ]);
                break;

            case 'logs':
                $filters = [
                    'status' => $this->sanitizeInput($_GET['status'] ?? ''),
                    'domain' => $this->sanitizeInput($_GET['domain'] ?? ''),
                    'error_type' => $this->sanitizeInput($_GET['error_type'] ?? ''),
                    'search' => $this->sanitizeInput($_GET['search'] ?? ''),
                ];
                $page = max(1, (int)($_GET['p'] ?? 1));
                $perPage = min(100, max(10, (int)($_GET['per_page'] ?? 50)));

                echo json_encode([
                    'success' => true,
                    'data' => $this->tracker->getFilteredLogs($filters, $page, $perPage),
                ]);
                break;

            case 'log_domains':
                echo json_encode([
                    'success' => true,
                    'data' => $this->tracker->getLogDomains(),
                ]);
                break;

            case 'export':
                $this->handleExport();
                break;

            default:
                echo json_encode([
                    'success' => false,
                    'error' => 'Unknown action',
                ]);
        }

        exit;
    }

    /**
     * Show Dashboard home (v3.0)
     */
    private function showDashboard(): void
    {
        $viewPath = $this->config['paths']['views'] . '/dashboard.php';

        if (!file_exists($viewPath)) {
            // Fallback to form if dashboard doesn't exist
            $this->showForm();
            return;
        }

        $stats = $this->dashboardService->getStats();
        $activeProcesses = $this->dashboardService->getActiveProcesses();
        $history = $this->dashboardService->getHistory(1, 10);
        $activity = $this->dashboardService->getRecentActivity(20);
        $charts = $this->dashboardService->getChartData('7d');
        $currentPage = 'dashboard';

        include $viewPath;
        exit;
    }

    /**
     * Show URL input form (Stage 1)
     */
    private function showForm(): void
    {
        $viewPath = $this->config['paths']['views'] . '/form.php';

        if (!file_exists($viewPath)) {
            throw new \Exception("View file not found: {$viewPath}");
        }

        $processId = $this->processId ?: $this->getProcessId();
        $phases = $this->config['phases'];
        $csrfToken = $this->generateCsrfToken();
        $currentPage = 'new';

        include $viewPath;
        exit;
    }

    /**
     * Handle URL submit (v3.0 - go to analyze)
     */
    private function handleUrlSubmit(): void
    {
        if (!$this->validateCsrfToken()) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
            return;
        }

        $rawUrls = $this->sanitizeUrlsInput($_POST['urls'] ?? '');
        $mode = $this->sanitizeInput($_POST['mode'] ?? 'standard');

        if (empty($rawUrls)) {
            $this->showForm();
            return;
        }

        // Store URLs in session for analysis page
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        $_SESSION['pending_urls'] = $rawUrls;
        $_SESSION['processing_mode'] = $mode;

        // Ensure session data is written before redirect
        session_write_close();

        // Redirect to analyze page
        header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/new/analyze');
        exit;
    }

    /**
     * Show analysis preview (Stage 2)
     */
    private function showAnalyze(): void
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        if (empty($_SESSION['pending_urls'])) {
            header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/new');
            exit;
        }

        $viewPath = $this->config['paths']['views'] . '/analyze.php';

        if (!file_exists($viewPath)) {
            // Fallback: start processing directly
            $this->startProcessing();
            return;
        }

        $rawUrls = $_SESSION['pending_urls'];
        $mode = $_SESSION['processing_mode'] ?? 'standard';

        // Pre-process URLs for analysis
        $urlProcessor = new UrlPreProcessor();
        $urlLines = array_filter(array_map('trim', explode("\n", $rawUrls)));
        $analysisResult = $urlProcessor->process($urlLines);

        $processId = $this->getProcessId();
        $csrfToken = $this->generateCsrfToken();
        $currentPage = 'analyze';

        // Get domain estimates
        $validUrls = $analysisResult['valid'];
        $domains = [];
        foreach ($validUrls as $url) {
            $host = parse_url($url, PHP_URL_HOST) ?? '';
            $domain = preg_replace('/^www\./', '', $host);
            if (!isset($domains[$domain])) {
                $domains[$domain] = [
                    'domain' => $domain,
                    'count' => 0,
                    'is_social_media' => $urlProcessor->isSocialMedia($url),
                    'success_rate' => $this->dashboardService->getDomainSuccessRate($domain),
                ];
            }
            $domains[$domain]['count']++;
        }
        uasort($domains, fn($a, $b) => $b['count'] <=> $a['count']);

        $estimates = $this->dashboardService->estimateProcessingTime(count($validUrls), array_keys($domains));

        include $viewPath;
        exit;
    }

    /**
     * Start processing (go to Stage 3) - v3.1 IMMEDIATE REDIRECT
     * Stores data in session and redirects immediately for instant page load
     */
    private function startProcessing(): void
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        if (empty($_SESSION['pending_urls'])) {
            header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/new');
            exit;
        }

        $rawUrls = $_SESSION['pending_urls'];
        $mode = $_POST['mode'] ?? $_SESSION['processing_mode'] ?? 'standard';
        $options = [
            'auto_export' => isset($_POST['auto_export']),
            'notify' => isset($_POST['notify']),
            'include_failed' => isset($_POST['include_failed']),
        ];

        // Generate process ID immediately
        $this->processId = $this->getProcessId();

        // v3.1: Store everything in session for DEFERRED initialization
        // This allows the page to load IMMEDIATELY while initialization happens via AJAX
        $_SESSION['pending_process'] = [
            'id' => $this->processId,
            'urls' => $rawUrls,
            'mode' => $mode,
            'options' => $options,
            'created_at' => time()
        ];

        // Clear the URL session data (now in pending_process)
        unset($_SESSION['pending_urls']);
        unset($_SESSION['processing_mode']);

        // Ensure session is written before redirect
        session_write_close();

        // REDIRECT IMMEDIATELY! (~10ms instead of ~900ms)
        // The progress page will call /api/v1/process/{id}/initialize to do the actual setup
        header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/process/' . $this->processId);
        exit;
    }

    /**
     * Initialize pending process via API (v3.1)
     * Called by the progress page via AJAX after immediate redirect
     */
    private function initializePendingProcess(string $processId): array
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        $pending = $_SESSION['pending_process'] ?? null;

        // Validate pending process
        if (!$pending || $pending['id'] !== $processId) {
            // Check if process already initialized (page refresh case)
            $this->processId = $processId;
            $this->tracker = new ProgressTracker($this->config, $processId, $this->redis);

            if ($this->tracker->hasProgress()) {
                return [
                    'success' => true,
                    'status' => 'already_initialized',
                    'total_links' => $this->tracker->loadProgress()['total_links'] ?? 0
                ];
            }

            return [
                'success' => false,
                'error' => 'Invalid or expired process session',
                'http_code' => 400
            ];
        }

        // Check if initialization took too long (timeout: 5 minutes)
        if (time() - $pending['created_at'] > 300) {
            unset($_SESSION['pending_process']);
            return [
                'success' => false,
                'error' => 'Process session expired',
                'http_code' => 410
            ];
        }

        try {
            // NOW do the actual initialization (user already sees the page!)
            $this->processId = $processId;
            $this->tracker = new ProgressTracker($this->config, $this->processId, $this->redis);
            $this->processor = new UrlProcessor($this->config, $this->database, $this->tracker);

            // Extract and initialize URLs
            $links = $this->processor->extractUrls($pending['urls']);
            $this->tracker->initialize($links);

            // Create process record in database
            $this->dashboardService->createProcess(
                $this->processId,
                count($links),
                $pending['mode'],
                $pending['options']
            );

            // Clear pending session
            unset($_SESSION['pending_process']);
            session_write_close();

            return [
                'success' => true,
                'status' => 'initialized',
                'total_links' => count($links),
                'process_id' => $this->processId
            ];

        } catch (\Exception $e) {
            return [
                'success' => false,
                'error' => 'Initialization failed: ' . $e->getMessage(),
                'http_code' => 500
            ];
        }
    }

    /**
     * Handle process page routing (v3.1 with pending process support)
     */
    private function handleProcessPage(string $page): void
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        // v3.1: Check if this is a pending process waiting for initialization
        $pending = $_SESSION['pending_process'] ?? null;
        $isPending = $pending && $pending['id'] === $this->processId;

        // If not pending and no progress exists, redirect to home
        if (!$isPending && (!$this->tracker || !$this->tracker->hasProgress())) {
            header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/');
            exit;
        }

        switch ($page) {
            case 'domains':
                if ($isPending) {
                    // Can't show domains for pending process
                    header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/process/' . $this->processId);
                    exit;
                }
                $this->showDomainsPage();
                break;
            case 'logs':
                if ($isPending) {
                    header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/process/' . $this->processId);
                    exit;
                }
                $this->showLogsPage();
                break;
            case 'export':
                if ($isPending) {
                    header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/process/' . $this->processId);
                    exit;
                }
                $this->showExportPage();
                break;
            default:
                // v3.1: Show progress page (handles both pending and active processes)
                $this->showProgressPage($isPending);
        }
    }

    /**
     * Show progress page (v3.1 - supports pending and active states)
     */
    private function showProgressPage(bool $isPending = false): void
    {
        $viewPath = $this->config['paths']['views'] . '/progress.php';

        if (!file_exists($viewPath)) {
            throw new \Exception("View file not found: {$viewPath}");
        }

        if ($isPending) {
            // Show page with initializing state - data will come via AJAX
            $data = [
                'total_links' => 0,
                'processed_links' => 0,
                'imported_links' => 0,
                'ignored_links' => 0,
                'error_links' => 0,
                'phase_queues' => [],
                'phase_stats' => [],
                'domain_stats' => [],
                'http_codes' => [],
                'status' => 'initializing'
            ];
            $logs = [];
            $processId = $this->processId;
            $currentPage = 'progress';
            $isPaused = false;
            $isInitializing = true;

            // v3.1 FIX: Generate CSRF token for pending processes (needed for AJAX calls)
            if (session_status() === PHP_SESSION_NONE) {
                session_start();
            }
            $csrfToken = $this->generateCsrfToken();

            include $viewPath;
            exit;
        }

        // Regular flow - process already initialized
        $state = $this->processManager->getState($this->processId);
        if ($state['status'] === 'paused') {
            $result = ['data' => $this->tracker->loadProgress(), 'logs' => [], 'paused' => true];
            $this->showProgress($result);
            return;
        }
        if ($state['status'] === 'cancelled') {
            $this->tracker->cleanup();
            header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/');
            exit;
        }

        // Normal progress display (processing happens via AJAX now)
        $data = $this->tracker->loadProgress();
        $logs = [];
        $processId = $this->processId;
        $currentPage = 'progress';
        $isPaused = false;
        $isInitializing = false;

        // v3.1 FIX: Generate CSRF token for AJAX calls
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
        $csrfToken = $this->generateCsrfToken();

        include $viewPath;
        exit;
    }

    /**
     * Show domains page
     */
    private function showDomainsPage(): void
    {
        $viewPath = $this->config['paths']['views'] . '/domains.php';

        if (!file_exists($viewPath)) {
            throw new \Exception("View file not found: {$viewPath}");
        }

        $data = $this->tracker->loadProgress();
        $domainStats = $this->tracker->getDomainStatsSorted();
        $processId = $this->processId;
        $currentPage = 'domains';

        include $viewPath;
        exit;
    }

    /**
     * Show logs page
     */
    private function showLogsPage(): void
    {
        $viewPath = $this->config['paths']['views'] . '/logs.php';

        if (!file_exists($viewPath)) {
            throw new \Exception("View file not found: {$viewPath}");
        }

        $data = $this->tracker->loadProgress();
        $logsData = $this->tracker->getFilteredLogs([], 1, 50);
        $logDomains = $this->tracker->getLogDomains();
        $processId = $this->processId;
        $currentPage = 'logs';

        include $viewPath;
        exit;
    }

    /**
     * Show export page
     */
    private function showExportPage(): void
    {
        $data = $this->tracker->loadProgress();
        $processId = $this->processId;
        $currentPage = 'export';

        // Simple export page
        header('Content-Type: text/html; charset=UTF-8');
        echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Export - Flowb0t DCI</title></head><body>';
        echo '<h1>Export Options for ' . htmlspecialchars($processId) . '</h1>';
        echo '<ul>';
        echo '<li><a href="?process_id=' . urlencode($processId) . '&page=api&action=export&type=logs&format=json">Export Logs (JSON)</a></li>';
        echo '<li><a href="?process_id=' . urlencode($processId) . '&page=api&action=export&type=logs&format=csv">Export Logs (CSV)</a></li>';
        echo '<li><a href="?process_id=' . urlencode($processId) . '&page=api&action=export&type=domains&format=json">Export Domains (JSON)</a></li>';
        echo '<li><a href="?process_id=' . urlencode($processId) . '&page=api&action=export&type=domains&format=csv">Export Domains (CSV)</a></li>';
        echo '</ul>';
        echo '<p><a href="?process_id=' . urlencode($processId) . '">Back to Progress</a></p>';
        echo '</body></html>';
        exit;
    }

    /**
     * Show history page
     */
    private function showHistory(): void
    {
        $page = max(1, (int)($_GET['p'] ?? 1));
        $limit = 20;
        $filters = [
            'status' => $_GET['status'] ?? null,
            'date_from' => $_GET['date_from'] ?? null,
            'date_to' => $_GET['date_to'] ?? null,
            'search' => $_GET['search'] ?? null,
            'sort' => $_GET['sort'] ?? 'created_at',
            'order' => $_GET['order'] ?? 'desc',
        ];

        $history = $this->dashboardService->getHistory($page, $limit, array_filter($filters));
        $currentPage = 'history';

        // Render history page
        $viewPath = $this->config['paths']['views'] . '/history.php';
        if (file_exists($viewPath)) {
            include $viewPath;
        } else {
            // Simple fallback
            header('Content-Type: text/html; charset=UTF-8');
            echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>History - Flowb0t DCI</title></head><body>';
            echo '<h1>Process History</h1>';
            echo '<pre>' . json_encode($history, JSON_PRETTY_PRINT) . '</pre>';
            echo '<p><a href="' . dirname($_SERVER['SCRIPT_NAME']) . '/">Back to Dashboard</a></p>';
            echo '</body></html>';
        }
        exit;
    }

    /**
     * Show settings page
     */
    private function showSettings(): void
    {
        $settings = $this->dashboardService->getSettings();
        $currentPage = 'settings';

        $viewPath = $this->config['paths']['views'] . '/settings.php';
        if (file_exists($viewPath)) {
            include $viewPath;
        } else {
            // Simple fallback
            header('Content-Type: text/html; charset=UTF-8');
            echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Settings - Flowb0t DCI</title></head><body>';
            echo '<h1>Settings</h1>';
            echo '<pre>' . json_encode($settings, JSON_PRETTY_PRINT) . '</pre>';
            echo '<p><a href="' . dirname($_SERVER['SCRIPT_NAME']) . '/">Back to Dashboard</a></p>';
            echo '</body></html>';
        }
        exit;
    }

    /**
     * Show crawler page (v6.0 Deep Crawler)
     */
    private function showCrawler(): void
    {
        $currentPage = 'crawler';

        // Get search engine config for the view
        $searchEnginesConfig = $this->config['crawler']['search_engines'] ?? [
            'duckduckgo' => ['enabled' => true],
            'google' => ['enabled' => false, 'api_key' => '', 'cx' => ''],
            'bing' => ['enabled' => false, 'api_key' => ''],
        ];

        $viewPath = $this->config['paths']['views'] . '/crawler.php';
        if (file_exists($viewPath)) {
            include $viewPath;
        } else {
            // Simple fallback
            header('Content-Type: text/html; charset=UTF-8');
            echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Crawler - Flowb0t DCI</title></head><body>';
            echo '<h1>Deep Crawler v6.0</h1>';
            echo '<p>Crawler view not found. Please ensure views/crawler.php exists.</p>';
            echo '<p><a href="' . dirname($_SERVER['SCRIPT_NAME']) . '/">Back to Dashboard</a></p>';
            echo '</body></html>';
        }
        exit;
    }

    /**
     * Handle Analytics API endpoints (v7.0)
     *
     * Endpoints:
     * - GET /api/v1/analytics/overview      - Dashboard overview
     * - GET /api/v1/analytics/domains       - Domain statistics
     * - GET /api/v1/analytics/performance   - Performance metrics
     * - GET /api/v1/analytics/timeline      - Timeline data
     * - GET /api/v1/analytics/errors        - Error analysis
     * - GET /api/v1/analytics/trends        - Trend analysis
     */
    private function handleAnalyticsApi(string $action, string $method, array $params): void
    {
        // Import the AnalyticsController
        $controllerPath = __DIR__ . '/../Api/v1/AnalyticsController.php';
        if (!file_exists($controllerPath)) {
            header('Content-Type: application/json');
            http_response_code(500);
            echo json_encode([
                'success' => false,
                'error' => 'Analytics controller not found',
            ]);
            return;
        }

        require_once $controllerPath;

        // Get config and database config
        $crawlerConfig = $this->config['crawler'] ?? [];
        $dbConfig = $this->config['database'] ?? [];

        $controller = new \FlowbotDCI\Api\v1\AnalyticsController($crawlerConfig, $dbConfig);

        header('Content-Type: application/json');

        switch ($action) {
            case 'overview':
                $result = $controller->getOverview($params);
                break;

            case 'domains':
                $result = $controller->getDomains($params);
                break;

            case 'performance':
                $result = $controller->getPerformance($params);
                break;

            case 'timeline':
                $result = $controller->getTimeline($params);
                break;

            case 'errors':
                $result = $controller->getErrors($params);
                break;

            case 'trends':
                $result = $controller->getTrends($params);
                break;

            default:
                $result = [
                    'success' => false,
                    'error' => 'Unknown analytics endpoint: ' . $action,
                    'http_code' => 404,
                ];
        }

        http_response_code($result['http_code'] ?? 200);
        echo json_encode($result);
    }

    /**
     * Show unified crawler page (v7.0)
     */
    private function showUnifiedCrawler(): void
    {
        $currentPage = 'crawler-unified';

        $viewPath = $this->config['paths']['views'] . '/crawler-unified.php';
        if (file_exists($viewPath)) {
            include $viewPath;
        } else {
            // Simple fallback
            header('Content-Type: text/html; charset=UTF-8');
            echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Unified Crawler - Flowb0t DCI</title></head><body>';
            echo '<h1>Unified Crawler v7.0</h1>';
            echo '<p>Unified crawler view not found. Please ensure views/crawler-unified.php exists.</p>';
            echo '<p><a href="' . dirname($_SERVER['SCRIPT_NAME']) . '/">Back to Dashboard</a></p>';
            echo '</body></html>';
        }
        exit;
    }

    /**
     * Handle Unified Crawler API endpoints (v7.0)
     *
     * Endpoints:
     * - GET  /api/v1/crawler/unified/start      - Start SSE crawl stream
     * - GET  /api/v1/crawler/unified/search     - Start SSE search stream
     * - POST /api/v1/crawler/unified/batch      - Batch URL processing
     * - GET  /api/v1/crawler/unified/status/{id} - Get job status
     * - POST /api/v1/crawler/unified/pause/{id}  - Pause job
     * - POST /api/v1/crawler/unified/resume/{id} - Resume job
     * - POST /api/v1/crawler/unified/stop/{id}   - Stop job
     * - DELETE /api/v1/crawler/unified/{id}      - Delete job
     * - GET  /api/v1/crawler/unified/jobs        - List jobs
     * - GET  /api/v1/crawler/unified/progress/{id} - Get progress
     * - GET  /api/v1/crawler/unified/logs/{id}   - Get logs
     * - GET  /api/v1/crawler/unified/domains     - Get domain stats
     * - GET  /api/v1/crawler/unified/analytics   - Get analytics
     */
    private function handleUnifiedCrawlerApi(string $action, string $method, array $parts): void
    {
        // Import the UnifiedCrawlerController
        $controllerPath = __DIR__ . '/../Api/v1/UnifiedCrawlerController.php';
        if (!file_exists($controllerPath)) {
            header('Content-Type: application/json');
            http_response_code(500);
            echo json_encode([
                'success' => false,
                'error' => 'Unified crawler controller not found',
            ]);
            return;
        }

        require_once $controllerPath;

        // Get crawler config and database config
        $crawlerConfig = $this->config['crawler'] ?? [];
        $dbConfig = $this->config['database'] ?? [];

        $controller = new \FlowbotDCI\Api\v1\UnifiedCrawlerController($crawlerConfig, $dbConfig);

        // Merge GET and POST params
        $params = $_GET;
        if ($method === 'POST') {
            $params = array_merge($params, $this->getJsonInput());
        }

        // Get extra path parts for job ID
        $jobId = $parts[3] ?? '';

        switch ($action) {
            case 'start':
                // SSE stream for crawl - GET /api/v1/crawler/unified/start
                $controller->streamCrawl($params);
                break;

            case 'search':
                // SSE stream for search - GET /api/v1/crawler/unified/search
                $controller->streamSearch($params);
                break;

            case 'batch':
                // Batch URL processing - POST /api/v1/crawler/unified/batch
                header('Content-Type: application/json');
                $result = $controller->processBatch($params);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'status':
                // Get job status - GET /api/v1/crawler/unified/status/{id}
                header('Content-Type: application/json');
                $result = $controller->getStatus($jobId);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'pause':
                // Pause job - POST /api/v1/crawler/unified/pause/{id}
                header('Content-Type: application/json');
                $result = $controller->pauseJob($jobId);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'resume':
                // Resume job - POST /api/v1/crawler/unified/resume/{id}
                header('Content-Type: application/json');
                $result = $controller->resumeJob($jobId);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'stop':
                // Stop job - POST /api/v1/crawler/unified/stop/{id}
                header('Content-Type: application/json');
                $result = $controller->stopJob($jobId);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'jobs':
                // List jobs - GET /api/v1/crawler/unified/jobs
                header('Content-Type: application/json');
                $result = $controller->listJobs($params);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'progress':
                // Get progress - GET /api/v1/crawler/unified/progress/{id}
                header('Content-Type: application/json');
                $result = $controller->getProgress($jobId);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'logs':
                // Get logs - GET /api/v1/crawler/unified/logs/{id}
                header('Content-Type: application/json');
                $result = $controller->getLogs($jobId, $params);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'domains':
                // Get domain stats - GET /api/v1/crawler/unified/domains
                header('Content-Type: application/json');
                $result = $controller->getDomainStats($params);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            case 'analytics':
                // Get analytics - GET /api/v1/crawler/unified/analytics
                header('Content-Type: application/json');
                $result = $controller->getAnalytics($params);
                http_response_code($result['http_code'] ?? 200);
                echo json_encode($result);
                break;

            default:
                // Handle DELETE method for job deletion
                if ($method === 'DELETE' && !empty($action)) {
                    header('Content-Type: application/json');
                    $result = $controller->deleteJob($action);
                    http_response_code($result['http_code'] ?? 200);
                    echo json_encode($result);
                    break;
                }

                header('Content-Type: application/json');
                http_response_code(404);
                echo json_encode([
                    'success' => false,
                    'error' => 'Unknown unified crawler endpoint: ' . $action,
                ]);
        }
    }

    /**
     * Legacy URL submit handler
     */
    private function handleUrlSubmitLegacy(): void
    {
        if (!$this->validateCsrfToken()) {
            http_response_code(403);
            header('Content-Type: application/json');
            echo json_encode([
                'success' => false,
                'error' => 'Invalid or missing CSRF token.',
            ]);
            return;
        }

        $sanitizedUrls = $this->sanitizeUrlsInput($_POST['urls']);
        $this->initializeProcess($sanitizedUrls);
    }

    /**
     * Initialize new processing session (legacy)
     */
    private function initializeProcess(string $rawUrls): void
    {
        $links = $this->processor->extractUrls($rawUrls);
        $this->tracker->initialize($links);

        header('Content-Type: text/html; charset=UTF-8');
        echo "<!DOCTYPE html><html><head><meta charset='UTF-8'>";
        echo "<title>Process Started</title>";
        echo "<style>body{font-family:Arial,sans-serif;background:#2c3e50;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0;}";
        echo ".box{background:#34495e;padding:30px;border-radius:12px;text-align:center;}</style></head><body>";
        echo "<div class='box'>";
        echo "<h2>Process Started</h2>";
        echo "<p>Process ID: <strong>{$this->processId}</strong></p>";
        echo "<p>Total Links: <strong>" . count($links) . "</strong></p>";
        echo "<p><a href='?process_id={$this->processId}' style='color:#3498db;'>Click here to see progress</a></p>";
        echo "</div></body></html>";
        exit;
    }

    /**
     * Process batch and show progress
     */
    private function processBatch(): void
    {
        // Check if process is paused or cancelled
        $state = $this->processManager->getState($this->processId);
        if ($state['status'] === 'paused') {
            $this->showProgress(['data' => $this->tracker->loadProgress(), 'logs' => [], 'paused' => true]);
            return;
        }
        if ($state['status'] === 'cancelled') {
            $this->tracker->cleanup();
            header('Location: ' . dirname($_SERVER['SCRIPT_NAME']) . '/');
            exit;
        }

        $result = $this->processor->processBatch();

        // Update dashboard stats
        $data = $result['data'];
        $this->dashboardService->updateProcess($this->processId, [
            'processed_urls' => $data['processed_links'] ?? 0,
            'success_count' => $data['processed_links'] ?? 0,
            'failed_count' => $data['error_links'] ?? 0,
            'current_phase' => $data['current_phase'] ?? 0,
            'status' => $result['complete'] ? 'completed' : 'running',
        ]);

        // Render progress
        $this->showProgress($result);

        // Auto-refresh if not complete
        if (!$result['complete']) {
            echo '<meta http-equiv="refresh" content="0">';
        } else {
            $this->tracker->cleanup();
            echo "<div style='text-align:center;margin-top:20px;'>";
            echo "<h2 style='color:#2ecc71;'>Process Complete!</h2>";
            echo "<p>Process <strong>{$this->processId}</strong> finished successfully.</p>";
            echo "<a href='" . dirname($_SERVER['SCRIPT_NAME']) . "/' style='color:#3498db;'>Back to Dashboard</a>";
            echo "</div>";
        }
    }

    /**
     * Show progress page
     */
    private function showProgress(array $result): void
    {
        $viewPath = $this->config['paths']['views'] . '/progress.php';

        if (!file_exists($viewPath)) {
            throw new \Exception("View file not found: {$viewPath}");
        }

        $data = $result['data'];
        $logs = $result['logs'] ?? [];
        $processId = $this->processId;
        $currentPage = 'progress';
        $isPaused = $result['paused'] ?? false;

        include $viewPath;
    }

    /**
     * Run one batch via API and return JSON result
     * Called by AJAX from progress.php for continuous processing
     */
    private function runBatchApi(): array
    {
        // Check if process exists
        if (!$this->tracker || !$this->tracker->hasProgress()) {
            return ['success' => false, 'error' => 'Process not found', 'http_code' => 404];
        }

        // Check pause/cancel state
        $state = $this->processManager->getState($this->processId);
        if ($state['status'] === 'paused') {
            return [
                'success' => true,
                'status' => 'paused',
                'data' => $this->tracker->loadProgress(),
                'complete' => false
            ];
        }
        if ($state['status'] === 'cancelled') {
            $this->tracker->cleanup();
            return ['success' => true, 'status' => 'cancelled', 'complete' => true];
        }

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

        // Update database
        $data = $result['data'];
        $this->dashboardService->updateProcess($this->processId, [
            'processed_urls' => $data['processed_links'] ?? 0,
            'success_count' => $data['processed_links'] ?? 0,
            'failed_count' => $data['error_links'] ?? 0,
            'current_phase' => $data['current_phase'] ?? 0,
            'status' => $result['complete'] ? 'completed' : 'running',
        ]);

        // Cleanup if complete
        if ($result['complete']) {
            $this->tracker->cleanup();
        }

        return [
            'success' => true,
            'status' => $result['complete'] ? 'completed' : 'running',
            'data' => $data,
            'complete' => $result['complete'],
            'logs' => $result['logs'] ?? []  // v3.1 FIX: Include batch logs for real-time display
        ];
    }

    /**
     * Handle export
     */
    private function handleExport(): void
    {
        $type = $this->sanitizeInput($_GET['type'] ?? 'logs');
        $format = $this->sanitizeInput($_GET['format'] ?? 'json');

        $allowedTypes = ['logs', 'domains'];
        $allowedFormats = ['json', 'csv'];
        $type = in_array($type, $allowedTypes) ? $type : 'logs';
        $format = in_array($format, $allowedFormats) ? $format : 'json';

        switch ($type) {
            case 'domains':
                $data = $this->tracker->getDomainStatsSorted();
                $filename = "domains_{$this->processId}";
                break;
            case 'logs':
            default:
                $logsData = $this->tracker->getFilteredLogs([], 1, 10000);
                $data = $logsData['logs'];
                $filename = "logs_{$this->processId}";
        }

        if ($format === 'csv') {
            header('Content-Type: text/csv');
            header("Content-Disposition: attachment; filename=\"{$filename}.csv\"");

            $output = fopen('php://output', 'w');
            if (!empty($data)) {
                $first = is_array($data) ? reset($data) : $data;
                if (is_array($first)) {
                    fputcsv($output, array_keys($first));
                    foreach ($data as $row) {
                        $flatRow = array_map(function($val) {
                            return is_array($val) ? json_encode($val) : $val;
                        }, $row);
                        fputcsv($output, $flatRow);
                    }
                }
            }
            fclose($output);
        } else {
            header('Content-Type: application/json');
            header("Content-Disposition: attachment; filename=\"{$filename}.json\"");
            echo json_encode($data, JSON_PRETTY_PRINT);
        }

        exit;
    }

    /**
     * Clean old folders
     */
    private function cleanOldFolders(): void
    {
        $tempPath = $this->config['paths']['temp'];
        $lifetime = $this->config['processing']['temp_folder_lifetime'];

        foreach (glob($tempPath . '/*') as $folder) {
            if (is_dir($folder) && (time() - filemtime($folder)) > $lifetime) {
                array_map('unlink', glob("$folder/*.*"));
                @rmdir($folder);
            }
        }
    }

    /**
     * Get database
     */
    public function getDatabase(): Database
    {
        return $this->database;
    }

    /**
     * Get Redis service
     */
    public function getRedis(): RedisService
    {
        return $this->redis;
    }

    /**
     * Get config
     */
    public function getConfig(): array
    {
        return $this->config;
    }
}
