<?php

namespace FleekDash\Services;

/**
 * Performance Audit Service
 *
 * Identifies performance bottlenecks in the analytics system.
 * Monitors queries, cache, and response times.
 */
class PerformanceAuditService
{
    private float $startTime;

    /** @var list<array{query: string, time: float, memory: int, timestamp: float}> */
    private array $queries = [];

    private int $cacheHits = 0;

    private int $cacheMisses = 0;

    /** @var list<array<string, mixed>> */
    private array $slowQueries = [];

    /** @var array<string, int> */
    private array $memoryUsage = [];

    /** @var array<string, list<array<string, mixed>>> */
    private array $endpointTimes = [];

    private const SLOW_QUERY_THRESHOLD = 1.0;

    public function __construct()
    {
        $this->startTime = microtime(true);
        $this->memoryUsage['start'] = memory_get_usage(true);

        \add_filter('query', [$this, 'monitorQuery'], 10, 1);
    }

    /**
     * Monitor individual database queries.
     *
     * @param string $query SQL query string.
     */
    public function monitorQuery(string $query): string
    {
        $start = microtime(true);

        $result = $query;

        $executionTime = microtime(true) - $start;

        $this->queries[] = [
            'query' => $query,
            'time' => $executionTime,
            'memory' => memory_get_usage(true),
            'timestamp' => microtime(true),
        ];

        if ($executionTime > self::SLOW_QUERY_THRESHOLD) {
            $this->slowQueries[] = [
                'query' => $query,
                'time' => $executionTime,
                'memory' => memory_get_usage(true),
                'backtrace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5),
            ];
        }

        return $result;
    }

    /**
     * Track cache performance.
     */
    public function trackCache(bool $hit = true): void
    {
        if ($hit) {
            $this->cacheHits++;
            return;
        }

        $this->cacheMisses++;
    }

    /**
     * Track endpoint performance.
     */
    public function trackEndpoint(string $endpoint, ?float $startTime = null): void
    {
        $startedAt = $startTime ?? microtime(true);
        $executionTime = microtime(true) - $startedAt;

        if (!isset($this->endpointTimes[$endpoint])) {
            $this->endpointTimes[$endpoint] = [];
        }

        $this->endpointTimes[$endpoint][] = [
            'time' => $executionTime,
            'memory' => memory_get_usage(true),
            'timestamp' => microtime(true),
        ];
    }

    /**
     * Get performance report.
     *
     * @return array<string, mixed>
     */
    public function getPerformanceReport(): array
    {
        $this->memoryUsage['end'] = memory_get_usage(true);
        $totalTime = microtime(true) - $this->startTime;

        return [
            'summary' => [
                'total_time' => round($totalTime, 4),
                'memory_peak' => round(memory_get_peak_usage(true) / 1024 / 1024, 2) . 'MB',
                'memory_usage' => round(($this->memoryUsage['end'] - $this->memoryUsage['start']) / 1024 / 1024, 2) . 'MB',
                'total_queries' => count($this->queries),
                'slow_queries' => count($this->slowQueries),
                'cache_hit_rate' => $this->getCacheHitRate(),
                'average_query_time' => $this->getAverageQueryTime(),
            ],
            'slow_queries' => $this->slowQueries,
            'endpoint_performance' => $this->getEndpointPerformance(),
            'recommendations' => $this->getRecommendations(),
        ];
    }

    public function getCacheHitRate(): float
    {
        $total = $this->cacheHits + $this->cacheMisses;
        if ($total === 0) {
            return 0.0;
        }

        return round(($this->cacheHits / $total) * 100, 2);
    }

