<?php

declare(strict_types=1);

namespace Archive\Archived;

use App\Framework\Cache\Cache;
use App\Framework\View\DomWrapper;
use App\Framework\View\Loading\TemplateLoader;
use App\Framework\View\RenderContext;
use App\Framework\View\TemplateProcessor;
use App\Framework\View\TemplateRenderer;

final class SmartCacheEngine implements TemplateRenderer
{
    private array $cacheStats = [
        'hits' => 0,
        'misses' => 0,
        'renders' => 0,
        'cached_templates' => [],
    ];

    public function __construct(
        private readonly TemplateLoader       $loader,
        private readonly TemplateAnalyzer     $analyzer,
        private readonly FragmentCacheManager $fragmentCache,
        private readonly Cache                $cache,
        private readonly TemplateProcessor    $processor = new TemplateProcessor(),
        private readonly bool                 $cacheEnabled = true,
        private readonly bool                 $debugMode = true,
        private readonly TemplatePreprocessor $preprocessor = new TemplatePreprocessor(),
    ) {
        dd('oldcacheengine');
    }

    public function render(RenderContext $context): string
    {
        $this->cacheStats['renders']++;

        if (!$this->cacheEnabled) {
            $this->logDebug("Cache disabled - rendering without cache", $context);
            return $this->renderWithoutCache($context);
        }

        $startTime = microtime(true);

        // Lade und analysiere Template
        $template = $this->loader->load($context->template, $context->controllerClass, $context);

        // Preprocessing für optimierte Cache-Keys
        $normalizedTemplate = $this->preprocessor->normalizeTemplate($template, $context);
        $cacheableBlocks = $this->preprocessor->extractCacheableBlocks($template);

        $analysis = $this->analyzer->analyzeCacheability($normalizedTemplate, $context);

        // Erweitere Analysis mit extrahierten Blöcken
        foreach ($cacheableBlocks as $block) {
            if ($block['type'] === 'explicit') {
                $analysis->addStaticFragment($block['id']);
            }
        }

        $this->logDebug("Template loaded, length: " . strlen($template) . " chars", $context);
        $this->logDebug("Cache strategy: {$analysis->cacheStrategy->value}, TTL: {$analysis->ttl}s", $context);
        $this->logDebug("Cacheable blocks found: " . count($cacheableBlocks), $context);

        $result = match($analysis->cacheStrategy) {
            CacheStrategy::STATIC => $this->renderStaticCached($context, $template, $analysis),
            CacheStrategy::PARTIAL => $this->renderPartialCached($context, $template, $analysis),
            CacheStrategy::FRAGMENT => $this->renderFragmentCached($context, $template, $analysis),
            CacheStrategy::DYNAMIC => $this->processor->render($context, $template),
            CacheStrategy::DISABLED => $this->renderWithoutCache($context),
        };

        $renderTime = microtime(true) - $startTime;
        $this->logCachePerformance($context, $analysis, $renderTime);

        return $result;
    }

    private function renderStaticCached(RenderContext $context, string $template, CacheabilityInfo $analysis): string
    {
        $cacheKey = $this->buildStaticCacheKey($context, $analysis);

        // Prüfe zuerst, ob bereits im Cache
        $wasInCache = $this->cache->has($cacheKey);

        // Nutze remember-Pattern
        $cacheItem = $this->cache->remember(
            $cacheKey,
            function() use ($context, $template) {
                $this->logDebug("CACHE MISS - Generating new content", $context);
                $this->cacheStats['misses']++;
                return $this->processor->render($context, $template);
            },
            $analysis->ttl
        );

        if ($wasInCache) {
            $this->logDebug("CACHE HIT - Using cached content", $context);
            $this->cacheStats['hits']++;
        }

        $this->cacheStats['cached_templates'][$context->template] = [
            'strategy' => 'static',
            'key' => $cacheKey,
            'ttl' => $analysis->ttl,
            'hit' => $wasInCache
        ];

        return $cacheItem->value;
    }

