<?php
namespace YouTube;

use Core\Config;
use Utils\Logger;
use Security\Security;

/**
 * Classe YouTubeService - Integração moderna com a API do YouTube
 * 
 * Versão melhorada que:
 * - Utiliza namespaces para organização
 * - Implementa padrão Singleton de forma mais robusta
 * - Melhora o sistema de cache
 * - Implementa tratamento de erros mais robusto
 */

class YouTubeService {
    private static $instance = null;
    private $apiKey;
    private $channelId;
    private $cachePath;
    private $cacheExpiration;
    private $security;
    private $logger;
    private $config;
    
    /**
     * Construtor privado (padrão Singleton)
     */
    private function __construct() {
        $this->config = Config::getInstance();
        $this->security = Security::getInstance();
        $this->logger = new Logger('youtube');
        
        // Obter configurações
        $this->apiKey = $this->config->get('youtube.api_key', '');
        $this->channelId = $this->config->get('youtube.channel_id', '');
        $this->cachePath = $this->config->get('cache_path', __DIR__ . '/../../cache');
        $this->cacheExpiration = $this->config->get('youtube.cache_expiration', 3600);
        
        // Criar diretório de cache se não existir
        if (!is_dir($this->cachePath)) {
            mkdir($this->cachePath, 0755, true);
        }
    }
    
    /**
     * Previne clonagem do objeto (padrão Singleton)
     */
    private function __clone() {}
    
    /**
     * Previne desserialização do objeto (padrão Singleton)
     */
    private function __wakeup() {}
    
