<?php

declare(strict_types=1);

namespace App\Framework\Cache\Commands;

use App\Framework\Cache\Cache;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ExitCode;
use App\Framework\Core\PathProvider;
use App\Framework\DI\Container;
use App\Framework\Redis\RedisConnectionPool;

final readonly class ClearCache
{
    public function __construct(
        private Cache $cache,
        private PathProvider $pathProvider,
        private Container $container
    ) {
    }

    #[ConsoleCommand("cache:clear", "Clears all caches (application, discovery, routes, opcache, redis)")]
    public function __invoke(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $cleared = [];

        // Clear OPcache
        if (function_exists('opcache_reset')) {
            opcache_reset();
            $cleared[] = 'OPcache';
        }

        // Clear Redis cache completely - this is now ALWAYS done
        // because Discovery cache and other critical framework caches are stored in Redis
        if ($this->clearRedisCompletely()) {
            $cleared[] = 'Redis (FLUSHALL)';
        } else {
            // Fallback: Clear application cache through Cache interface
            $this->cache->clear();
            $cleared[] = 'Application cache (fallback)';
        }

        // Clear discovery cache files (redundant after Redis FLUSHALL, but safe)
        $this->clearDiscoveryFiles();
        $cleared[] = 'Discovery files';

        // Clear routes cache
        $routesCacheFile = $this->pathProvider->resolvePath('/cache/routes.cache.php');
        if (file_exists($routesCacheFile)) {
            unlink($routesCacheFile);
            $cleared[] = 'Routes cache';
        }

        // Clear all cache files
        $this->clearAllCacheFiles();
        $cleared[] = 'All cache files';

        $output->writeSuccess('Cleared: ' . implode(', ', $cleared));

        return ExitCode::SUCCESS;
    }

    #[ConsoleCommand("redis:flush", "Advanced Redis cache clearing with options")]
    public function flushRedis(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $cleared = [];

        if (! $this->container->has(RedisConnectionPool::class)) {
            $output->writeError('Redis connection pool not available');

            return ExitCode::FAILURE;
        }

        try {
            $redisPool = $this->container->get(RedisConnectionPool::class);
            $connection = $redisPool->getConnection('cache');
            $redis = $connection->getClient();

            // Option: --db to flush specific database
            if ($input->hasOption('db')) {
                $database = (int) $input->getOption('db');
                $redis->select($database);
                $result = $redis->flushDB();
                if ($result === true) {
                    $cleared[] = "Redis database $database";
                } else {
                    $output->writeError("Failed to flush Redis database $database");

                    return ExitCode::FAILURE;
                }
            } else {
                // Default: FLUSHALL
                $result = $redis->flushAll();
                if ($result === true) {
                    $cleared[] = 'Redis (all databases)';
                } else {
                    $output->writeError('Failed to flush Redis');

                    return ExitCode::FAILURE;
                }
            }

            $output->writeSuccess('Cleared: ' . implode(', ', $cleared));

            return ExitCode::SUCCESS;
        } catch (\Throwable $e) {
            $output->writeError('Redis flush failed: ' . $e->getMessage());

            return ExitCode::FAILURE;
        }
    }

    private function clearDiscoveryFiles(): void
    {
        $cacheDir = $this->pathProvider->resolvePath('/cache');
        if (! is_dir($cacheDir)) {
            return;
        }

        $files = glob($cacheDir . '/discovery_*.cache.php');
        foreach ($files as $file) {
            if (file_exists($file)) {
                unlink($file);
            }
        }
    }

    private function clearAllCacheFiles(): void
    {
        $cacheDir = $this->pathProvider->resolvePath('/cache');
        if (! is_dir($cacheDir)) {
            return;
        }

        $files = glob($cacheDir . '/*.cache.php');
        foreach ($files as $file) {
            if (file_exists($file)) {
                unlink($file);
            }
        }
    }

    private function clearRedisCompletely(): bool
    {
        try {
            // Strategy 1: Direct Redis connection via RedisConnectionPool (lazy-loaded)
            try {
                if ($this->container->has(RedisConnectionPool::class)) {
                    $redisPool = $this->container->get(RedisConnectionPool::class);
                    $connection = $redisPool->getConnection('cache');
                    $redis = $connection->getClient();

                    // Use FLUSHALL to clear all Redis databases
                    $result = $redis->flushAll();
                    if ($result === true) {
                        return true;
                    }

                    // Fallback: FLUSHDB for cache database (database 1)
                    $redis->select(1);
                    $result = $redis->flushDB();
                    if ($result === true) {
                        return true;
                    }
                }
            } catch (\Throwable $e) {
                error_log("Direct Redis connection failed: " . $e->getMessage());
            }

            // Strategy 2: Through cache interface clear method (limited effectiveness)
            try {
                $this->cache->clear();

                // Note: This only clears application cache patterns, not Discovery cache
                return false; // Return false to indicate partial clearing
            } catch (\Throwable $e) {
                error_log("Cache interface clear failed: " . $e->getMessage());
            }

            return false;
        } catch (\Exception $e) {
            error_log("Redis cache clear failed: " . $e->getMessage());

            return false;
        }
    }
}