    private function renderPartialCached(RenderContext $context, string $template, CacheabilityInfo $analysis): string
    {
        $cacheKey = $this->buildPartialCacheKey($context, $analysis);
        $wasInCache = $this->cache->has($cacheKey);

        $cacheItem = $this->cache->remember(
            $cacheKey,
            function() use ($context, $template) {
                $this->cacheStats['misses']++;
                return $this->processor->render($context, $template);
            },
            $analysis->ttl
        );

        if ($wasInCache) {
            $this->cacheStats['hits']++;
        }

        return $cacheItem->value;
    }

    private function renderFragmentCached(RenderContext $context, string $template, CacheabilityInfo $analysis): string
    {
        // Wenn keine Fragmente erkannt wurden, verwende ein Template-weites Fragment
        if (empty($analysis->staticFragments) && empty($analysis->dynamicFragments)) {
            $this->logDebug("No fragments found - creating template-wide fragment", $context);

            $templateFragmentKey = "template_{$context->template}";
            $wasInCache = $this->fragmentCache->hasFragment($templateFragmentKey, $analysis->dependencies ?? [], $analysis->ttl);

            if ($wasInCache) {
                $this->logDebug("FRAGMENT HIT - Using cached template", $context);
                $this->cacheStats['hits']++;
                $content = $this->fragmentCache->getFragment($templateFragmentKey, $analysis->dependencies ?? []);
            } else {
                $this->logDebug("FRAGMENT MISS - Generating new template content", $context);
                $this->cacheStats['misses']++;
                $content = $this->fragmentCache->cacheFragment(
                    $templateFragmentKey,
                    function() use ($context, $template) {
                        return $this->processor->render($context, $template);
                    },
                    $analysis->dependencies ?? [],
                    $analysis->ttl
                );
            }

            $this->cacheStats['cached_templates'][$context->template] = [
                'strategy' => 'fragment',
                'key' => $templateFragmentKey,
                'ttl' => $analysis->ttl,
                'hit' => $wasInCache
            ];

                // Request-spezifische Statistiken
                $hitRate = $this->cacheStats['hits'] + $this->cacheStats['misses'] > 0
                    ? round(($this->cacheStats['hits'] / ($this->cacheStats['hits'] + $this->cacheStats['misses'])) * 100, 1)
                    : 0;

                error_log("SmartCache Request Stats: Hits: " . $this->cacheStats['hits'] . ", Misses: " . $this->cacheStats['misses'] . ", Hit Rate: {$hitRate}%");

            return $content;
        } else {
            $this->logDebug("Found " . count($analysis->staticFragments) . " static fragments and " . count($analysis->dynamicFragments) . " dynamic fragments", $context);
        }

        // Verarbeite explizite Fragmente
        $finalContent = '';

        foreach ($analysis->staticFragments as $fragmentId) {
            $wasInCache = $this->fragmentCache->hasFragment($fragmentId, $analysis->dependencies ?? [], $analysis->ttl);

            if ($wasInCache) {
                $this->logDebug("FRAGMENT HIT - Using cached fragment: {$fragmentId}", $context);
                $this->cacheStats['hits']++;
                // Direkt aus Cache holen ohne Generator zu verwenden
                $fragmentContent = $this->fragmentCache->getFragment($fragmentId, $analysis->dependencies ?? []);
            } else {
                $this->logDebug("FRAGMENT MISS - Generating fragment: {$fragmentId}", $context);
                $this->cacheStats['misses']++;
                $fragmentContent = $this->fragmentCache->cacheFragment(
                    $fragmentId,
                    function() use ($fragmentId, $template, $context) {
                        return $this->renderFragmentFromTemplate($fragmentId, $template, $context);
                    },
                    $analysis->dependencies ?? [],
                    $analysis->ttl
                );
            }

            $finalContent .= $fragmentContent;
        }

        // Rendere dynamische Fragmente ohne Caching
        foreach ($analysis->dynamicFragments as $fragmentId) {
            $fragmentContent = $this->renderFragmentFromTemplate($fragmentId, $template, $context);
            $finalContent .= $fragmentContent;
        }

        return $finalContent ?: $this->processor->render($context, $template);
    }

