<?php
/**
 * Kontroler Xtream Check - sprawdzanie statusu kont Xtream Codes + Generator M3U
 */

require_once __DIR__ . '/BaseController.php';

class XtreamCheckController extends BaseController
{
    public function __construct()
    {
        parent::__construct();
        
        // Wymaga zalogowania
        if (!$this->auth->check()) {
            $this->redirect('login');
            exit;
        }
    }
    
    /**
     * Strona główna z formularzem
     */
    public function index(): void
    {
        // Sprawdź czy przekazano parametry z playlisty
        $prefillData = null;
        $server = $_GET['server'] ?? '';
        $user = $_GET['user'] ?? '';
        $pass = $_GET['pass'] ?? '';
        
        if ($server && $user && $pass) {
            $prefillData = [
                'server' => $server,
                'username' => $user,
                'password' => $pass
            ];
        }
        
        $this->render('xtream_check/index.tpl', [
            'pageTitle' => 'Xtream Check',
            'result' => null,
            'prefillData' => $prefillData
        ]);
    }
    
    /**
     * Sprawdź konto Xtream
     */
    public function check(): void
    {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            $this->redirect('xtream-check');
            return;
        }
        
        if (!$this->verifyCsrf()) {
            return;
        }
        
        $inputType = post('input_type', 'url');
        $server = '';
        $username = '';
        $password = '';
        
        if ($inputType === 'url') {
            $url = trim(post('xtream_link', ''));
            
            if (empty($url)) {
                $this->renderWithError('Podaj link do API Xtream.');
                return;
            }
            
            // Parsuj URL
            $parsed = $this->parseXtreamUrl($url);
            
            if (!$parsed) {
                $this->renderWithError('Nieprawidłowy format linku. Obsługiwane formaty: player_api.php lub get.php z parametrami username i password.');
                return;
            }
            
            $server = $parsed['server'];
            $username = $parsed['username'];
            $password = $parsed['password'];
        } else {
            $server = trim(post('xc_host_addr', ''));
            $username = trim(post('xc_api_login', ''));
            $password = trim(post('xc_api_key', ''));
            
            if (empty($server) || empty($username) || empty($password)) {
                $this->renderWithError('Wszystkie pola są wymagane.');
                return;
            }
            
            // Normalizuj serwer
            $server = rtrim($server, '/');
            if (!preg_match('#^https?://#', $server)) {
                $server = 'http://' . $server;
            }
        }
        
        // Sprawdź konto
        $result = $this->checkXtreamAccount($server, $username, $password);
        
