<?php

declare(strict_types=1);

namespace App\Framework\Performance;

use App\Framework\DateTime\Clock;

class PerformanceMetricLogger
{
    private \Redis $redis;
    private Clock $clock;
    private array $config;

    public function __construct(\Redis $redis, Clock $clock, array $config = [])
    {
        $this->redis = $redis;
        $this->clock = $clock;
        $this->config = array_merge([
            'enabled' => true,
            'ttl_minutes' => 60,
            'slow_threshold_ms' => 100,
            'max_slow_endpoints' => 100,
            'max_popular_endpoints' => 100,
        ], $config);
    }

    public function log(PerformanceMeter $meter, array $context = []): void
    {
        if (!$this->config['enabled']) {
            return;
        }

        $report = $meter->generateReport();
        $totalTime = $report['summary']['total_time_ms'];
        $url = $_SERVER['REQUEST_URI'] ?? 'cli';
        $method = $_SERVER['REQUEST_METHOD'] ?? 'CLI';

        try {
            $this->updateLiveMetrics($url, $method, $totalTime, $report, $context);
        } catch (\Exception $e) {
            // Redis-Fehler sollten nicht den Request crashen lassen
            error_log("Redis metrics update failed: " . $e->getMessage());
        }
    }

    private function updateLiveMetrics(string $url, string $method, float $duration, array $report, array $context): void
    {
        $now = $this->clock->now();
        $minute = $now->format('Y-m-d H:i');
        $hour = $now->format('Y-m-d H');
        $ttl = $this->config['ttl_minutes'] * 60;

        // Pipeline für bessere Performance
        $pipe = $this->redis->pipeline();

        // Requests pro Minute/Stunde
        $pipe->incr("performance:requests_per_minute:$minute");
        $pipe->expire("performance:requests_per_minute:$minute", $ttl);

        $pipe->incr("performance:requests_per_hour:$hour");
        $pipe->expire("performance:requests_per_hour:$hour", $ttl * 24);

        // Response-Zeiten sammeln
        $pipe->lpush("performance:response_times:$minute", $duration);
        $pipe->ltrim("performance:response_times:$minute", 0, 999); // Max 1000 Werte
        $pipe->expire("performance:response_times:$minute", $ttl);

        // Speicherverbrauch
        $memoryMb = $report['summary']['total_memory_mb'];
        $pipe->lpush("performance:memory_usage:$minute", $memoryMb);
        $pipe->ltrim("performance:memory_usage:$minute", 0, 999);
        $pipe->expire("performance:memory_usage:$minute", $ttl);

        // Langsame Endpoints tracken
        if ($duration > $this->config['slow_threshold_ms']) {
            $endpoint = "$method $url";
            $pipe->zadd('performance:slow_endpoints', $duration, $endpoint);
            $pipe->zremrangebyrank('performance:slow_endpoints', 0, -($this->config['max_slow_endpoints'] + 1));
        }

        // Populäre Endpoints
        $endpoint = "$method $url";
        $pipe->zincrby('performance:popular_endpoints', 1, $endpoint);
        $pipe->zremrangebyrank('performance:popular_endpoints', 0, -($this->config['max_popular_endpoints'] + 1));

        // Kategorien-Statistiken
        foreach ($report['categories'] ?? [] as $category => $data) {
            if (isset($data['total_time_ms'])) {
                $pipe->lpush("performance:category:$category:$minute", $data['total_time_ms']);
                $pipe->ltrim("performance:category:$category:$minute", 0, 99);
                $pipe->expire("performance:category:$category:$minute", $ttl);
            }
        }

        // Error-Rate tracking
        if (isset($context['has_errors']) && $context['has_errors']) {
            $pipe->incr("performance:errors_per_minute:$minute");
            $pipe->expire("performance:errors_per_minute:$minute", $ttl);
        }

        $pipe->exec();
    }

    public function getLiveMetrics(): array
    {
        try {
            $now = $this->clock->now();
            $minute = $now->format('Y-m-d H:i');
            $prevMinute = $now->modify('-1 minute')->format('Y-m-d H:i');
            $hour = $now->format('Y-m-d H');

            return [
                'requests' => [
                    'current_minute' => (int) ($this->redis->get("performance:requests_per_minute:$minute") ?? 0),
                    'previous_minute' => (int) ($this->redis->get("performance:requests_per_minute:$prevMinute") ?? 0),
                    'current_hour' => (int) ($this->redis->get("performance:requests_per_hour:$hour") ?? 0),
                ],
                'response_times' => [
                    'current_avg_ms' => $this->calculateAverage("performance:response_times:$minute"),
                    'previous_avg_ms' => $this->calculateAverage("performance:response_times:$prevMinute"),
                ],
                'memory' => [
                    'current_avg_mb' => $this->calculateAverage("performance:memory_usage:$minute"),
                    'previous_avg_mb' => $this->calculateAverage("performance:memory_usage:$prevMinute"),
                ],
                'slowest_endpoints' => $this->redis->zrevrange('performance:slow_endpoints', 0, 9, true),
                'popular_endpoints' => $this->redis->zrevrange('performance:popular_endpoints', 0, 9, true),
                'error_rate' => [
                    'current_minute' => (int) ($this->redis->get("performance:errors_per_minute:$minute") ?? 0),
                    'previous_minute' => (int) ($this->redis->get("performance:errors_per_minute:$prevMinute") ?? 0),
                ],
                'categories' => $this->getCategoryMetrics($minute, $prevMinute),
            ];

        } catch (\Exception $e) {
            return ['error' => $e->getMessage()];
        }
    }

    private function calculateAverage(string $key): float
    {
        $values = $this->redis->lrange($key, 0, -1);
        if (empty($values)) {
            return 0.0;
        }

        $sum = array_sum(array_map('floatval', $values));
        return round($sum / count($values), 2);
    }

    private function getCategoryMetrics(string $minute, string $prevMinute): array
    {
        $categories = [];
        $keys = $this->redis->keys("performance:category:*:$minute");

        foreach ($keys as $key) {
            if (preg_match('/performance:category:([^:]+):/', $key, $matches)) {
                $category = $matches[1];
                $prevKey = "performance:category:$category:$prevMinute";

                $categories[$category] = [
                    'current_avg_ms' => $this->calculateAverage($key),
                    'previous_avg_ms' => $this->calculateAverage($prevKey),
                ];
            }
        }

        return $categories;
    }

    public function clearMetrics(): void
    {
        $keys = $this->redis->keys('performance:*');
        if (!empty($keys)) {
            $this->redis->del($keys);
        }
    }

    public function setEnabled(bool $enabled): void
    {
        $this->config['enabled'] = $enabled;
    }

    public function isEnabled(): bool
    {
        return $this->config['enabled'];
    }
}
