<?php
/**
 * ===========================================
 * FLOWBOT DCI - API v1 CONTROLLER
 * ===========================================
 * API-002: Versioned API endpoints
 * Version 1.0 - Initial stable API
 */

declare(strict_types=1);

namespace FlowbotDCI\Api\v1;

use FlowbotDCI\Services\ProgressTracker;
use FlowbotDCI\Services\CircuitBreaker;
use FlowbotDCI\Services\RetryManager;

class ApiController
{
    private string $processId;
    private ProgressTracker $tracker;
    private ?CircuitBreaker $circuitBreaker = null;
    private ?RetryManager $retryManager = null;

    // API version
    const VERSION = '1.0';
    const MIN_VERSION = '1.0';

    public function __construct(string $processId)
    {
        $this->processId = $processId;
        $this->tracker = new ProgressTracker($processId);
    }

    /**
     * Set circuit breaker instance
     */
    public function setCircuitBreaker(CircuitBreaker $cb): self
    {
        $this->circuitBreaker = $cb;
        return $this;
    }

    /**
     * Set retry manager instance
     */
    public function setRetryManager(RetryManager $rm): self
    {
        $this->retryManager = $rm;
        return $this;
    }

    /**
     * Sanitize and validate input parameters
     * SEC-001: Input validation for API endpoints
     */
    private function sanitizeParams(array $params): array
    {
        $sanitized = [];

        // Integer params with limits
        $sanitized['page'] = max(1, (int)($params['page'] ?? 1));
        $sanitized['per_page'] = min(100, max(10, (int)($params['per_page'] ?? 50)));
        $sanitized['limit'] = min(1000, max(10, (int)($params['limit'] ?? 100)));
        $sanitized['offset'] = max(0, (int)($params['offset'] ?? 0));

        // String params - whitelist allowed characters only
        if (isset($params['filter'])) {
            $sanitized['filter'] = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $params['filter']);
        }

        // Sort param - whitelist allowed values
        if (isset($params['sort'])) {
            $allowedSorts = ['total', 'success', 'failed', 'name', 'domain', 'created_at'];
            $sanitized['sort'] = in_array($params['sort'], $allowedSorts) ? $params['sort'] : 'total';
        }

        // Direction param - only asc or desc
        if (isset($params['dir'])) {
            $sanitized['dir'] = strtolower($params['dir']) === 'asc' ? 'asc' : 'desc';
        }

        // Type param - whitelist allowed values
        if (isset($params['type'])) {
            $allowedTypes = ['error', 'success', 'warning', 'info', 'import', 'ignore'];
            $sanitized['type'] = in_array($params['type'], $allowedTypes) ? $params['type'] : null;
        }