        $this->render('xtream_check/index.tpl', [
            'pageTitle' => 'Xtream Check',
            'result' => $result,
            'inputData' => [
                'input_type' => $inputType,
                'url' => $url ?? '',
                'server' => $server,
                'username' => $username,
                'password' => $password // Potrzebne do generatora M3U
            ]
        ]);
    }
    
    /**
     * Parsuj URL Xtream
     */
    private function parseXtreamUrl(string $url): ?array
    {
        // Format 1: http://server.com:port/player_api.php?username=xxx&password=yyy
        // Format 2: http://server.com:port/get.php?username=xxx&password=yyy&type=m3u_plus&output=ts
        // Format 3: http://server.com:port/xxx/yyy (username/password w ścieżce)
        
        $parsed = parse_url($url);
        
        if (!$parsed || !isset($parsed['host'])) {
            return null;
        }
        
        // Zbuduj bazowy URL serwera
        $scheme = $parsed['scheme'] ?? 'http';
        $host = $parsed['host'];
        $port = isset($parsed['port']) ? ':' . $parsed['port'] : '';
        $server = "{$scheme}://{$host}{$port}";
        
        $username = null;
        $password = null;
        
        // Spróbuj pobrać z query string
        if (isset($parsed['query'])) {
            parse_str($parsed['query'], $query);
            $username = $query['username'] ?? $query['user'] ?? null;
            $password = $query['password'] ?? $query['pass'] ?? null;
        }
        
        // Jeśli nie ma w query, spróbuj ze ścieżki
        if ((!$username || !$password) && isset($parsed['path'])) {
            $path = trim($parsed['path'], '/');
            $parts = explode('/', $path);
            
            // Format: /username/password lub /live/username/password
            if (count($parts) >= 2) {
                // Sprawdź czy to nie jest plik php
                $lastPart = end($parts);
                if (strpos($lastPart, '.php') === false) {
                    if (count($parts) === 2) {
                        $username = $parts[0];
                        $password = $parts[1];
                    } elseif (count($parts) >= 3 && in_array($parts[0], ['live', 'movie', 'series', 'get'])) {
                        $username = $parts[1];
                        $password = $parts[2];
                    }
                }
            }
        }
        
        if (!$username || !$password) {
            return null;
        }
        
        return [
            'server' => $server,
            'username' => $username,
            'password' => $password
        ];
    }
    
    /**
     * Sprawdź konto Xtream przez API
     */
    private function checkXtreamAccount(string $server, string $username, string $password): array
    {
        $result = [
            'success' => false,
            'server' => $server,
            'username' => $username,
            'error' => null,
            'user_info' => null,
            'server_info' => null,
            'categories' => null,
            'polish_categories' => null,
            'stats' => null
        ];
        
        // 1. Pobierz podstawowe info o koncie
        $apiUrl = "{$server}/player_api.php?username={$username}&password={$password}";
        
        $response = $this->makeRequest($apiUrl);
        
        if (!$response['success']) {
            $result['error'] = $response['error'];
            return $result;
        }
        
        $data = json_decode($response['body'], true);
        
        if (!$data) {
            $result['error'] = 'Nieprawidłowa odpowiedź z serwera (nie JSON).';
            return $result;
        }
        
        if (isset($data['user_info']['auth']) && $data['user_info']['auth'] == 0) {
            $result['error'] = 'Błąd autoryzacji - nieprawidłowy login lub hasło.';
            return $result;
        }
        
        $result['success'] = true;
        $result['user_info'] = $data['user_info'] ?? null;
        $result['server_info'] = $data['server_info'] ?? null;
        
        // Oblicz dni do wygaśnięcia
        if (isset($result['user_info']['exp_date']) && $result['user_info']['exp_date']) {
            $expTimestamp = (int)$result['user_info']['exp_date'];
            $result['user_info']['exp_date_formatted'] = date('d.m.Y H:i', $expTimestamp);
            $result['user_info']['days_left'] = (int)ceil(($expTimestamp - time()) / 86400);
        }
        
        // Oblicz procent połączeń
        if (isset($result['user_info']['max_connections']) && $result['user_info']['max_connections'] > 0) {
            $activeCons = (int)($result['user_info']['active_cons'] ?? 0);
            $maxCons = (int)$result['user_info']['max_connections'];
            $result['user_info']['conn_percent'] = round(($activeCons / $maxCons) * 100);
        }
        
        // 2. Pobierz kategorie Live
        $liveCategories = $this->fetchCategories($server, $username, $password, 'live');
        $vodCategories = $this->fetchCategories($server, $username, $password, 'vod');
        $seriesCategories = $this->fetchCategories($server, $username, $password, 'series');
        
        $result['categories'] = [
            'live' => $liveCategories,
            'vod' => $vodCategories,
            'series' => $seriesCategories
        ];
        
        // 3. Znajdź polskie kategorie
        // UWAGA: Szukamy tylko wyraźnie polskich oznaczeń, nie globalnych marek!
        $polishKeywords = [
            // Bezpośrednie określenia kraju/języka
            'poland', 'polish', 'polska', 'polskie', 'polski', 'polskich', 'polsko',
            '|pl|', '| pl|', '|pl |', ' pl|', 'pl|', ' pl |', 'pl |', ' pl ', 'pl:', '|PL', ':pl','PL ',
            
            // Wyłącznie polskie stacje TV (nie występują w innych krajach)
            'tvp', 'tvp1', 'tvp2', 'tvp3', 'tvp info', 'tvp sport', 'tvp hd', 'tvp historia', 'tvp kultura', 'tvp seriale', 'tvp rozrywka', 'tvp abc', 'tvp dokument',
            'polsat', 'super polsat', 'polsat sport', 'polsat news', 'polsat film', 'polsat seriale', 'polsat cafe', 'polsat play', 'polsat romans', 'polsat games',
            'tvn', 'tvn24', 'tvn7', 'tvn turbo', 'tvn style', 'tvn fabuła',
            'tv puls', 'puls 2',
            'ttv',
            'tv trwam', 'trwam',
            'kino polska', 'stopklatka', 'ale kino', 'filmax',
            'nowa tv', 'zoom tv', 'metro tv',
            'polo tv', 'eska tv', 'vox music tv',
            'fokus tv', 'wp tv',
            'eleven sports', 'sportklub',
            
            // Polskie miasta (unikalne konteksty)
            'warszawa', 'kraków', 'krakow', 'wrocław', 'wroclaw', 'poznań', 'poznan', 
            'gdańsk', 'gdansk', 'łódź', 'lodz', 'katowice', 'szczecin', 'lublin', 
            'białystok', 'bialystok', 'rzeszów', 'rzeszow', 'bydgoszcz', 'olsztyn',
            
            // Polskie frazy
            'filmy polskie', 'seriale polskie', 'polskie filmy', 'polskie seriale',
            'telewizja polska', 'kanały polskie', 'kanaly polskie',
            'bajki pl', 'dla dzieci pl',
            
            // Sport polski
            'ekstraklasa', 'lotto ekstraklasa', 'liga polska',
            
            // Muzyka polska
            'disco polo', 'muzyka polska'
        ];
        $result['polish_categories'] = [
            'live' => $this->findPolishCategories($liveCategories, $polishKeywords),
            'vod' => $this->findPolishCategories($vodCategories, $polishKeywords),
            'series' => $this->findPolishCategories($seriesCategories, $polishKeywords)
        ];
        
        // 4. Statystyki
        $result['stats'] = [
            'live_categories' => count($liveCategories),
            'vod_categories' => count($vodCategories),
            'series_categories' => count($seriesCategories),
            'total_categories' => count($liveCategories) + count($vodCategories) + count($seriesCategories),
            'has_polish' => !empty($result['polish_categories']['live']) || 
                           !empty($result['polish_categories']['vod']) || 
                           !empty($result['polish_categories']['series'])
        ];
        
        return $result;
    }
    
    /**
     * Pobierz kategorie danego typu
     */
    private function fetchCategories(string $server, string $username, string $password, string $type): array
    {
        $action = match($type) {
            'live' => 'get_live_categories',
            'vod' => 'get_vod_categories',
            'series' => 'get_series_categories',
            default => 'get_live_categories'
        };
        
        $url = "{$server}/player_api.php?username={$username}&password={$password}&action={$action}";
        $response = $this->makeRequest($url);
        
        if (!$response['success']) {
            return [];
        }
        
        $data = json_decode($response['body'], true);
        
        return is_array($data) ? $data : [];
    }
    
    /**
     * Znajdź polskie kategorie
     */
    private function findPolishCategories(array $categories, array $keywords): array
    {
        $polish = [];
        
        // Krótkie słowa wymagające dopasowania jako całe słowo lub z separatorami
        // Te słowa mogą być częścią innych słów więc muszą być otoczone separatorami
        $shortKeywords = ['ttv', 'tvn', 'tvp'];
        
        foreach ($categories as $category) {
            $name = strtolower($category['category_name'] ?? '');
            $found = false;
            
            // Najpierw sprawdź czy to NIE jest kategoria innego kraju (NL, EN, DE, etc.)
            // Jeśli nazwa zaczyna się od kodu innego kraju, pomijamy
            if (preg_match('/^[\s\|]*(?:nl|en|de|fr|it|es|uk|us|ru|tr|ar|in|pt|se|no|dk|fi|be|at|ch)\s*\|/i', $name)) {
                continue;
            }
            
            foreach ($keywords as $keyword) {
                $keywordLower = strtolower($keyword);
                
                // Dla krótkich słów kluczowych - szukaj jako całe słowo lub z separatorami
                if (in_array($keywordLower, $shortKeywords)) {
                    // Szukaj jako całe słowo (otoczone spacjami, początkiem/końcem, lub separatorami)
                    $pattern = '/(?:^|[\s\|\-\_\:\.\,\(\)\[\]])' . preg_quote($keywordLower, '/') . '(?:$|[\s\|\-\_\:\.\,\(\)\[\]\d])/';
                    if (preg_match($pattern, $name)) {
                        $found = true;
                        break;
                    }
                } else {
                    // Dla dłuższych słów - zwykłe wyszukiwanie
                    if (strpos($name, $keywordLower) !== false) {
                        $found = true;
                        break;
                    }
                }
            }
            
            if ($found) {
                $polish[] = $category;
            }
        }
        
        return $polish;
    }
    
    /**
     * Wykonaj request HTTP
     */
    private function makeRequest(string $url, int $timeout = 15): array
    {
        $ch = curl_init();
        
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            CURLOPT_HTTPHEADER => [
                'Accept: application/json',
                'Accept-Language: en-US,en;q=0.9'
            ]
        ]);
        
        $body = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        if ($error) {
            return [
                'success' => false,
                'error' => "Błąd połączenia: {$error}",
                'body' => null
            ];
        }
        
        if ($httpCode !== 200) {
            return [
                'success' => false,
                'error' => "Serwer zwrócił błąd HTTP {$httpCode}.",
                'body' => null
            ];
        }
        
        return [
            'success' => true,
            'error' => null,
            'body' => $body
        ];
    }
    
    /**
     * Renderuj z błędem
     */
    private function renderWithError(string $error): void
    {
        $this->render('xtream_check/index.tpl', [
            'pageTitle' => 'Xtream Check',
            'result' => null,
            'error' => $error,
            'inputData' => [
                'input_type' => post('input_type', 'url'),
                'url' => post('xtream_link', ''),
                'server' => post('xc_host_addr', ''),
                'username' => post('xc_api_login', '')
            ]
        ]);
    }
    
    /**
     * Pobierz streamy dla danego typu i kategorii (AJAX)
     */
    public function getStreams(): void
    {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            $this->json(['success' => false, 'error' => 'Invalid method']);
            return;
        }
        
        $server = post('server');
        $username = post('username');
        $password = post('password');
        $type = post('type', 'live');
        $categoryId = post('category_id');
        
        if (!$server || !$username || !$password) {
            $this->json(['success' => false, 'error' => 'Missing credentials']);
            return;
        }
        
        $action = match($type) {
            'live' => 'get_live_streams',
            'vod' => 'get_vod_streams',
            'series' => 'get_series',
            default => 'get_live_streams'
        };
        
        $url = "{$server}/player_api.php?username={$username}&password={$password}&action={$action}";
        if ($categoryId) {
            $url .= "&category_id={$categoryId}";
        }
        
        $response = $this->makeRequest($url);
        
        if (!$response['success']) {
            $this->json(['success' => false, 'error' => $response['error']]);
            return;
        }
        
        $streams = json_decode($response['body'], true);
        
        $this->json([
            'success' => true,
            'streams' => is_array($streams) ? $streams : [],
            'count' => is_array($streams) ? count($streams) : 0
        ]);
    }
    
    /**
     * Generuj plik M3U
     */
    public function generateM3u(): void
    {
        // Zwiększ limity dla dużych playlist
        set_time_limit(600); // 10 minut
        ini_set('memory_limit', '512M');
        
        // Wyłącz buforowanie output
        if (ob_get_level()) {
            ob_end_clean();
        }
        
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            $this->redirect('xtream-check');
            return;
        }
        
        if (!$this->verifyCsrf()) {
            return;
        }
        
        $server = post('server');
        $username = post('username');
        $password = post('password');
        $selectedCategories = post('categories', []);
        
        if (!$server || !$username || !$password) {
            $this->redirect('xtream-check', 'danger', 'Brak danych serwera.');
            return;
        }
        
        // Sprawdź czy cokolwiek zaznaczono
        $hasAnyCategory = false;
        foreach ($selectedCategories as $type => $cats) {
            if (!empty($cats)) {
                $hasAnyCategory = true;
                break;
            }
        }
        
        if (!$hasAnyCategory) {
            $this->redirect('xtream-check', 'danger', 'Wybierz przynajmniej jedną kategorię.');
            return;
        }
        
        // Normalizuj serwer
        $server = rtrim($server, '/');
        if (!preg_match('#^https?://#', $server)) {
            $server = 'http://' . $server;
        }
        
        $m3u = "#EXTM3U\n";
        $totalStreams = 0;
        
        // Pobierz nazwy kategorii dla wszystkich typów
        $categoryNames = [];
        foreach (['live', 'vod', 'series'] as $type) {
            if (empty($selectedCategories[$type])) continue;
            
            $catAction = match($type) {
                'live' => 'get_live_categories',
                'vod' => 'get_vod_categories',
                'series' => 'get_series_categories',
                default => null
            };
            
            if ($catAction) {
                $catUrl = "{$server}/player_api.php?username={$username}&password={$password}&action={$catAction}";
                $catResponse = $this->makeRequest($catUrl);
                if ($catResponse['success']) {
                    $cats = json_decode($catResponse['body'], true);
                    if (is_array($cats)) {
                        foreach ($cats as $cat) {
                            $categoryNames[$cat['category_id']] = $cat['category_name'];
                        }
                    }
                }
            }
        }
        
        // Generuj dla każdego typu
        foreach (['live', 'vod', 'series'] as $type) {
            $typeCategories = $selectedCategories[$type] ?? [];
            
            // Pomijamy typ jeśli nie wybrano kategorii
            if (empty($typeCategories)) {
                continue;
            }
            
            // Konwertuj na tablicę ID dla szybkiego sprawdzania
            $selectedCatIds = array_flip($typeCategories);
            
            $action = match($type) {
                'live' => 'get_live_streams',
                'vod' => 'get_vod_streams',
                'series' => 'get_series',
                default => null
            };
            
            if (!$action) continue;
            
            // Pobierz WSZYSTKIE strumienie danego typu na raz (bez category_id)
            $streamUrl = "{$server}/player_api.php?username={$username}&password={$password}&action={$action}";
            
            $response = $this->makeRequest($streamUrl, 120); // Dłuższy timeout dla dużych list
            if (!$response['success']) continue;
            
            $allStreams = json_decode($response['body'], true);
            if (!is_array($allStreams)) continue;
            
            // Filtruj strumienie tylko z wybranych kategorii
            $filteredStreams = [];
            foreach ($allStreams as $stream) {
                $streamCatId = $stream['category_id'] ?? '';
                if (isset($selectedCatIds[$streamCatId])) {
                    $filteredStreams[] = $stream;
                }
            }
            
            if ($type === 'series' && !empty($filteredStreams)) {
                // Dla seriali - pobierz wszystkie info równolegle używając curl_multi
                $seriesData = $this->fetchSeriesInfoBatch($filteredStreams, $server, $username, $password);
                
                foreach ($filteredStreams as $stream) {
                    $seriesId = $stream['series_id'] ?? null;
                    if (!$seriesId || !isset($seriesData[$seriesId])) continue;
                    
                    $seriesInfo = $seriesData[$seriesId];
                    if (empty($seriesInfo['episodes'])) continue;
                    
                    $seriesName = $stream['name'] ?? 'Unknown Series';
                    $logo = $stream['cover'] ?? '';
                    $catId = $stream['category_id'] ?? '';
                    $groupTitle = $categoryNames[$catId] ?? 'Seriale';
                    
                    foreach ($seriesInfo['episodes'] as $seasonNum => $episodes) {
                        foreach ($episodes as $episode) {
                            $epNum = $episode['episode_num'] ?? '00';
                            $epTitle = $episode['title'] ?? "Odcinek {$epNum}";
                            $epId = $episode['id'] ?? '';
                            
                            $fullTitle = sprintf('%s S%02dE%02d - %s', 
                                $seriesName, (int)$seasonNum, (int)$epNum, $epTitle);
                            
                            $m3u .= sprintf(
                                '#EXTINF:-1 tvg-name="%s" tvg-logo="%s" group-title="%s",%s' . "\n",
                                $this->escapeM3u($fullTitle),
                                $this->escapeM3u($logo),
                                $this->escapeM3u($groupTitle),
                                $fullTitle
                            );
							            $ext = $stream['container_extension'] ?? 'mp4';
							$m3u .= "{$server}/series/{$username}/{$password}/{$epId}.{$ext}\n";
                            $totalStreams++;
                        }
                    }
                }
            } else {
                // Live/VOD - standardowe przetwarzanie
                foreach ($filteredStreams as $stream) {
                    $m3u .= $this->buildM3uEntry($stream, $type, $server, $username, $password, $categoryNames);
                    $totalStreams++;
                }
            }
        }
        
        if ($totalStreams === 0) {
            $this->redirect('xtream-check', 'warning', 'Nie znaleziono żadnych strumieni dla wybranych kategorii.');
            return;
        }
        
        // Generuj nazwę pliku
        $types = [];
        if (!empty($selectedCategories['live'])) $types[] = 'live';
        if (!empty($selectedCategories['vod'])) $types[] = 'vod';
        if (!empty($selectedCategories['series'])) $types[] = 'series';
        
        $filename = sprintf('%s_%s_%s.m3u', 
            preg_replace('/[^a-zA-Z0-9]/', '', $username),
            implode('-', $types),
            date('Ymd_His')
        );
        
        // Wyślij plik
        header('Content-Type: application/x-mpegurl; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Length: ' . strlen($m3u));
        header('Cache-Control: no-cache, no-store, must-revalidate');
        echo $m3u;
        exit;
    }
    
    /**
     * Buduj wpis M3U dla streamu
     * Live - bez rozszerzenia
     * VOD - wymaga rozszerzenia (container_extension)
     */
    private function buildM3uEntry(array $stream, string $type, string $server, string $username, string $password, array $categoryNames): string
    {
        $name = $stream['name'] ?? 'Unknown';
        $streamId = $stream['stream_id'] ?? ($stream['series_id'] ?? '');
        $logo = $stream['stream_icon'] ?? ($stream['cover'] ?? '');
        $catId = $stream['category_id'] ?? '';
        $groupTitle = $categoryNames[$catId] ?? 'Uncategorized';
        $epgId = $stream['epg_channel_id'] ?? '';
        
        // Ścieżka URL
        $path = match($type) {
            'live' => 'live',
            'vod' => 'movie',
            'series' => 'series',
            default => 'movie'
        };
        
        $entry = '#EXTINF:-1';
        if ($epgId) {
            $entry .= ' tvg-id="' . $this->escapeM3u($epgId) . '"';
        }
        $entry .= ' tvg-name="' . $this->escapeM3u($name) . '"';
        if ($logo) {
            $entry .= ' tvg-logo="' . $this->escapeM3u($logo) . '"';
        }
        $entry .= ' group-title="' . $this->escapeM3u($groupTitle) . '"';
        $entry .= ',' . $name . "\n";
        
        // URL - VOD wymaga rozszerzenia, Live nie
        if ($type === 'vod' || $type === 'series' ) {
            $ext = $stream['container_extension'] ?? 'mp4';
            $entry .= "{$server}/{$path}/{$username}/{$password}/{$streamId}.{$ext}\n";
        } else {
            $entry .= "{$server}/{$path}/{$username}/{$password}/{$streamId}.m3u8\n";
        }
        
        return $entry;
    }
    
    /**
     * Pobierz info o serialach równolegle (curl_multi)
     * Znacznie szybsze niż sekwencyjne zapytania
     */
    private function fetchSeriesInfoBatch(array $seriesList, string $server, string $username, string $password): array
    {
        $results = [];
        $batchSize = 20; // Ile równoległych połączeń na raz
        $chunks = array_chunk($seriesList, $batchSize);
        
        foreach ($chunks as $chunk) {
            $multiHandle = curl_multi_init();
            $handles = [];
            
            foreach ($chunk as $series) {
                $seriesId = $series['series_id'] ?? null;
                if (!$seriesId) continue;
                
                $url = "{$server}/player_api.php?username={$username}&password={$password}&action=get_series_info&series_id={$seriesId}";
                
                $ch = curl_init();
                curl_setopt_array($ch, [
                    CURLOPT_URL => $url,
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_TIMEOUT => 30,
                    CURLOPT_CONNECTTIMEOUT => 10,
                    CURLOPT_FOLLOWLOCATION => true,
                    CURLOPT_SSL_VERIFYPEER => false,
                    CURLOPT_SSL_VERIFYHOST => false,
                    CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                ]);
                
                curl_multi_add_handle($multiHandle, $ch);
                $handles[$seriesId] = $ch;
            }
            
            // Wykonaj wszystkie zapytania równolegle
            $running = null;
            do {
                curl_multi_exec($multiHandle, $running);
                curl_multi_select($multiHandle);
            } while ($running > 0);
            
            // Pobierz wyniki
            foreach ($handles as $seriesId => $ch) {
                $response = curl_multi_getcontent($ch);
                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                
                if ($httpCode === 200 && $response) {
                    $data = json_decode($response, true);
                    if (is_array($data)) {
                        $results[$seriesId] = $data;
                    }
                }
                
                curl_multi_remove_handle($multiHandle, $ch);
                curl_close($ch);
            }
            
            curl_multi_close($multiHandle);
        }
        
        return $results;
    }
    
    /**
     * Escapuj wartość dla M3U
     */
    private function escapeM3u(string $value): string
    {
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
    }
}
