<?php

namespace Modules\Admin\App\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\File;

class CacheManagerService
{
    private string $driver;

    public function __construct(string $driver = null)
    {
        $this->driver = $driver ?? config('cache.default');
    }

    /**
     * Obtiene estadísticas de caché para el driver especificado.
     */
    public function getCacheStats(string $driver = null): array
    {
        $driver = $driver ?? $this->driver;

        if (!$this->isSupportedDriver($driver)) {
            return $this->response('warning', 'Driver no soportado o no configurado.');
        }

        try {
            return match ($driver) {
                'database' => $this->_getDatabaseStats(),
                'file' => $this->_getFilecacheStats(),
                'redis' => $this->_getRedisStats(),
                'memcached' => $this->_getMemcachedStats(),
                default => $this->response('info', 'No hay estadísticas disponibles para este driver.'),
            };
        } catch (\Exception $e) {
            return $this->response('danger', 'Error al obtener estadísticas: ' . $e->getMessage());
        }
    }

    public function clearCache(string $driver = null): array
    {
        $driver = $driver ?? $this->driver;

        if (!$this->isSupportedDriver($driver)) {
            return $this->response('warning', 'Driver no soportado o no configurado.');
        }

        try {
            switch ($driver) {
                case 'redis':
                    $keysCleared = $this->clearRedisCache();

                    return $keysCleared
                        ? $this->response('warning', 'Se ha purgado toda la caché de Redis.')
                        : $this->response('info', 'No se encontraron claves en Redis para eliminar.');

                case 'memcached':
                    $keysCleared = $this->clearMemcachedCache();

                    return $keysCleared
                        ? $this->response('warning', 'Se ha purgado toda la caché de Memcached.')
                        : $this->response('info', 'No se encontraron claves en Memcached para eliminar.');

                case 'database':
                    $rowsDeleted = $this->clearDatabaseCache();

                    return $rowsDeleted
                        ? $this->response('warning', 'Se ha purgado toda la caché almacenada en la base de datos.')
                        : $this->response('info', 'No se encontraron registros en la caché de la base de datos.');

                case 'file':
                    $filesDeleted = $this->clearFilecache();

                    return $filesDeleted
                        ? $this->response('warning', 'Se ha purgado toda la caché de archivos.')
                        : $this->response('info', 'No se encontraron archivos en la caché para eliminar.');

                default:
                    Cache::flush();

                    return $this->response('warning', 'Caché purgada.');
            }
        } catch (\Exception $e) {
            return $this->response('danger', 'Error al limpiar la caché: ' . $e->getMessage());
        }
    }

    public function getRedisStats()
    {
        try {
            if (!Redis::ping()) {
                return $this->response('warning', 'No se puede conectar con el servidor Redis.');
            }

            $info = Redis::info();

            $databases = $this->getRedisDatabases();

            $redisInfo = [
                'server' => config('database.redis.default.host'),
                'redis_version' => $info['redis_version'] ?? 'N/A',
                'os' => $info['os'] ?? 'N/A',
                'tcp_port' => $info['tcp_port'] ?? 'N/A',
                'connected_clients' => $info['connected_clients'] ?? 'N/A',
                'blocked_clients' => $info['blocked_clients'] ?? 'N/A',
                'maxmemory' => $info['maxmemory'] ?? 0,
                'used_memory_human' => $info['used_memory_human'] ?? 'N/A',
                'used_memory_peak' => $info['used_memory_peak'] ?? 'N/A',
                'used_memory_peak_human' => $info['used_memory_peak_human'] ?? 'N/A',
                'total_system_memory' => $info['total_system_memory'] ?? 0,
                'total_system_memory_human' => $info['total_system_memory_human'] ?? 'N/A',
                'maxmemory_human' => $info['maxmemory_human'] !== '0B' ? $info['maxmemory_human'] : 'Sin Límite',
                'total_connections_received' => number_format($info['total_connections_received']) ?? 'N/A',
                'total_commands_processed' => number_format($info['total_commands_processed']) ?? 'N/A',
                'maxmemory_policy' => $info['maxmemory_policy'] ?? 'N/A',
                'role' => $info['role'] ?? 'N/A',
                'cache_database' => '',
                'sessions_database' => '',
                'general_database' => ',',
                'keys' => $databases['total_keys'],
                'used_memory' => $info['used_memory'] ?? 0,
                'uptime' => gmdate('H\h i\m s\s', $info['uptime_in_seconds'] ?? 0),
                'databases' => $databases,
            ];

            return $this->response('success', 'Se a recargado las estadísticas de Redis.', ['info' => $redisInfo]);
        } catch (\Exception $e) {
            return $this->response('danger', 'Error al conectar con el servidor Redis: ' . Redis::getLastError());
        }
    }

