<?php

declare(strict_types=1);

namespace App\Framework\Performance;

use App\Framework\DateTime\Clock;
use App\Framework\DateTime\SystemClock;
use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\Contracts\PerformanceReporterInterface;
use App\Framework\Performance\ValueObjects\CategoryMetrics;
use App\Framework\Performance\ValueObjects\PerformanceReport;
use App\Framework\Performance\ValueObjects\PerformanceSummary;
use RuntimeException;

final readonly class PerformanceReporter implements PerformanceReporterInterface
{
    private MemoryMonitor $memoryMonitor;

    private Clock $clock;

    public function __construct(
        private PerformanceCollectorInterface $collector,
        #private PerformanceConfig                      $config
        ?Clock $clock = null
    ) {
        $this->memoryMonitor = new MemoryMonitor();
        $this->clock = $clock ?? new SystemClock();
    }

    public function generateReport(string $format = 'array'): string|array
    {
        $report = $this->collectReportData();

        return match ($format) {
            'json' => json_encode($report->toArray(), JSON_PRETTY_PRINT) ?: '{}',
            'html' => $this->generateHtmlReport($report),
            'text' => $this->generateTextReport($report),
            'array' => $report->toArray(),
            default => throw new \InvalidArgumentException("Unsupported format: {$format}"),
        };
    }

    private function collectReportData(): PerformanceReport
    {
        $metrics = $this->collector->getMetrics();
        $memorySummary = $this->memoryMonitor->getSummary();

        // Create performance summary
        $summary = PerformanceSummary::fromRawValues(
            totalRequestTimeMs: round($this->collector->getTotalRequestTime(), 2),
            totalRequestMemoryBytes: $this->collector->getTotalRequestMemory(),
            peakMemoryBytes: $this->collector->getPeakMemory(),
            metricsCount: count($metrics),
            memorySummary: $memorySummary
        );

        // Group metrics by category
        $categorizedMetrics = [];
        foreach ($metrics as $metric) {
            $category = $metric->getCategory()->value;
            $categorizedMetrics[$category][] = $metric;
        }

        // Process each category
        $categories = [];
        foreach ($categorizedMetrics as $categoryName => $categoryMetrics) {
            $categories[$categoryName] = CategoryMetrics::fromMetrics($categoryMetrics);
        }

        // Add individual metrics
        $metricsArray = [];
        foreach ($metrics as $metric) {
            $metricsArray[$metric->getKey()] = $metric->toArray();
        }

        return new PerformanceReport(
            timestamp: $this->clock->time(),
            summary: $summary,
            categories: $categories,
            metrics: $metricsArray
        );
    }

    private function generateHtmlReport(PerformanceReport $report): string
    {
        $html = '<div style="font-family: monospace; font-size: 12px; background: #f8f9fa; padding: 15px; border-radius: 5px; max-width: 1200px;">';

        // Header
        $html .= '<h3 style="margin: 0 0 15px 0; color: #333;">🚀 Performance Report</h3>';
        $html .= '<p style="margin: 0 0 15px 0; color: #666;">Generated at: ' . $report->timestamp->format('Y-m-d H:i:s') . '</p>';

        // Summary
        $summary = $report->summary;
        $memorySummary = $summary->memorySummary;
        $html .= '<div style="background: white; padding: 10px; margin-bottom: 15px; border-radius: 3px; border-left: 4px solid #007bff;">';
        $html .= '<h4 style="margin: 0 0 10px 0;">📊 Summary</h4>';
        $html .= '<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px;">';
        $html .= '<div><strong>Request Time:</strong> ' . $summary->getFormattedRequestTime() . '</div>';
        $html .= '<div><strong>Memory Usage:</strong> ' . $memorySummary->getCurrentHumanReadable() . '</div>';
        $html .= '<div><strong>Peak Memory:</strong> ' . $memorySummary->getPeakHumanReadable() . '</div>';
        $html .= '<div><strong>Memory Limit:</strong> ' . $memorySummary->getLimitHumanReadable() . '</div>';
        $html .= '<div><strong>Memory Available:</strong> ' . $memorySummary->getAvailableMemory()->toHumanReadable() . '</div>';
        $html .= '<div><strong>Usage:</strong> ' . $memorySummary->getUsagePercentageFormatted() . '</div>';
        $html .= '<div><strong>Metrics Count:</strong> ' . $summary->metricsCount . '</div>';
        $html .= '</div></div>';

        // Categories
        if (! empty($report->categories)) {
            $html .= '<div style="background: white; padding: 10px; margin-bottom: 15px; border-radius: 3px; border-left: 4px solid #28a745;">';
            $html .= '<h4 style="margin: 0 0 10px 0;">📂 Categories</h4>';
            $html .= '<table style="width: 100%; border-collapse: collapse;">';
            $html .= '<thead><tr style="background: #f8f9fa;">';
            $html .= '<th style="padding: 8px; text-align: left; border-bottom: 1px solid #dee2e6;">Category</th>';
            $html .= '<th style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">Metrics</th>';
            $html .= '<th style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">Total Time (ms)</th>';
            $html .= '<th style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">Total Calls</th>';
            $html .= '<th style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">Avg Time (ms)</th>';
            $html .= '</tr></thead><tbody>';

            foreach ($report->categories as $categoryName => $categoryData) {
                $html .= '<tr>';
                $html .= '<td style="padding: 8px; border-bottom: 1px solid #dee2e6;">' . htmlspecialchars($categoryName) . '</td>';
                $html .= '<td style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">' . $categoryData->metricsCount . '</td>';
                $html .= '<td style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">' . round($categoryData->totalTime->toMilliseconds(), 2) . '</td>';
                $html .= '<td style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">' . $categoryData->totalCalls . '</td>';
                $html .= '<td style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">' . round($categoryData->averageTime->toMilliseconds(), 2) . '</td>';
                $html .= '</tr>';
            }

            $html .= '</tbody></table></div>';
        }

        // Top metrics by time
        $topMetrics = $report->getTopMetricsByTime(10);
        if (! empty($topMetrics)) {
            $html .= '<div style="background: white; padding: 10px; margin-bottom: 15px; border-radius: 3px; border-left: 4px solid #ffc107;">';
            $html .= '<h4 style="margin: 0 0 10px 0;">⏱️ Slowest Operations</h4>';
            $html .= '<table style="width: 100%; border-collapse: collapse;">';
            $html .= '<thead><tr style="background: #f8f9fa;">';
            $html .= '<th style="padding: 8px; text-align: left; border-bottom: 1px solid #dee2e6;">Metric</th>';
            $html .= '<th style="padding: 8px; text-align: left; border-bottom: 1px solid #dee2e6;">Category</th>';
            $html .= '<th style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">Total Time (ms)</th>';
            $html .= '<th style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">Avg Time (ms)</th>';
            $html .= '<th style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">Count</th>';
            $html .= '</tr></thead><tbody>';

            foreach ($topMetrics as $metricData) {
                $measurements = $metricData['measurements'];
                $html .= '<tr>';
                $html .= '<td style="padding: 8px; border-bottom: 1px solid #dee2e6;">' . htmlspecialchars($metricData['key']) . '</td>';
                $html .= '<td style="padding: 8px; border-bottom: 1px solid #dee2e6;">' . htmlspecialchars($metricData['category']) . '</td>';
                $html .= '<td style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">' . $measurements['total_duration_ms'] . '</td>';
                $html .= '<td style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">' . $measurements['avg_duration_ms'] . '</td>';
                $html .= '<td style="padding: 8px; text-align: right; border-bottom: 1px solid #dee2e6;">' . $metricData['count'] . '</td>';
                $html .= '</tr>';
            }

            $html .= '</tbody></table></div>';
        }

        $html .= '</div>';

        return $html;
    }

    private function generateTextReport(PerformanceReport $report): string
    {
        $text = "=== Performance Report ===\n";
        $text .= "Generated at: {$report->timestamp->format('Y-m-d H:i:s')}\n\n";

        // Summary
        $summary = $report->summary;
        $memorySummary = $summary->memorySummary;
        $text .= "=== Summary ===\n";
        $text .= sprintf("Request Time: %s\n", $summary->getFormattedRequestTime());
        $text .= sprintf("Memory Usage: %s\n", $memorySummary->getCurrentHumanReadable());
        $text .= sprintf("Peak Memory: %s\n", $memorySummary->getPeakHumanReadable());
        $text .= sprintf("Memory Limit: %s\n", $memorySummary->getLimitHumanReadable());
        $text .= sprintf("Memory Available: %s\n", $memorySummary->getAvailableMemory()->toHumanReadable());
        $text .= sprintf("Memory Usage: %s\n", $memorySummary->getUsagePercentageFormatted());
        $text .= sprintf("Metrics Count: %d\n\n", $summary->metricsCount);

        // Categories
        if (! empty($report->categories)) {
            $text .= "=== Categories ===\n";
            $text .= sprintf("%-20s %10s %15s %12s %15s\n", "Category", "Metrics", "Total Time(ms)", "Total Calls", "Avg Time(ms)");
            $text .= str_repeat("-", 75) . "\n";

            foreach ($report->categories as $categoryName => $categoryData) {
                $text .= sprintf(
                    "%-20s %10d %15.2f %12d %15.2f\n",
                    $categoryName,
                    $categoryData->metricsCount,
                    $categoryData->totalTime->toMilliseconds(),
                    $categoryData->totalCalls,
                    $categoryData->averageTime->toMilliseconds()
                );
            }
            $text .= "\n";
        }

        // Top metrics
        $topMetrics = $report->getTopMetricsByTime(5);
        if (! empty($topMetrics)) {
            $text .= "=== Slowest Operations ===\n";
            foreach ($topMetrics as $metricData) {
                $measurements = $metricData['measurements'];
                $text .= sprintf(
                    "%s (%s): %.2f ms total, %.2f ms avg, %d calls\n",
                    $metricData['key'],
                    $metricData['category'],
                    $measurements['total_duration_ms'],
                    $measurements['avg_duration_ms'],
                    $metricData['count']
                );
            }
        }

        return $text;
    }

    public function getSummary(): array
    {
        $report = $this->collectReportData();

        return $report->summary->toArray();
    }

    public function getTopMetricsByTime(int $limit = 10): array
    {
        $report = $this->collectReportData();

        return $report->getTopMetricsByTime($limit);
    }

    public function getTopMetricsByMemory(int $limit = 10): array
    {
        $report = $this->collectReportData();

        return $report->getTopMetricsByMemory($limit);
    }

    public function getMetricsByCategory(): array
    {
        $report = $this->collectReportData();

        $result = [];
        foreach ($report->categories as $name => $category) {
            $result[$name] = $category->toArray();
        }

        return $result;
    }

    public function exportToFile(FilePath $filepath, string $format = 'json'): void
    {
        $report = $this->generateReport($format);

        if (is_array($report)) {
            $report = json_encode($report, JSON_PRETTY_PRINT);
        }

        if (! is_string($report)) {
            throw new RuntimeException('Report must be a string for file export');
        }

        $directory = $filepath->getDirectory();
        if (! $directory->exists() && ! mkdir($directory->toString(), 0755, true)) {
            throw new RuntimeException('Failed to create directory: ' . $directory);
        }

        if (file_put_contents($filepath->toString(), $report) === false) {
            throw new RuntimeException('Failed to write report to file: ' . $filepath);
        }
    }
}