    private function renderFragmentFromTemplate(string $fragmentId, string $template, RenderContext $context): string
    {
        // Für jetzt: Wenn es ein Template-Fragment ist, rendere das komplette Template
        if (str_starts_with($fragmentId, 'template_')) {
            return $this->processor->render($context, $template);
        }

        // TODO: Hier könnten wir später spezifische Fragment-Extraktion implementieren
        // Für jetzt verwende DOM-Wrapper für Fragment-Extraktion
        try {
            $domWrapper = DomWrapper::fromString($template);
            return $this->renderFragment($fragmentId, $domWrapper, $context);
        } catch (\Exception $e) {
            $this->logDebug("Fragment rendering failed for {$fragmentId}: " . $e->getMessage(), $context);
            // Fallback: Rendere komplettes Template
            return $this->processor->render($context, $template);
        }
    }

    private function renderWithoutCache(RenderContext $context): string
    {
        $template = $this->loader->load($context->template, $context->controllerClass, $context);
        return $this->processor->render($context, $template);
    }

    private function renderFragment(string $fragmentId, DomWrapper $domWrapper, RenderContext $context): string
    {
        // Vereinfachte Fragment-Rendering-Logik
        // In einer vollständigen Implementierung würde hier das spezifische Fragment extrahiert
        $elements = $domWrapper->getElementsByAttribute('data-fragment-id', $fragmentId);

        if (count($elements) > 0) {
            $element = $elements[0];
            return $domWrapper->document->saveHTML($element);
        }

        return '';
    }

    private function extractStaticContent(string $content, CacheabilityInfo $analysis): string
    {
        // Entferne dynamische Platzhalter für statisches Caching
        $staticContent = $content;

        // Ersetze dynamische Teile mit Platzhaltern
        $staticContent = preg_replace('/\{\{[^}]+}}/', '{{DYNAMIC_PLACEHOLDER}}', $staticContent);

        return $staticContent;
    }

    private function buildStaticCacheKey(RenderContext $context, $analysis): string
    {
        $dependencies = is_object($analysis) && method_exists($analysis, 'dependencies')
            ? $analysis->dependencies
            : ($analysis->dependencies ?? []);

        $optimizedKey = $this->preprocessor->generateOptimizedCacheKey($context, $dependencies);

        return "static:{$optimizedKey}";
    }

    private function buildPartialCacheKey(RenderContext $context, $analysis): string
    {
        $dependencies = is_object($analysis) && method_exists($analysis, 'dependencies')
            ? $analysis->dependencies
            : ($analysis->dependencies ?? []);

        $optimizedKey = $this->preprocessor->generateOptimizedCacheKey($context, $dependencies);

        return "partial:{$optimizedKey}";
    }


    public function getCacheStats(): array
    {
        $hitRate = $this->cacheStats['hits'] + $this->cacheStats['misses'] > 0
            ? round(($this->cacheStats['hits'] / ($this->cacheStats['hits'] + $this->cacheStats['misses'])) * 100, 2)
            : 0;

        return [
            'smart_cache' => [
                'enabled' => $this->cacheEnabled,
                'debug_mode' => $this->debugMode,
                'cache_interface' => get_class($this->cache),
                'performance' => [
                    'total_renders' => $this->cacheStats['renders'],
                    'cache_hits' => $this->cacheStats['hits'],
                    'cache_misses' => $this->cacheStats['misses'],
                    'hit_rate' => $hitRate . '%',
                ],
                'cached_templates' => $this->cacheStats['cached_templates'],
                'fragment_stats' => $this->fragmentCache->getFragmentStats(),
            ]
        ];
    }


