<?php

declare(strict_types=1);

namespace App\Framework\Database\Cache;

use App\Framework\Cache\Cache;

final class CacheAdapterStrategy implements CacheStrategy
{
    private array $stats;

    public function __construct(
        private readonly Cache $cache,
        private readonly string $keyPrefix = 'db_query:'
    ) {
        $this->stats = [
            'hits' => 0,
            'misses' => 0,
            'sets' => 0,
            'deletes' => 0,
            'invalidations' => 0,
        ];
    }

    public function set(QueryCacheKey $key, array $value, int $ttlSeconds): bool
    {
        $fullKey = $this->keyPrefix . $key->toString();

        try {
            $success = $this->cache->set($fullKey, $value, $ttlSeconds);

            if ($success) {
                $this->stats['sets']++;
            }

            return $success;
        } catch (\Throwable) {
            return false;
        }
    }

    public function get(QueryCacheKey $key): ?array
    {
        $fullKey = $this->keyPrefix . $key->toString();

        try {
            $value = $this->cache->get($fullKey);

            if ($value === null) {
                $this->stats['misses']++;

                return null;
            }

            $this->stats['hits']++;

            return $value->value;
        } catch (\Throwable) {
            $this->stats['misses']++;

            return null;
        }
    }

    public function has(QueryCacheKey $key): bool
    {
        $fullKey = $this->keyPrefix . $key->toString();

        try {
            return $this->cache->has($fullKey);
        } catch (\Throwable) {
            return false;
        }
    }

    public function delete(QueryCacheKey $key): bool
    {
        $fullKey = $this->keyPrefix . $key->toString();

        try {
            $success = $this->cache->forget($fullKey);

            if ($success) {
                $this->stats['deletes']++;
            }

            return $success;
        } catch (\Throwable) {
            return false;
        }
    }

    public function invalidatePattern(string $pattern): int
    {
        // Fallback für Cache-Implementierungen ohne Pattern-Support
        if (! method_exists($this->cache, 'deleteByPattern')) {
            // Für einfache Caches: kompletter Clear bei Pattern-Invalidierung
            $this->clear();

            return 1; // Unbekannte Anzahl, also 1 als Indikator
        }

        try {
            $searchPattern = $this->keyPrefix . '*' . $pattern . '*';
            $deleted = $this->cache->deleteByPattern($searchPattern);
            $this->stats['invalidations'] += $deleted;

            return $deleted;
        } catch (\Throwable) {
            return 0;
        }
    }

    public function clear(): void
    {
        try {
            // Wenn Cache Tagged-Cache unterstützt, nutze Tags
            if (method_exists($this->cache, 'deleteByTag')) {
                $deleted = $this->cache->deleteByTag('database_query');
                $this->stats['invalidations'] += $deleted;

                return;
            }

            // Wenn Pattern-Deletion unterstützt wird
            if (method_exists($this->cache, 'deleteByPattern')) {
                $deleted = $this->cache->deleteByPattern($this->keyPrefix . '*');
                $this->stats['invalidations'] += $deleted;

                return;
            }

            // Fallback: Ganzer Cache wird geleert (nicht ideal)
            if (method_exists($this->cache, 'clear')) {
                $this->cache->clear();
                $this->stats['invalidations']++;
            }
        } catch (\Throwable) {
            // Ignoriere Fehler bei Cache-Clear
        }
    }

    public function getStats(): array
    {
        $baseStats = $this->stats;

        // Erweitere um Framework-Cache-spezifische Stats falls verfügbar
        if (method_exists($this->cache, 'getStats')) {
            try {
                $cacheStats = $this->cache->getStats();
                $baseStats = array_merge($baseStats, [
                    'cache_stats' => $cacheStats,
                    'cache_type' => get_class($this->cache),
                ]);
            } catch (\Throwable) {
                // Ignoriere Fehler bei Stats-Abruf
            }
        }

        // Hit Ratio berechnen
        $baseStats['hit_ratio'] = $this->calculateHitRatio();

        return $baseStats;
    }

    private function calculateHitRatio(): float
    {
        $total = $this->stats['hits'] + $this->stats['misses'];

        return $total > 0 ? ($this->stats['hits'] / $total) : 0.0;
    }

    /**
     * Factory-Methode für TaggedCache wenn verfügbar
     */
    public static function withTags(Cache $cache, array $tags = ['database_query'], string $keyPrefix = 'db_query:'): self
    {
        // Wenn TaggedCache verfügbar ist, nutze es
        if (method_exists($cache, 'tags')) {
            $taggedCache = $cache->tags($tags);

            return new self($taggedCache, $keyPrefix);
        }

        return new self($cache, $keyPrefix);
    }

    /**
     * Hilfsmethode für Cache-Tagging
     */
    private function setWithTags(string $key, array $value, int $ttl, array $tags = []): bool
    {
        if (method_exists($this->cache, 'setWithTags')) {
            return $this->cache->setWithTags($key, $value, $ttl, array_merge(['database_query'], $tags));
        }

        return $this->cache->set($key, $value, $ttl);
    }

    /**
     * Invalidierung basierend auf Tabellen-Tags
     */
    public function invalidateByTable(string $tableName): int
    {
        if (method_exists($this->cache, 'deleteByTag')) {
            try {
                return $this->cache->deleteByTag("table:{$tableName}");
            } catch (\Throwable) {
                // Fallback zu Pattern-basierter Invalidierung
            }
        }

        // Fallback: Pattern-basierte Invalidierung
        return $this->invalidatePattern("*{$tableName}*");
    }
}