        return $sanitized;
    }

    /**
     * Check API rate limit by IP address
     * SEC-006: API rate limiting to prevent abuse
     * @param string $ip Client IP address
     * @param int $maxRequests Maximum requests per window
     * @param int $windowSeconds Time window in seconds
     * @return bool True if request allowed, false if rate limited
     */
    private function checkRateLimit(string $ip, int $maxRequests = 60, int $windowSeconds = 60): bool
    {
        $rateLimitDir = sys_get_temp_dir() . '/flowbot_api_ratelimit';
        if (!is_dir($rateLimitDir)) {
            @mkdir($rateLimitDir, 0700, true);
        }

        $file = $rateLimitDir . '/' . md5($ip) . '.json';
        $now = time();
        $requests = [];

        if (file_exists($file)) {
            $data = @json_decode(file_get_contents($file), true) ?: [];
            // Filter to only include requests within the current window
            $requests = array_filter($data, fn($ts) => $ts > ($now - $windowSeconds));
        }

        if (count($requests) >= $maxRequests) {
            return false; // Rate limited
        }

        $requests[] = $now;
        @file_put_contents($file, json_encode(array_values($requests)));

        return true;
    }

    /**
     * Handle API request
     * @param string $action API action
     * @param array $params Request parameters
     * @return array Response data
     */
    public function handle(string $action, array $params = []): array
    {
        try {
            // SEC-006: Check rate limit before processing
            $clientIp = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
            if (!$this->checkRateLimit($clientIp)) {
                return $this->error('Rate limit exceeded. Please try again later.', 429);
            }

            switch ($action) {
                case 'progress':
                    return $this->getProgress();
                case 'domains':
                    return $this->getDomains($params);
                case 'logs':
                    return $this->getLogs($params);
                case 'stats':
                    return $this->getStats();
                case 'health':
                    return $this->getHealth();
                case 'circuits':
                    return $this->getCircuits();
                case 'retries':
                    return $this->getRetries();
                case 'version':
                    return $this->getVersion();
                default:
                    return $this->error('Unknown action: ' . $action, 404);
            }
        } catch (\Exception $e) {
            return $this->error($e->getMessage(), 500);
        }
    }

    /**
     * GET /api/v1/progress
     * Get current processing progress
     */
    private function getProgress(): array
    {
        $data = $this->tracker->getData();

        return $this->success([
            'process_id' => $this->processId,
            'status' => $data['status'] ?? 'unknown',
            'phase_index' => $data['phase_index'] ?? 0,
            'total_urls' => $data['total_urls'] ?? 0,
            'processed' => $data['processed'] ?? 0,
            'successful' => $data['successful'] ?? 0,
            'errors' => $data['errors'] ?? 0,
            'ignored' => $data['ignored'] ?? 0,
            'progress_percent' => $data['progress_percent'] ?? 0,
            'timing' => $data['timing'] ?? [],
            'phase_stats' => $data['phase_stats'] ?? [],
        ]);
    }

    /**
     * GET /api/v1/domains
     * Get domain statistics
     * SEC-001: Input validation applied via sanitizeParams()
     */
    private function getDomains(array $params): array
    {
        // SEC-001: Sanitize all input parameters
        $params = $this->sanitizeParams($params);

        $data = $this->tracker->getData();
        $domains = $data['domain_stats'] ?? [];

        // Apply filters (now sanitized)
        if (!empty($params['filter'])) {
            $filter = strtolower($params['filter']);
            $domains = array_filter($domains, function($stats, $domain) use ($filter) {
                return stripos($domain, $filter) !== false;
            }, ARRAY_FILTER_USE_BOTH);
        }

        // Apply sorting (now validated against whitelist)
        $sortBy = $params['sort'] ?? 'total';
        $sortDir = ($params['dir'] ?? 'desc') === 'asc' ? 1 : -1;

        uasort($domains, function($a, $b) use ($sortBy, $sortDir) {
            $aVal = $a[$sortBy] ?? 0;
            $bVal = $b[$sortBy] ?? 0;
            return ($aVal <=> $bVal) * $sortDir;
        });

        // Apply pagination (now with validated limits)
        $page = $params['page'];
        $perPage = $params['per_page'];
        $total = count($domains);
        $offset = ($page - 1) * $perPage;

        $domains = array_slice($domains, $offset, $perPage, true);

        return $this->success([
            'domains' => $domains,
            'pagination' => [
                'page' => $page,
                'per_page' => $perPage,
                'total' => $total,
                'total_pages' => ceil($total / $perPage),
            ],
        ]);
    }

    /**
     * GET /api/v1/logs
     * Get processing logs
     * SEC-001: Input validation applied via sanitizeParams()
     */
    private function getLogs(array $params): array
    {
        // SEC-001: Sanitize all input parameters
        $params = $this->sanitizeParams($params);

        $type = $params['type'] ?? null;
        $limit = $params['limit'];
        $offset = $params['offset'];

        $logs = $this->tracker->getFilteredLogs($type, $limit, $offset);

        return $this->success([
            'logs' => $logs,
            'filter' => $type,
            'limit' => $limit,
            'offset' => $offset,
        ]);
    }

    /**
     * GET /api/v1/stats
     * Get detailed statistics
     */
    private function getStats(): array
    {
        $data = $this->tracker->getData();

        $stats = [
            'summary' => [
                'total_urls' => $data['total_urls'] ?? 0,
                'processed' => $data['processed'] ?? 0,
                'successful' => $data['successful'] ?? 0,
                'errors' => $data['errors'] ?? 0,
                'ignored' => $data['ignored'] ?? 0,
            ],
            'timing' => $data['timing'] ?? [],
            'phase_stats' => $data['phase_stats'] ?? [],
            'http_codes' => $data['http_codes'] ?? [],
            'retry_stats' => $data['retry_stats'] ?? [],
            'request_stats' => $data['request_stats'] ?? [],
        ];

        // Add circuit breaker stats if available
        if ($this->circuitBreaker) {
            $stats['circuit_breaker'] = $this->circuitBreaker->getStats();
        }

        // Add retry manager stats if available
        if ($this->retryManager) {
            $stats['retry_manager'] = $this->retryManager->getStats();
        }

        return $this->success($stats);
    }

    /**
     * GET /api/v1/health
     * Get system health status
     */
    private function getHealth(): array
    {
        $data = $this->tracker->getData();
        $status = $data['status'] ?? 'unknown';

        $isHealthy = !in_array($status, ['error', 'failed']);

        return $this->success([
            'status' => $isHealthy ? 'healthy' : 'unhealthy',
            'process_status' => $status,
            'uptime' => isset($data['timing']['start_time'])
                ? time() - strtotime($data['timing']['start_time'])
                : 0,
            'memory_usage' => memory_get_usage(true),
            'memory_peak' => memory_get_peak_usage(true),
        ], $isHealthy ? 200 : 503);
    }

    /**
     * GET /api/v1/circuits
     * Get circuit breaker status
     */
    private function getCircuits(): array
    {
        if (!$this->circuitBreaker) {
            return $this->success([
                'enabled' => false,
                'message' => 'Circuit breaker not configured',
            ]);
        }

        return $this->success([
            'enabled' => true,
            'stats' => $this->circuitBreaker->getStats(),
            'open_circuits' => $this->circuitBreaker->getOpenCircuits(),
            'all_circuits' => $this->circuitBreaker->getAllCircuits(),
        ]);
    }

    /**
     * GET /api/v1/retries
     * Get retry manager status
     */
    private function getRetries(): array
    {
        if (!$this->retryManager) {
            return $this->success([
                'enabled' => false,
                'message' => 'Retry manager not configured',
            ]);
        }

        return $this->success([
            'enabled' => true,
            'stats' => $this->retryManager->getStats(),
            'tracked_urls' => $this->retryManager->getAll(),
        ]);
    }

    /**
     * GET /api/v1/version
     * Get API version info
     */
    private function getVersion(): array
    {
        return $this->success([
            'api_version' => self::VERSION,
            'min_version' => self::MIN_VERSION,
            'app_name' => 'Flowbot DCI',
            'app_version' => '2.1',
        ]);
    }

    /**
     * Format success response
     */
    private function success(array $data, int $code = 200): array
    {
        return [
            'success' => true,
            'data' => $data,
            'meta' => [
                'api_version' => self::VERSION,
                'timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
                'process_id' => $this->processId,
            ],
            'http_code' => $code,
        ];
    }

    /**
     * Format error response
     */
    private function error(string $message, int $code = 400): array
    {
        return [
            'success' => false,
            'error' => [
                'message' => $message,
                'code' => $code,
            ],
            'meta' => [
                'api_version' => self::VERSION,
                'timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
                'process_id' => $this->processId,
            ],
            'http_code' => $code,
        ];
    }
}