    public function getAverageQueryTime(): float
    {
        if ($this->queries === []) {
            return 0.0;
        }

        $totalTime = array_sum(array_column($this->queries, 'time'));

        return round($totalTime / count($this->queries), 4);
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function getSlowQueries(): array
    {
        return $this->slowQueries;
    }

    /**
     * @return array<string, array{calls: int, avg_time: float, max_time: float, min_time: float}>
     */
    private function getEndpointPerformance(): array
    {
        $performance = [];

        foreach ($this->endpointTimes as $endpoint => $times) {
            $avgTime = array_sum(array_column($times, 'time')) / count($times);
            $maxTime = max(array_column($times, 'time'));
            $minTime = min(array_column($times, 'time'));

            $performance[$endpoint] = [
                'calls' => count($times),
                'avg_time' => round($avgTime, 4),
                'max_time' => round($maxTime, 4),
                'min_time' => round($minTime, 4),
            ];
        }

        return $performance;
    }

    /**
     * @return list<array{type: string, message: string, action: string}>
     */
    private function getRecommendations(): array
    {
        $recommendations = [];

        if ($this->slowQueries !== []) {
            $recommendations[] = [
                'type' => 'warning',
                'message' => 'Found ' . count($this->slowQueries) . ' slow queries (>1s)',
                'action' => 'Optimize database queries and add indexes',
            ];
        }

        $hitRate = $this->getCacheHitRate();
        if ($hitRate < 80) {
            $recommendations[] = [
                'type' => 'info',
                'message' => 'Cache hit rate is ' . $hitRate . '% (target: >80%)',
                'action' => 'Review cache TTLs and invalidation strategy',
            ];
        }

        $endMemory = $this->memoryUsage['end'] ?? memory_get_usage(true);
        $memoryUsageMb = ($endMemory - $this->memoryUsage['start']) / 1024 / 1024;
        if ($memoryUsageMb > 50) {
            $recommendations[] = [
                'type' => 'warning',
                'message' => 'High memory usage: ' . round($memoryUsageMb, 2) . 'MB',
                'action' => 'Optimize memory usage and consider pagination',
            ];
        }

        if (count($this->queries) > 50) {
            $recommendations[] = [
                'type' => 'warning',
                'message' => 'High query count: ' . count($this->queries),
                'action' => 'Consider query optimization and caching',
            ];
        }

        return $recommendations;
    }

    /**
     * @return array<string, mixed>|null
     */
    public function analyzeEndpoint(string $endpoint): ?array
    {
        if (!isset($this->endpointTimes[$endpoint])) {
            return null;
        }

        $times = $this->endpointTimes[$endpoint];
        $avgTime = array_sum(array_column($times, 'time')) / count($times);

        return [
            'endpoint' => $endpoint,
            'calls' => count($times),
            'avg_time' => round($avgTime, 4),
            'max_time' => round(max(array_column($times, 'time')), 4),
            'min_time' => round(min(array_column($times, 'time')), 4),
            'performance_grade' => $this->getPerformanceGrade($avgTime),
        ];
    }

    private function getPerformanceGrade(float $avgTime): string
    {
        $grade = 'F';

        if ($avgTime < 0.1) {
            $grade = 'A+';
        } elseif ($avgTime < 0.3) {
            $grade = 'A';
        } elseif ($avgTime < 0.5) {
            $grade = 'B';
        } elseif ($avgTime < 1.0) {
            $grade = 'C';
        } elseif ($avgTime < 2.0) {
            $grade = 'D';
        }

        return $grade;
    }

    /**
     * @return array<string, mixed>
     */
    public function exportDetailedReport(): array
    {
        $report = $this->getPerformanceReport();

        $report['detailed_queries'] = [];
        foreach ($this->queries as $query) {
            $report['detailed_queries'][] = [
                'query' => substr($query['query'], 0, 200) . '...',
                'time' => $query['time'],
                'memory' => $query['memory'],
                'timestamp' => $query['timestamp'],
            ];
        }

        return $report;
    }

    public function reset(): void
    {
        $this->queries = [];
        $this->cacheHits = 0;
        $this->cacheMisses = 0;
        $this->slowQueries = [];
        $this->memoryUsage = [];
        $this->endpointTimes = [];
        $this->startTime = microtime(true);
        $this->memoryUsage['start'] = memory_get_usage(true);
    }

    /**
     * Record cache operation performance.
     */
    public function recordCacheOperation(string $key, float $executionTime, string $operation = 'get'): void
    {
        if (!isset($this->endpointTimes['cache_operations'])) {
            $this->endpointTimes['cache_operations'] = [];
        }

        $this->endpointTimes['cache_operations'][] = [
            'key' => $key,
            'operation' => $operation,
            'time' => $executionTime,
            'memory' => memory_get_usage(true),
            'timestamp' => microtime(true),
        ];

        if ($executionTime > 0.1) {
            $this->slowQueries[] = [
                'query' => "Cache {$operation}: {$key}",
                'time' => $executionTime,
                'memory' => memory_get_usage(true),
                'type' => 'cache_operation',
            ];
        }
    }
}
