<?php

declare(strict_types=1);

/**
 * Cache simples com APCu + fallback em arquivo
 * VERSÃO OTIMIZADA - Evita I/O desnecessário com cache em memória
 */
class SimpleCache {
    private static ?string $cacheDir = null;
    private static ?bool $useApcu = null;
    private static array $memoryCache = []; // Cache em memória para a request
    private static bool $initialized = false;

    /**
     * Inicializa o cache (lazy loading)
     */
    public static function init(): void {
        if (self::$initialized) {
            return;
        }
        
        self::$useApcu = function_exists('apcu_enabled') && apcu_enabled();
        self::$cacheDir = __DIR__ . '/../../app/logs/cache';
        
        if (!self::$useApcu && !is_dir(self::$cacheDir)) {
            @mkdir(self::$cacheDir, 0755, true);
        }
        
        self::$initialized = true;
    }

    /**
     * Busca valor do cache
     * @param string $key Chave única
     * @return mixed|null Retorna o valor ou null se não encontrado/expirado
     */
    public static function get(string $key) {
        // 1. Verifica cache de memória primeiro (mais rápido)
        if (isset(self::$memoryCache[$key])) {
            $cached = self::$memoryCache[$key];
            if ($cached['expires'] > time()) {
                return $cached['value'];
            }
            unset(self::$memoryCache[$key]);
        }
        
        self::init();
        
        // 2. Tenta APCu
        if (self::$useApcu) {
            $value = apcu_fetch($key, $success);
            if ($success) {
                // Armazena em memória para próximas leituras na mesma request
                self::$memoryCache[$key] = [
                    'value' => $value,
                    'expires' => time() + 30
                ];
                return $value;
            }
        }
        
        // 3. Fallback para arquivo
        $filepath = self::$cacheDir . '/' . md5($key) . '.cache';
        if (file_exists($filepath)) {
            $data = @file_get_contents($filepath);
            if ($data !== false) {
                $decoded = @unserialize($data);
                if ($decoded !== false && isset($decoded['expires'], $decoded['value'])) {
                    if ($decoded['expires'] > time()) {
                        // Armazena em memória
                        self::$memoryCache[$key] = [
                            'value' => $decoded['value'],
                            'expires' => time() + 30
                        ];
                        return $decoded['value'];
                    }
                    // Expirado - remove
                    @unlink($filepath);
                }
            }
        }
        
        return null;
    }

    /**
     * Armazena valor no cache
     * @param string $key Chave única
     * @param mixed $value Valor a armazenar
     * @param int $ttl Tempo de vida em segundos (padrão: 30 = 30 segundos)
     * @return bool Sucesso ou não
     */
    public static function set(string $key, $value, int $ttl = 30): bool {
        self::init();
        
        // Sempre armazena em memória
        self::$memoryCache[$key] = [
            'value' => $value,
            'expires' => time() + $ttl
        ];
        
        // Tenta APCu primeiro
        if (self::$useApcu) {
            $success = apcu_store($key, $value, $ttl);
            if ($success) {
                return true;
            }
        }
        
        // Fallback para arquivo
        $filepath = self::$cacheDir . '/' . md5($key) . '.cache';
        $data = serialize([
            'expires' => time() + $ttl,
            'value' => $value
        ]);
        
        return @file_put_contents($filepath, $data, LOCK_EX) !== false;
    }

    /**
     * Remove valor do cache
     * @param string $key Chave única
     * @return bool Sucesso ou não
     */
    public static function delete(string $key): bool {
        self::init();
        
        // Remove de memória
        unset(self::$memoryCache[$key]);
        
        $success = true;
        
        // Remove de APCu
        if (self::$useApcu) {
            apcu_delete($key);
        }
        
        // Remove arquivo
        $filepath = self::$cacheDir . '/' . md5($key) . '.cache';
        if (file_exists($filepath)) {
            $success = @unlink($filepath);
        }
        
        return $success;
    }
    
    /**
     * Invalida todos os caches do dashboard para uma empresa
     * @param int $cdempresa ID da empresa
     */
    public static function invalidateDashboard(int $cdempresa): void {
        $anoCorrente = (int)date('Y');
        $mesCorrente = (int)date('n');
        $diaCorrente = (int)date('j');
        self::delete("dashboard_heavy_{$cdempresa}_v3");
        // Nota: NÃO deletamos cache de feriados pois é independente da sincronização
        // Feriados são dados estáticos que não devem ser afetados por sync de realizado
    }

    /**
     * Verifica se houve sincronização recente e invalida cache se necessário
     * Se a última sincronização foi há menos de 5 minutos, o cache é expirado imediatamente
     * @param \PDO $pdo Conexão com banco de dados
     * @param int $cdempresa ID da empresa
     * @param int $minutosThreshold Threshold em minutos (padrão: 5)
     * @return bool True se cache foi invalidado, false caso contrário
     */
    public static function checkRecentSyncAndInvalidate(\PDO $pdo, int $cdempresa, int $minutosThreshold = 5): bool {
        try {
            // Buscar a última sincronização bem-sucedida para qualquer integração desta empresa
            // Usa dtexec_fim que registra quando a sincronização terminou
            $stmt = $pdo->prepare("
                SELECT MAX(dtexec_fim) as ultima_sinc 
                FROM back.integracao_log
                WHERE cdempresa = :emp 
                AND status = 'sucesso'
                LIMIT 1
            ");
            $stmt->execute([':emp' => $cdempresa]);
            $resultado = $stmt->fetch(\PDO::FETCH_ASSOC);
            
            if (!$resultado || !$resultado['ultima_sinc']) {
                return false; // Nenhuma sincronização encontrada
            }
            
            // Converter string de timestamp para DateTime
            try {
                $dt_ultima = new \DateTime($resultado['ultima_sinc']);
                $dt_agora = new \DateTime();
                
                // Calcular diferença em minutos
                $diff = $dt_agora->diff($dt_ultima);
                $minutos_passados = ($diff->days * 24 * 60) + ($diff->h * 60) + $diff->i;
                
                // Se a sincronização foi há menos que o threshold, invalidar cache
                if ($minutos_passados <= $minutosThreshold) {
                    self::invalidateDashboard($cdempresa);
                    return true;
                }
            } catch (\Exception $e) {
                // Em caso de erro ao processar data, apenas retorna false
                return false;
            }
            
            return false;
        } catch (\Exception $e) {
            // Em caso de erro na query, apenas retorna false (não quebra o código)
            return false;
        }
    }

    /**
     * Limpa cache expirado (apenas arquivos)
     * @return int Número de arquivos removidos
     */
    public static function cleanup(): int {
        self::init();
        $removed = 0;
        
        if (self::$useApcu) {
            return 0; // APCu gerencia automaticamente
        }
        
        $files = glob(self::$cacheDir . '/*.cache');
        if ($files === false) {
            return 0;
        }
        
        foreach ($files as $file) {
            $data = @file_get_contents($file);
            if ($data !== false) {
                $decoded = @unserialize($data);
                if ($decoded !== false && isset($decoded['expires'])) {
                    if ($decoded['expires'] < time()) {
                        if (@unlink($file)) {
                            $removed++;
                        }
                    }
                }
            }
        }
        
        return $removed;
    }
}