    public function getMemcachedStats()
    {
        try {
            $memcachedStats = [];

            // Crear instancia del cliente Memcached
            $memcached = new \Memcached();
            $memcached->addServer(config('memcached.host'), config('memcached.port'));

            // Obtener estadísticas del servidor
            $stats = $memcached->getStats();

            foreach ($stats as $server => $data) {
                $server = explode(':', $server);

                $memcachedStats[] = [
                    'server' => $server[0],
                    'tcp_port' => $server[1],
                    'uptime' => $data['uptime'] ?? 'N/A',
                    'version' => $data['version'] ?? 'N/A',
                    'libevent' => $data['libevent'] ?? 'N/A',
                    'max_connections' => $data['max_connections'] ?? 0,
                    'total_connections' => $data['total_connections'] ?? 0,
                    'rejected_connections' => $data['rejected_connections'] ?? 0,
                    'curr_items' => $data['curr_items'] ?? 0, // Claves almacenadas
                    'bytes' => $data['bytes'] ?? 0, // Memoria usada
                    'limit_maxbytes' => $data['limit_maxbytes'] ?? 0, // Memoria máxima
                    'cmd_get' => $data['cmd_get'] ?? 0, // Comandos GET ejecutados
                    'cmd_set' => $data['cmd_set'] ?? 0, // Comandos SET ejecutados
                    'get_hits' => $data['get_hits'] ?? 0, // GET exitosos
                    'get_misses' => $data['get_misses'] ?? 0, // GET fallidos
                    'evictions' => $data['evictions'] ?? 0, // Claves expulsadas
                    'bytes_read' => $data['bytes_read'] ?? 0, // Bytes leídos
                    'bytes_written' => $data['bytes_written'] ?? 0, // Bytes escritos
                    'total_items' => $data['total_items'] ?? 0,
                ];
            }

            return $this->response('success', 'Se a recargado las estadísticas de Memcached.', ['info' => $memcachedStats]);
        } catch (\Exception $e) {
            return $this->response('danger', 'Error al conectar con el servidor Memcached: ' . $e->getMessage());
        }
    }


    /**
     * Obtiene estadísticas para caché en base de datos.
     */
    private function _getDatabaseStats(): array
    {
        try {
            $recordCount = DB::table('cache')->count();
            $tableInfo = DB::select("SHOW TABLE STATUS WHERE Name = 'cache'");

            $memory_usage = isset($tableInfo[0]) ? $this->formatBytes($tableInfo[0]->Data_length + $tableInfo[0]->Index_length) : 'N/A';

            return $this->response('success', 'Se ha recargado la información de la caché de base de datos.', ['item_count' => $recordCount, 'memory_usage' => $memory_usage]);
        } catch (\Exception $e) {
            return $this->response('danger', 'Error al obtener estadísticas de la base de datos: ' . $e->getMessage());
        }
    }

    /**
     * Obtiene estadísticas para caché en archivos.
     */
    private function _getFilecacheStats(): array
    {
        try {
            $cachePath = config('cache.stores.file.path');
            $files = glob($cachePath . '/*');

            $memory_usage = $this->formatBytes(array_sum(array_map('filesize', $files)));

            return $this->response('success', 'Se ha recargado la información de la caché de archivos.', ['item_count' => count($files), 'memory_usage' => $memory_usage]);
        } catch (\Exception $e) {
            return $this->response('danger', 'Error al obtener estadísticas de archivos: ' . $e->getMessage());
        }
    }

    private function _getRedisStats()
    {
        try {
            $prefix = config('cache.prefix'); // Asegúrate de agregar el sufijo correcto si es necesario

            $info = Redis::info();
            $keys = Redis::connection('cache')->keys($prefix . '*');

            $memory_usage = $this->formatBytes($info['used_memory'] ?? 0);

            return $this->response('success', 'Se ha recargado la información de la caché de Redis.', ['item_count' => count($keys), 'memory_usage' => $memory_usage]);
        } catch (\Exception $e) {
            return $this->response('danger', 'Error al obtener estadísticas de Redis: ' . $e->getMessage());
        }
    }

