<?php

declare(strict_types=1);

namespace App\Framework\Database;

use App\Framework\Cache\Cache;
use App\Framework\Database\Cache\CacheStrategy;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Driver\Driver;
use App\Framework\Database\Driver\DriverConfig;
use App\Framework\Database\Driver\DriverType;
use App\Framework\Database\Driver\MysqlDriver;
use App\Framework\Database\Driver\PostgresDriver;
use App\Framework\Database\Driver\SqliteDriver;
use App\Framework\Database\Exception\DatabaseException;
use App\Framework\Database\Middleware\MiddlewarePipeline;
use App\Framework\Database\Middleware\CacheMiddleware;
use App\Framework\Database\Middleware\RetryMiddleware;
use App\Framework\Database\Middleware\HealthCheckMiddleware;
use App\Framework\Database\Cache\CacheAdapterStrategy;
use App\Framework\Database\Cache\SimpleCacheStrategy;
use Pdo\Mysql;
use Pdo\Pgsql;
use Pdo\Sqlite;

final readonly class DatabaseFactory
{
    public static function createConnection(
        array|DriverConfig $config,
        array $middlewareConfig = []
    ): ConnectionInterface {
        $lazy = $middlewareConfig['lazy'] ?? false;
        $retry = $middlewareConfig['retry'] ?? false;
        $healthCheck = $middlewareConfig['health_check'] ?? false;
        $cache = $middlewareConfig['cache'] ?? false;

        if (!$lazy && !$retry && !$healthCheck && !$cache) {
            // Keine Middleware - direkte Verbindung
            return self::createDirectConnection($config);
        }

        // Base Connection erstellen
        if ($lazy) {
            $baseConnection = LazyConnectionFactory::createLazyGhost($config);
        } else {
            $baseConnection = self::createDirectConnection($config);
        }

        // Middleware-Pipeline erstellen
        $pipeline = new MiddlewarePipeline();

        if ($cache) {
            $cacheConfig = is_array($cache) ? $cache : [];
            $cacheStrategy = self::createCacheStrategy($cacheConfig);

            $pipeline->add(new CacheMiddleware(
                $cacheStrategy,
                $cacheConfig['ttl'] ?? 300,
                $cacheConfig['enabled'] ?? true,
                $cacheConfig['cacheable_operations'] ?? ['query', 'queryOne', 'queryColumn', 'queryScalar']
            ));
        }

        if ($retry) {
            $retryConfig = is_array($retry) ? $retry : [];
            $pipeline->add(new RetryMiddleware(
                $retryConfig['max_retries'] ?? 3,
                $retryConfig['delay_ms'] ?? 100,
                $retryConfig['retryable_exceptions'] ?? [\PDOException::class, DatabaseException::class]
            ));
        }

        if ($healthCheck) {
            $healthConfig = is_array($healthCheck) ? $healthCheck : [];
            $pipeline->add(new HealthCheckMiddleware(
                $healthConfig['interval'] ?? 30,
                $healthConfig['enabled'] ?? true
            ));
        }

        // Wenn Pipeline leer ist, gib direkt die Base Connection zurück
        if (empty($pipeline->getMiddleware())) {
            return $baseConnection;
        }

        return new MiddlewareConnection($baseConnection, $pipeline);
    }

    public static function createDirectConnection(array|DriverConfig $config): ConnectionInterface
    {
        if(is_array($config)) {
            $config = DriverConfig::fromArray($config);
        }

        $driver = self::createDriver($config);
        $pdo = self::createPdo($driver);

        return new PdoConnection($pdo);
    }

    public static function createLazyConnection(
        array|DriverConfig $config,
        array $additionalMiddleware = []
    ): ConnectionInterface {
        return self::createConnection($config, array_merge(
            ['lazy' => true],
            $additionalMiddleware
        ));
    }

    public static function createRetryableConnection(
        array|DriverConfig $config,
        int $maxRetries = 3,
        int $retryDelayMs = 100
    ): ConnectionInterface {
        return self::createConnection($config, [
            'retry' => [
                'max_retries' => $maxRetries,
                'delay_ms' => $retryDelayMs
            ]
        ]);
    }

    public static function createRobustConnection(
        array|DriverConfig $config,
        array $middlewareConfig = []
    ): ConnectionInterface {
        // Standard "robuste" Konfiguration
        $defaultConfig = [
            'lazy' => true,
            'retry' => [
                'max_retries' => 3,
                'delay_ms' => 100
            ],
            'health_check' => [
                'interval' => 30,
                'enabled' => true
            ]
        ];

        return self::createConnection($config, array_merge($defaultConfig, $middlewareConfig));
    }

    public static function createPureLazyConnection(array|DriverConfig $config): ConnectionInterface
    {
        // Reine LazyGhost-Connection ohne zusätzliche Middleware
        return LazyConnectionFactory::createLazyGhost($config);
    }

    public static function isLazyConnection(ConnectionInterface $connection): bool
    {
        if ($connection instanceof MiddlewareConnection) {
            $baseConnection = $connection->getBaseConnection();
            return LazyConnectionFactory::isLazyGhost($baseConnection);
        }

        return LazyConnectionFactory::isLazyGhost($connection);
    }

    public static function forceLazyInitialization(ConnectionInterface $connection): void
    {
        if ($connection instanceof MiddlewareConnection) {
            $baseConnection = $connection->getBaseConnection();
            if (LazyConnectionFactory::isLazyGhost($baseConnection)) {
                LazyConnectionFactory::initializeLazyGhost($baseConnection);
            }
        } elseif (LazyConnectionFactory::isLazyGhost($connection)) {
            LazyConnectionFactory::initializeLazyGhost($connection);
        }
    }

    private static function createCacheStrategy(array $config): CacheStrategy
    {
        // Verwende direktes CacheInterface oder fallback zu ArrayCache
        $keyPrefix = $config['prefix'] ?? 'db_query:';
        $tags = $config['tags'] ?? ['database_query'];

        // Wenn eine CacheInterface-Instanz direkt übergeben wird
        if (isset($config['cache_instance']) && $config['cache_instance'] instanceof Cache) {
            return new CacheAdapterStrategy($config['cache_instance'], $keyPrefix);
        }

        // Fallback zu einfacher Array-basierter Implementierung
        return new SimpleCacheStrategy($keyPrefix);
    }

    /**
     * Erstelle Cache-Strategy mit direkter CacheInterface-Instanz
     */
    public static function createCacheStrategyFromCache(
        Cache $cache,
        string $keyPrefix = 'db_query:',
        array $tags = ['database_query']
    ): CacheStrategy {
        return CacheAdapterStrategy::withTags($cache, $tags, $keyPrefix);
    }

    public static function createCachedConnection(
        array|DriverConfig $config,
        array $cacheConfig = []
    ): ConnectionInterface {
        return self::createConnection($config, [
            'cache' => array_merge([
                'ttl' => 300,
                'enabled' => true,
                'prefix' => 'db_query:',
                'tags' => ['database_query']
            ], $cacheConfig)
        ]);
    }

    public static function createCachedConnectionWithCache(
        array|DriverConfig $config,
        Cache $cache,
        array $additionalConfig = []
    ): ConnectionInterface {
        return self::createCachedConnection($config, array_merge([
            'cache_instance' => $cache
        ], $additionalConfig));
    }

    /**
     * Erstelle Connection mit externem Cache-System
     */
    public static function createConnectionWithCustomCache(
        array|DriverConfig $config,
        Cache $cache,
        string $keyPrefix = 'db_query:',
        int $ttl = 300
    ): ConnectionInterface {
        return self::createConnection($config, [
            'cache' => [
                'cache_instance' => $cache,
                'prefix' => $keyPrefix,
                'ttl' => $ttl,
                'enabled' => true
            ]
        ]);
    }

    public static function createFullFeaturedConnection(
        array|DriverConfig $config,
        array $middlewareConfig = []
    ): ConnectionInterface {
        // "Alles dabei" - Connection mit allen Features
        $defaultConfig = [
            'lazy' => true,
            'cache' => [
                'ttl' => 300,
                'enabled' => true,
                'prefix' => 'db_query:',
                'tags' => ['database_query']
            ],
            'retry' => [
                'max_retries' => 3,
                'delay_ms' => 100
            ],
            'health_check' => [
                'interval' => 30,
                'enabled' => true
            ]
        ];

        return self::createConnection($config, array_merge($defaultConfig, $middlewareConfig));
    }

    public static function createProductionConnection(
        array|DriverConfig $config,
        ?Cache $cache = null,
        array $middlewareConfig = []
    ): ConnectionInterface {
        // Production-optimierte Konfiguration
        $defaultConfig = [
            'lazy' => true,
            'cache' => [
                'ttl' => 1800,
                'enabled' => true,
                'prefix' => 'prod_db:',
                'tags' => ['database_query', 'production']
            ],
            'retry' => [
                'max_retries' => 5,
                'delay_ms' => 200
            ],
            'health_check' => [
                'interval' => 60,
                'enabled' => true
            ]
        ];

        // Wenn externe Cache-Instanz übergeben wird
        if ($cache !== null) {
            $defaultConfig['cache']['cache_instance'] = $cache;
        }

        return self::createConnection($config, array_merge($defaultConfig, $middlewareConfig));
    }

    private static function createDriver(DriverConfig $config): Driver
    {
        return match($config->driverType) {
            DriverType::MYSQL => new MysqlDriver($config),
            DriverType::PGSQL => new PostgresDriver($config),
            DriverType::SQLITE => new SqliteDriver($config),
        };
    }

    private static function createPdo(Driver $driver): \PDO
    {
        return match($driver->config->driverType) {
            DriverType::MYSQL => new Mysql(
                $driver->dsn,
                $driver->config->username,
                $driver->config->password,
                $driver->options
            ),
            DriverType::PGSQL => new Pgsql(
                $driver->dsn,
                $driver->config->username,
                $driver->config->password,
                $driver->options
            ),
            DriverType::SQLITE => new Sqlite(
                $driver->dsn,
                $driver->config->username,
                $driver->config->password,
                $driver->options
            ),
        };
    }
}