    public function invalidateCache(?string $template = null): int
    {
        $invalidated = 0;

        if ($template) {
            // Invalidiere spezifisches Template
            $patterns = [
                "static:{$template}:",
                "partial:{$template}:",
                "template_{$template}",
            ];

            foreach ($patterns as $pattern) {
                // Vereinfachte Implementierung - da das Cache-Interface keine Pattern-Suche unterstützt
                // könnte hier eine erweiterte Lösung implementiert werden
                $invalidated++;
            }
        } else {
            // Alle Caches löschen und Stats zurücksetzen
            if ($this->cache->clear()) {
                $this->resetStats();
                $invalidated = 1;
            }
        }

        return $invalidated;
    }

    public function resetStats(): void
    {
        $this->cacheStats = [
            'hits' => 0,
            'misses' => 0,
            'renders' => 0,
            'cached_templates' => [],
        ];
    }

    public function warmupCache(array $templates = []): array
    {
        if (!$this->warmer) {
            return ['error' => 'CacheWarmer not configured'];
        }

        return $this->warmer->warmupTemplates($templates);
    }

    public function addPopularTemplate(string $template, array $data = []): void
    {
        if ($this->warmer) {
            $this->warmer->addPopularTemplate($template, $data);
        }
    }

    public function diagnoseCaching(): void
    {
        echo "\n=== CACHE DIAGNOSTICS ===\n";
        echo "Smart Cache Enabled: " . ($this->cacheEnabled ? 'YES' : 'NO') . "\n";
        echo "Cache Interface: " . get_class($this->cache) . "\n";
        echo "Debug Mode: " . ($this->debugMode ? 'YES' : 'NO') . "\n";
        echo "Total Renders: " . $this->cacheStats['renders'] . "\n";
        echo "Cache Hits: " . $this->cacheStats['hits'] . "\n";
        echo "Cache Misses: " . $this->cacheStats['misses'] . "\n";

        $hitRate = $this->cacheStats['hits'] + $this->cacheStats['misses'] > 0
            ? round(($this->cacheStats['hits'] / ($this->cacheStats['hits'] + $this->cacheStats['misses'])) * 100, 2)
            : 0;
        echo "Hit Rate: " . $hitRate . "%\n";

        echo "\n=== ANALYSIS ===\n";
        if ($this->cacheStats['hits'] === 0 && $this->cacheStats['misses'] === 0) {
            echo "⚠️  PROBLEM: No cache operations detected!\n";
            echo "   This suggests that templates are being rendered with CacheStrategy::DYNAMIC or CacheStrategy::DISABLED\n";
            echo "   Check the TemplateAnalyzer logic to ensure templates are being marked as cacheable.\n";
        }

        echo "========================\n";
    }

    private function logCachePerformance(RenderContext $context, CacheabilityInfo $analysis, float $renderTime): void
    {
        if (!$this->debugMode) {
            return;
        }

        $logData = [
            'template' => $context->template,
            'strategy' => $analysis->cacheStrategy->value,
            'render_time' => round($renderTime * 1000, 2) . 'ms',
            'complexity' => $analysis->getCacheComplexity(),
            'cacheable' => $analysis->cacheStrategy->shouldCache(),
                'request_hits' => $this->cacheStats['hits'],
                'request_misses' => $this->cacheStats['misses'],
            'static_fragments' => count($analysis->staticFragments),
            'dynamic_fragments' => count($analysis->dynamicFragments),
            'ttl' => $analysis->ttl,
        ];

        error_log('SmartCache Performance: ' . json_encode($logData));

        // Zusätzliche Fragment-Details wenn Fragment-Strategy
        if ($analysis->cacheStrategy === CacheStrategy::FRAGMENT) {
            error_log('SmartCache Fragments: static=[' . implode(',', $analysis->staticFragments) .
                     '] dynamic=[' . implode(',', $analysis->dynamicFragments) . ']');
        }
    }

    private function logDebug(string $message, RenderContext $context): void
    {
        if (!$this->debugMode) {
            return;
        }

        error_log("SmartCache DEBUG [{$context->template}]: {$message}");
    }
}