    public function _getMemcachedStats(): array
    {
        try {
            // Obtener estadísticas generales del servidor
            $stats = Cache::getStore()->getMemcached()->getStats();

            if (empty($stats)) {
                return $this->response('error', 'No se pudieron obtener las estadísticas del servidor Memcached.', ['item_count' => 0, 'memory_usage' => 0]);
            }

            // Usar el primer servidor configurado (en la mayoría de los casos hay uno)
            $serverStats = array_shift($stats);

            return $this->response(
                'success',
                'Estadísticas del servidor Memcached obtenidas correctamente.',
                [
                    'item_count' => $serverStats['curr_items'] ?? 0, // Número total de claves
                    'memory_usage' => $this->formatBytes($serverStats['bytes'] ?? 0), // Memoria usada
                    'max_memory' => $this->formatBytes($serverStats['limit_maxbytes'] ?? 0), // Memoria máxima asignada
                ]
            );
        } catch (\Exception $e) {
            return $this->response('danger', 'Error al obtener estadísticas de Memcached: ' . $e->getMessage());
        }
    }

    private function getRedisDatabases(): array
    {
        // Verificar si Redis está en uso
        $isRedisUsed = collect([
            config('cache.default'),
            config('session.driver'),
            config('queue.default'),
        ])->contains('redis');

        if (!$isRedisUsed) {
            return []; // Si Redis no está en uso, devolver un arreglo vacío
        }

        // Configuraciones de bases de datos de Redis según su uso
        $databases = [
            'default' => config('database.redis.default.database', 0), // REDIS_DB
            'cache' => config('database.redis.cache.database', 0), // REDIS_CACHE_DB
            'sessions' => config('database.redis.sessions.database', 0), // REDIS_SESSION_DB
        ];

        $result = [];
        $totalKeys = 0;

        // Recorrer solo las bases configuradas y activas
        foreach ($databases as $type => $db) {
            Redis::select($db); // Seleccionar la base de datos

            $keys = Redis::dbsize(); // Contar las claves en la base

            if ($keys > 0) {
                $result[$type] = [
                    'database' => $db,
                    'keys' => $keys,
                ];

                $totalKeys += $keys;
            }
        }

        if (!empty($result)) {
            $result['total_keys'] = $totalKeys;
        }

        return $result;
    }


    private function clearDatabaseCache(): bool
    {
        $count = DB::table(config('cache.stores.database.table'))->count();

        if ($count > 0) {
            DB::table(config('cache.stores.database.table'))->truncate();
            return true;
        }

        return false;
    }

    private function clearFilecache(): bool
    {
        $cachePath = config('cache.stores.file.path');
        $files = glob($cachePath . '/*');

        if (!empty($files)) {
            File::deleteDirectory($cachePath);
            return true;
        }

        return false;
    }

    private function clearRedisCache(): bool
    {
        $prefix = config('cache.prefix', '');
        $keys = Redis::connection('cache')->keys($prefix . '*');

        if (!empty($keys)) {
            Redis::connection('cache')->flushdb();

            // Simulate cache clearing delay
            sleep(1);

            return true;
        }

        return false;
    }

    private function clearMemcachedCache(): bool
    {
        // Obtener el cliente Memcached directamente
        $memcached = Cache::store('memcached')->getStore()->getMemcached();

        // Ejecutar flush para eliminar todo
        if ($memcached->flush()) {
            // Simulate cache clearing delay
            sleep(1);

            return true;
        }

        return false;
    }


    /**
     * Verifica si un driver es soportado.
     */
    private function isSupportedDriver(string $driver): bool
    {
        return in_array($driver, ['redis', 'memcached', 'database', 'file']);
    }

    /**
     * Convierte bytes en un formato legible.
     */
    private function formatBytes($bytes)
    {
        $sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
        $factor = floor((strlen($bytes) - 1) / 3);

        return sprintf('%.2f', $bytes / pow(1024, $factor)) . ' ' . $sizes[$factor];
    }

    /**
     * Genera una respuesta estandarizada.
     */
    private function response(string $status, string $message, array $data = []): array
    {
        return array_merge(compact('status', 'message'), $data);
    }
}
