<?php

namespace Licenzo\Http\Middleware;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

use Licenzo\Framework\Http\Request;
use Licenzo\Framework\Http\Response;

/**
 * Performance Monitor Middleware
 *
 * Monitors request performance and adds debugging information
 */
class PerformanceMonitor
{
    /**
     * Performance thresholds (in milliseconds).
     */
    protected array $thresholds = [
        'slow_request'      => 1000,    // 1 second
        'very_slow_request' => 3000, // 3 seconds
    ];

    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, \Closure $next)
    {
        $startTime = microtime(true);
        $startMemory = memory_get_usage(true);

        // Process the request
        $response = $next($request);

        // Calculate performance metrics
        $endTime = microtime(true);
        $endMemory = memory_get_usage(true);

        $duration = ($endTime - $startTime) * 1000; // Convert to milliseconds
        $memoryUsed = $endMemory - $startMemory;
        $peakMemory = memory_get_peak_usage(true);

        // Add performance headers
        if ($response instanceof Response) {
            $this->addPerformanceHeaders($response, $duration, $memoryUsed, $peakMemory);
        }

        // Log slow requests
        $this->logSlowRequests($request, $duration, $memoryUsed);

        // Store performance data for debugging
        $this->storePerformanceData($request, [
            'duration'    => $duration,
            'memory_used' => $memoryUsed,
            'peak_memory' => $peakMemory,
            'start_time'  => $startTime,
            'end_time'    => $endTime,
        ]);

        return $response;
    }

    /**
     * Add performance headers to the response.
     */
    protected function addPerformanceHeaders(Response $response, float $duration, int $memoryUsed, int $peakMemory): void
    {
        // Only add headers in debug mode
        if (!defined('WP_DEBUG') || !WP_DEBUG) {
            return;
        }

        $response->header('X-Response-Time', number_format($duration, 2) . 'ms');
        $response->header('X-Memory-Usage', $this->formatBytes($memoryUsed));
        $response->header('X-Peak-Memory', $this->formatBytes($peakMemory));

        // Add performance classification
        if ($duration > $this->thresholds['very_slow_request']) {
            $response->header('X-Performance-Status', 'very-slow');
        } elseif ($duration > $this->thresholds['slow_request']) {
            $response->header('X-Performance-Status', 'slow');
        } else {
            $response->header('X-Performance-Status', 'normal');
        }
    }

    /**
     * Log slow requests.
     */
    protected function logSlowRequests(Request $request, float $duration, int $memoryUsed): void
    {
        if ($duration < $this->thresholds['slow_request']) {
            return;
        }

        $method = $request->getMethod();
        $uri = $request->getUri();
        $level = $duration > $this->thresholds['very_slow_request'] ? 'VERY SLOW' : 'SLOW';

        $message = sprintf(
            '[Licenzo] %s REQUEST: %s %s - %0.2fms, Memory: %s',
            $level,
            $method,
            $uri,
            $duration,
            $this->formatBytes($memoryUsed)
        );

        do_action('licenzo_slow_request', $request, $duration, $memoryUsed);
    }

    /**
     * Store performance data for debugging.
     */
    protected function storePerformanceData(Request $request, array $data): void
    {
        // Only store in debug mode
        if (!defined('WP_DEBUG') || !WP_DEBUG) {
            return;
        }

        // Store in WordPress transient for debugging
        $key = 'licenzo_perf_' . md5($request->getUri() . microtime());
        $transient_data = array_merge($data, [
            'method'    => $request->getMethod(),
            'uri'       => $request->getUri(),
            'timestamp' => time(),
        ]);

        set_transient($key, $transient_data, 300); // Store for 5 minutes

        // Track keys in a standard option (not WP transient internals)
        $tracked = get_option('licenzo_perf_tracked_keys', []);
        $tracked[$key] = time() + 300;
        update_option('licenzo_perf_tracked_keys', $tracked, false);

        // Keep only the last 10 performance records
        $this->cleanupOldPerformanceData();
    }

    /**
     * Cleanup old performance data.
     */
    protected function cleanupOldPerformanceData(): void
    {
        $tracked = get_option('licenzo_perf_tracked_keys', []);

        foreach ($tracked as $key => $expiry) {
            if ($expiry < time()) {
                delete_transient($key);
                unset($tracked[$key]);
            }
        }

        // Keep only the 10 most recent
        if (count($tracked) > 10) {
            asort($tracked);
            $overflow = array_slice(array_keys($tracked), 0, count($tracked) - 10);
            foreach ($overflow as $key) {
                delete_transient($key);
                unset($tracked[$key]);
            }
        }

        update_option('licenzo_perf_tracked_keys', $tracked, false);
    }

    /**
     * Format bytes to human readable format.
     */
    public function formatBytes(int $bytes): string
    {
        $units = ['B', 'KB', 'MB', 'GB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);

        $bytes /= pow(1024, $pow);

        return round($bytes, 2) . ' ' . $units[$pow];
    }

    /**
     * Get performance statistics.
     */
    public function getStats(): array
    {
        $stats = [];
        $tracked = get_option('licenzo_perf_tracked_keys', []);

        // Sort by expiry descending (most recent first)
        arsort($tracked);

        // Get only the 10 most recent entries
        $recentKeys = array_slice(array_keys($tracked), 0, 10);

        foreach ($recentKeys as $key) {
            $data = get_transient($key);
            if (is_array($data)) {
                $stats[] = $data;
            }
        }

        return $stats;
    }

    /**
     * Set performance thresholds.
     */
    public function setThresholds(array $thresholds): void
    {
        $this->thresholds = array_merge($this->thresholds, $thresholds);
    }

    /**
     * Get current thresholds.
     */
    public function getThresholds(): array
    {
        return $this->thresholds;
    }
}