    /**
     * Obtém a instância única da classe
     * 
     * @return YouTubeService
     */
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        
        return self::$instance;
    }
    
    /**
     * Define a chave da API
     * 
     * @param string $apiKey Chave da API do YouTube
     * @return YouTubeService Instância atual para encadeamento
     */
    public function setApiKey($apiKey) {
        $this->apiKey = $this->security->sanitizeInput($apiKey);
        return $this;
    }
    
    /**
     * Define o ID do canal
     * 
     * @param string $channelId ID do canal do YouTube
     * @return YouTubeService Instância atual para encadeamento
     */
    public function setChannelId($channelId) {
        $this->channelId = $this->security->sanitizeInput($channelId);
        return $this;
    }
    
    /**
     * Define o tempo de expiração do cache
     * 
     * @param int $seconds Tempo em segundos
     * @return YouTubeService Instância atual para encadeamento
     */
    public function setCacheExpiration($seconds) {
        $this->cacheExpiration = max(300, intval($seconds)); // Mínimo de 5 minutos
        return $this;
    }
    
    /**
     * Obter os últimos vídeos do canal
     * 
     * @param int $maxResults Número máximo de resultados a retornar
     * @return array Array com informações dos vídeos
     */
    public function getLatestVideos($maxResults = 3) {
        // Validar parâmetros
        $maxResults = min(10, max(1, intval($maxResults))); // Entre 1 e 10
        
        // Verificar se a chave da API e o ID do canal estão definidos
        if (empty($this->apiKey) || empty($this->channelId)) {
            $this->logger->error("Chave da API ou ID do canal não definidos");
            return $this->getEmptyVideoArray($maxResults);
        }
        
        // Verificar se há cache válido
        $cachedData = $this->getCache($maxResults);
        if ($cachedData !== false) {
            $this->logger->info("Usando dados em cache para {$maxResults} vídeos");
            return $cachedData;
        }
        
        try {
            // Obter os últimos vídeos da API
            $videos = $this->fetchVideosFromAPI($maxResults);
            
            // Salvar no cache
            $this->saveCache($videos, $maxResults);
            
            return $videos;
        } catch (\Exception $e) {
            $this->logger->error("Erro ao obter vídeos: " . $e->getMessage(), [
                'exception' => get_class($e)
            ]);
            return $this->getEmptyVideoArray($maxResults);
        }
    }
    
    /**
     * Obter vídeos da API do YouTube
     * 
     * @param int $maxResults Número máximo de resultados a retornar
     * @return array Array com informações dos vídeos
     */
    private function fetchVideosFromAPI($maxResults) {
        // Construir URL da API para obter os uploads do canal
        $apiUrl = "https://www.googleapis.com/youtube/v3/search?";
        $apiUrl .= "key=" . urlencode($this->apiKey);
        $apiUrl .= "&channelId=" . urlencode($this->channelId);
        $apiUrl .= "&part=snippet,id";
        $apiUrl .= "&order=date";
        $apiUrl .= "&maxResults=" . $maxResults;
        $apiUrl .= "&type=video";
        
        $this->logger->debug("Fazendo requisição à API do YouTube", [
            'url' => str_replace($this->apiKey, '[API_KEY]', $apiUrl)
        ]);
        
        // Fazer requisição à API com tratamento de erros
        $context = stream_context_create([
            'http' => [
                'timeout' => 10, // Timeout de 10 segundos
                'user_agent' => 'PHP YouTubeService'
            ]
        ]);
        
        $response = @file_get_contents($apiUrl, false, $context);
        
        if ($response === false) {
            throw new \Exception("Falha na requisição à API do YouTube");
        }
        
        // Decodificar resposta JSON
        $data = json_decode($response, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new \Exception("Erro ao decodificar resposta JSON: " . json_last_error_msg());
        }
        
        if (!isset($data['items']) || empty($data['items'])) {
            $this->logger->warning("Nenhum vídeo encontrado na resposta da API");
            return $this->getEmptyVideoArray($maxResults);
        }
        
        // Processar resultados
        $videos = [];
        foreach ($data['items'] as $item) {
            if (!isset($item['id']['videoId'])) {
                continue;
            }
            
            $videoId = $item['id']['videoId'];
            $title = $item['snippet']['title'] ?? 'Sem título';
            $description = $item['snippet']['description'] ?? '';
            $publishedAt = $item['snippet']['publishedAt'] ?? date('c');
            $thumbnail = $item['snippet']['thumbnails']['high']['url'] ?? '';
            
            // Obter estatísticas do vídeo (visualizações, likes, etc.)
            try {
                $videoStats = $this->getVideoStats($videoId);
            } catch (\Exception $e) {
                $this->logger->error("Erro ao obter estatísticas do vídeo {$videoId}: " . $e->getMessage());
                $videoStats = [
                    'views' => 0,
                    'likes' => 0,
                    'duration' => 'PT0M0S'
                ];
            }
            
            $videos[] = [
                'id' => $videoId,
                'title' => $title,
                'description' => $description,
                'publishedAt' => $publishedAt,
                'thumbnail' => $thumbnail,
                'url' => "https://www.youtube.com/watch?v=$videoId",
                'embed_url' => "https://www.youtube.com/embed/$videoId",
                'views' => $videoStats['views'],
                'likes' => $videoStats['likes'],
                'duration' => $videoStats['duration'],
                'formatted_duration' => $this->formatDuration($videoStats['duration']),
                'formatted_views' => $this->formatViews($videoStats['views']),
                'time_ago' => $this->timeAgo($publishedAt)
            ];
            
            if (count($videos) >= $maxResults) {
                break;
            }
        }
        
        $this->logger->info("Obtidos {$maxResults} vídeos da API do YouTube");
        return $videos;
    }
    
    /**
     * Obter estatísticas de um vídeo específico
     * 
     * @param string $videoId ID do vídeo
     * @return array Array com estatísticas do vídeo
     */
    private function getVideoStats($videoId) {
        // Verificar se há cache válido para as estatísticas
        $cacheKey = "video_stats_{$videoId}";
        $cachedStats = $this->getCacheItem($cacheKey);
        if ($cachedStats !== false) {
            return $cachedStats;
        }
        
        // Construir URL da API para obter estatísticas do vídeo
        $apiUrl = "https://www.googleapis.com/youtube/v3/videos?";
        $apiUrl .= "key=" . urlencode($this->apiKey);
        $apiUrl .= "&id=" . urlencode($videoId);
        $apiUrl .= "&part=statistics,contentDetails";
        
        $this->logger->debug("Obtendo estatísticas para o vídeo {$videoId}");
        
        // Fazer requisição à API com tratamento de erros
        $context = stream_context_create([
            'http' => [
                'timeout' => 5, // Timeout de 5 segundos
                'user_agent' => 'PHP YouTubeService'
            ]
        ]);
        
        $response = @file_get_contents($apiUrl, false, $context);
        
        if ($response === false) {
            throw new \Exception("Falha na requisição de estatísticas para o vídeo {$videoId}");
        }
        
        // Decodificar resposta JSON
        $data = json_decode($response, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new \Exception("Erro ao decodificar resposta JSON de estatísticas: " . json_last_error_msg());
        }
        
        if (!isset($data['items'][0])) {
            throw new \Exception("Nenhum dado de estatísticas encontrado para o vídeo {$videoId}");
        }
        
        $item = $data['items'][0];
        
        $stats = [
            'views' => isset($item['statistics']['viewCount']) ? intval($item['statistics']['viewCount']) : 0,
            'likes' => isset($item['statistics']['likeCount']) ? intval($item['statistics']['likeCount']) : 0,
            'duration' => isset($item['contentDetails']['duration']) ? $item['contentDetails']['duration'] : 'PT0M0S'
        ];
        
        // Salvar estatísticas no cache
        $this->saveCacheItem($cacheKey, $stats);
        
        return $stats;
    }
    
    /**
     * Obter dados do cache
     * 
     * @param int $maxResults Número de resultados para o cache
     * @return array|false Dados do cache ou false se não houver cache válido
     */
    private function getCache($maxResults) {
        $cacheFile = $this->getCacheFilePath($maxResults);
        
        // Verificar se o arquivo de cache existe
        if (!file_exists($cacheFile)) {
            return false;
        }
        
        // Verificar se o cache expirou
        $cacheTime = filemtime($cacheFile);
        if (time() - $cacheTime > $this->cacheExpiration) {
            return false;
        }
        
        // Ler dados do cache
        $cacheData = file_get_contents($cacheFile);
        $data = json_decode($cacheData, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            // Cache corrompido
            $this->logger->warning("Cache corrompido, removendo arquivo", [
                'file' => $cacheFile
            ]);
            @unlink($cacheFile);
            return false;
        }
        
        return $data;
    }
    
    /**
     * Obter um item específico do cache
     * 
     * @param string $key Chave do item
     * @return mixed|false Dados do cache ou false se não houver cache válido
     */
    private function getCacheItem($key) {
        $cacheFile = $this->getCacheItemPath($key);
        
        // Verificar se o arquivo de cache existe
        if (!file_exists($cacheFile)) {
            return false;
        }
        
        // Verificar se o cache expirou
        $cacheTime = filemtime($cacheFile);
        if (time() - $cacheTime > $this->cacheExpiration) {
            return false;
        }
        
        // Ler dados do cache
        $cacheData = file_get_contents($cacheFile);
        $data = json_decode($cacheData, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            // Cache corrompido
            $this->logger->warning("Cache de item corrompido, removendo arquivo", [
                'key' => $key,
                'file' => $cacheFile
            ]);
            @unlink($cacheFile);
            return false;
        }
        
        return $data;
    }
    
    /**
     * Salvar dados no cache
     * 
     * @param array $data Dados a serem salvos no cache
     * @param int $maxResults Número de resultados para o cache
     * @return bool True se o cache foi salvo com sucesso, false caso contrário
     */
    private function saveCache($data, $maxResults) {
        $cacheFile = $this->getCacheFilePath($maxResults);
        $result = file_put_contents($cacheFile, json_encode($data)) !== false;
        
        if ($result) {
            $this->logger->debug("Cache salvo com sucesso", [
                'file' => $cacheFile,
                'size' => count($data)
            ]);
        } else {
            $this->logger->error("Falha ao salvar cache", [
                'file' => $cacheFile
            ]);
        }
        
        return $result;
    }
    
    /**
     * Salvar um item específico no cache
     * 
     * @param string $key Chave do item
     * @param mixed $data Dados a serem salvos
     * @return bool True se o cache foi salvo com sucesso, false caso contrário
     */
    private function saveCacheItem($key, $data) {
        $cacheFile = $this->getCacheItemPath($key);
        return file_put_contents($cacheFile, json_encode($data)) !== false;
    }
    
    /**
     * Obter caminho do arquivo de cache
     * 
     * @param int $maxResults Número de resultados para o cache
     * @return string Caminho do arquivo de cache
     */
    private function getCacheFilePath($maxResults) {
        return $this->cachePath . '/youtube_cache_' . $this->channelId . '_' . $maxResults . '.json';
    }
    
    /**
     * Obter caminho do arquivo de cache para um item específico
     * 
     * @param string $key Chave do item
     * @return string Caminho do arquivo de cache
     */
    private function getCacheItemPath($key) {
        $safeKey = preg_replace('/[^a-zA-Z0-9_]/', '_', $key);
        return $this->cachePath . '/youtube_cache_item_' . $safeKey . '.json';
    }
    
    /**
     * Limpar todo o cache
     * 
     * @return bool True se o cache foi limpo com sucesso, false caso contrário
     */
    public function clearCache() {
        $pattern = $this->cachePath . '/youtube_cache_*.json';
        $files = glob($pattern);
        
        if (empty($files)) {
            $this->logger->info("Nenhum arquivo de cache encontrado para limpar");
            return true;
        }
        
        $success = true;
        $count = 0;
        foreach ($files as $file) {
            if (unlink($file)) {
                $count++;
            } else {
                $success = false;
                $this->logger->error("Falha ao remover arquivo de cache", [
                    'file' => $file
                ]);
            }
        }
        
        $this->logger->info("Cache limpo com sucesso", [
            'files_removed' => $count
        ]);
        
        return $success;
    }
    
    /**
     * Formatar duração do vídeo
     * 
     * @param string $duration Duração no formato ISO 8601
     * @return string Duração formatada (ex: "10:15")
     */
    public function formatDuration($duration) {
        try {
            // Converter duração ISO 8601 para segundos
            $interval = new \DateInterval($duration);
            $seconds = $interval->h * 3600 + $interval->i * 60 + $interval->s;
            
            // Formatar segundos para MM:SS ou HH:MM:SS
            if ($interval->h > 0) {
                return sprintf('%02d:%02d:%02d', $interval->h, $interval->i, $interval->s);
            } else {
                return sprintf('%02d:%02d', $interval->i, $interval->s);
            }
        } catch (\Exception $e) {
            $this->logger->error("Erro ao formatar duração: " . $e->getMessage(), [
                'duration' => $duration,
                'exception' => get_class($e)
            ]);
            return "00:00";
        }
    }
    
    /**
     * Formatar número de visualizações
     * 
     * @param int $views Número de visualizações
     * @return string Visualizações formatadas (ex: "1.2K")
     */
    public function formatViews($views) {
        if ($views >= 1000000) {
            return round($views / 1000000, 1) . 'M';
        } elseif ($views >= 1000) {
            return round($views / 1000, 1) . 'K';
        } else {
            return $views;
        }
    }
    
    /**
     * Calcular tempo decorrido desde a publicação
     * 
     * @param string $publishedAt Data de publicação no formato ISO 8601
     * @return string Tempo decorrido formatado (ex: "há 2 dias")
     */
    public function timeAgo($publishedAt) {
        try {
            $publishedTime = strtotime($publishedAt);
            $currentTime = time();
            $diff = $currentTime - $publishedTime;
            
            $intervals = [
                31536000 => 'ano',
                2592000 => 'mês',
                604800 => 'semana',
                86400 => 'dia',
                3600 => 'hora',
                60 => 'minuto',
                1 => 'segundo'
            ];
            
            foreach ($intervals as $seconds => $label) {
                $count = floor($diff / $seconds);
                
                if ($count > 0) {
                    if ($count > 1) {
                        // Pluralizar
                        if ($label === 'mês') {
                            $label = 'meses';
                        } else {
                            $label .= 's';
                        }
                    }
                    
                    return "há $count $label";
                }
            }
            
            return 'agora mesmo';
        } catch (\Exception $e) {
            $this->logger->error("Erro ao calcular tempo decorrido: " . $e->getMessage(), [
                'publishedAt' => $publishedAt,
                'exception' => get_class($e)
            ]);
            return 'data desconhecida';
        }
    }
    
    /**
     * Obter array vazio de vídeos para casos de erro
     * 
     * @param int $count Número de itens vazios a retornar
     * @return array Array com vídeos vazios
     */
    private function getEmptyVideoArray($count = 3) {
        $videos = [];
        
        for ($i = 0; $i < $count; $i++) {
            $videos[] = [
                'id' => '',
                'title' => 'Vídeo não disponível',
                'description' => 'Não foi possível carregar os detalhes do vídeo.',
                'publishedAt' => date('c'),
                'thumbnail' => '',
                'url' => '#',
                'embed_url' => '#',
                'views' => 0,
                'likes' => 0,
                'duration' => 'PT0M0S',
                'formatted_duration' => '00:00',
                'formatted_views' => '0',
                'time_ago' => 'indisponível'
            ];
        }
        
        return $videos;
    }
    
    /**
     * Verificar se um vídeo está ao vivo
     * 
     * @param string $videoId ID do vídeo
     * @return bool True se o vídeo estiver ao vivo, false caso contrário
     */
    public function isLiveVideo($videoId) {
        try {
            // Verificar se há cache válido
            $cacheKey = "live_status_{$videoId}";
            $cachedStatus = $this->getCacheItem($cacheKey);
            
            // Usar cache com tempo de expiração menor para status ao vivo
            if ($cachedStatus !== false && (time() - filemtime($this->getCacheItemPath($cacheKey)) < 300)) {
                return $cachedStatus;
            }
            
            // Construir URL da API
            $apiUrl = "https://www.googleapis.com/youtube/v3/videos?";
            $apiUrl .= "key=" . urlencode($this->apiKey);
            $apiUrl .= "&id=" . urlencode($videoId);
            $apiUrl .= "&part=snippet,liveStreamingDetails";
            
            // Fazer requisição à API
            $context = stream_context_create([
                'http' => [
                    'timeout' => 5,
                    'user_agent' => 'PHP YouTubeService'
                ]
            ]);
            
            $response = @file_get_contents($apiUrl, false, $context);
            
            if ($response === false) {
                throw new \Exception("Falha na requisição de status ao vivo para o vídeo {$videoId}");
            }
            
            // Decodificar resposta JSON
            $data = json_decode($response, true);
            
            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new \Exception("Erro ao decodificar resposta JSON: " . json_last_error_msg());
            }
            
            if (!isset($data['items'][0])) {
                return false;
            }
            
            $item = $data['items'][0];
            
            // Verificar se o vídeo está ao vivo
            $isLive = false;
            
            if (isset($item['snippet']['liveBroadcastContent']) && $item['snippet']['liveBroadcastContent'] === 'live') {
                $isLive = true;
            }
            
            if (isset($item['liveStreamingDetails']['actualStartTime']) && !isset($item['liveStreamingDetails']['actualEndTime'])) {
                $isLive = true;
            }
            
            // Salvar status no cache
            $this->saveCacheItem($cacheKey, $isLive);
            
            return $isLive;
            
        } catch (\Exception $e) {
            $this->logger->error("Erro ao verificar status ao vivo: " . $e->getMessage(), [
                'videoId' => $videoId,
                'exception' => get_class($e)
            ]);
            return false;
        }
    }
}
