<?php

/**
 * Fetch live Fanatiz events and output a JSON playlist for their streams.
 *
 * - Lists live events according to multiple live flags.
 * - Fetches each live event's stream URL and saves a JSON array with stream metadata to a file.
 */

const EVENTS_URL = 'https://watch.fanatiz.com/api/sports/events?sortByAsc=startDate%2CstatusCategory%2C__belong.Tournament&filterBy-statusCategory=live%2Cfuture&filterBy-__belong.Tournament=67dc3ed9b044c008d5fdd5c5&itemsPerPage=10';



const VIDEO_URL_TEMPLATE = 'https://watch.fanatiz.com/api/assets/videos/%s?json=true';
const OUTPUT_FILE = 'eventos_tigo.json';

$sharedHeaders = [
    'accept: */*',
    'accept-language: es-419,es;q=0.9,en;q=0.8',
    'cache-control: no-cache',
    'pragma: no-cache',
    'priority: u=1, i',
    'sec-ch-ua: "Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
    'sec-ch-ua-mobile: ?0',
    'sec-ch-ua-platform: "Linux"',
    'sec-fetch-dest: empty',
    'sec-fetch-mode: cors',
    'sec-fetch-site: same-origin',
    'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
];

$cookieJar = '__cmpcc=1; __cmpconsent54618=CQY3-bgQY3-bgAfbXBESB_FgAAAAAAAAAAigF5wBgAAgAQAAyAFsAXIC8wLzgAgLzAAA; __cmpccpausps=1YNN; _ga=GA1.1.207661758.1759784256; watchfanatiz.sid=s%3AJwrok2r-yGodXuwYl7ifOa-6ljt6Isz-.j5ke3SI%2FFHlowJEM0pMRHMFvbiBAhdNuNSj%2BEg3z4rI; _ga=GA1.3.207661758.1759784256; _gid=GA1.3.1217958767.1759784258; __stripe_mid=c52bbb44-1ce8-4018-8dba-71ce9d237fad519340; __stripe_sid=fd565792-6cac-44d2-9f24-63addb9638afe4e99f; IR_gbd=fanatiz.com; _fbp=fb.1.1759784260782.304841749623069288; _scid=pRa8rTIm02V9fj7CmdD5QKkreC76tPqp; _tt_enable_cookie=1; _ttp=01K6XN1NH9PZMB1H5VM3YWMXGY_.tt.1; _ScCbts=%5B%5D; intercom-id-ppekucfa=9c99b9b4-e6a8-4fd3-939e-6fea86e24ee0; intercom-device-id-ppekucfa=74758314-7863-4637-9d36-c57999287514; ajs_anonymous_id=c6b9108a-f5e3-4dd5-a169-03ed20bb0802; __cmpcccu54618=aCQY6cTbgBaXmBK01qxrWmmMTViDS0JGEJhl0A8TSixGjSYiBggxMhWiZJoyGliBpUaWBMAYiCRHkEMdJeCoVgBTEKRY; _hjSession_3692240=eyJpZCI6IjA4NWZkOWRmLTNlYjgtNGFmNy04NTg1LTZiYTgyYzY4OGJjZiIsImMiOjE3NTk3ODQyNjU2NzcsInMiOjAsInIiOjAsInNiIjowLCJzciI6MCwic2UiOjAsImZzIjoxLCJzcCI6MX0=; _sctr=1%7C1759719600000; _clck=s759jj%5E2%5Efzx%5E1%5E2105; _hjSessionUser_3692240=eyJpZCI6IjA0YWIxMDUwLWRiNTktNTgxZS05NDczLTJlMjVlMjQ0MDNlYiIsImNyZWF0ZWQiOjE3NTk3ODQyNjU2NzYsImV4aXN0aW5nIjp0cnVlfQ==; ajs_user_id=68e2f4ec31a55c7c849d3814; _gcl_au=1.1.504422061.1759784256.952152113.1759785272.1759785276; cookieProducts-geovanni.almiron@gmail.com=true; cookieOrders-geovanni.almiron@gmail.com=true; _ga_54Z2R1X58L=GS2.1.s1759784255$o1$g1$t1759787143$j56$l0$h0; _uetsid=199727f0a2f711f0924b310c07235e66; _uetvid=19975ed0a2f711f0b26a21967831eb2f; IR_10706=1759787144216%7C0%7C1759787144216%7C%7C; _scid_r=opa8rTIm02V9fj7CmdD5QKkreC76tPqpVMgGgw; ttcsid=1759784261164::RPF-kVU5Q5QxO4VCZsVx.1.1759787146221.0; ttcsid_CQVM0P3C77U5R0PTKL5G=1759784261164::AGUqD67halxFOY4hD-a2.1.1759787146221.0; _gat_clientSpecific=1; _gat_allDeployments=1; _ga_L3ZGEBGKNW=GS2.3.s1759784260$o1$g1$t1759787150$j60$l0$h0; _ga_QZ5RT3N2R2=GS2.3.s1759784260$o1$g1$t1759787150$j60$l0$h0; intercom-session-ppekucfa=QWs4NFBrYzRRSndiK2dlQXNFSGRsY1EvcHpxaDRBc1FTaVl2YTNlYW5ZdWRodXZrTDNJRHZxQ0l5SFpHekhoa29VeUVKMTdDOG9Bcm5JMFBLNzltM0hkK1VPeUpGYnYxVTdqYlVtcWhEekU9LS11QjVVYlpJOWhaTnVXWUw5U0NaaFhRPT0=--2987267b4294b15f6df59afd0b3110bd002b6e32; _clsk=ajti3g%5E1759787154199%5E1%5E1%5El.clarity.ms%2Fcollect';

try {
    $eventsResponse = fetchJson(EVENTS_URL, $sharedHeaders, $cookieJar);
} catch (RuntimeException $e) {
    fwrite(STDERR, "Error al obtener eventos: {$e->getMessage()}" . PHP_EOL);
    exit(1);
}

$items = $eventsResponse['data']['items'] ?? [];
$sections = $eventsResponse['data']['sections'] ?? [];
$defaultTournament = $sections[0]['title']['original'] ?? 'Torneo desconocido';

$tournamentMap = [];
foreach ($sections as $section) {
    if (!empty($section['_id'])) {
        $tournamentMap[$section['_id']] = $section['title']['original'] ?? $defaultTournament;
    }
}

$liveEvents = [];
$videoRequests = [];

foreach ($items as $event) {
    if (!isLiveEvent($event)) {
        continue;
    }

    $eventId = $event['id'] ?? null;
    $tournament = $defaultTournament;

    foreach ($event['__belong']['Tournament'] ?? [] as $tournamentId) {
        if (isset($tournamentMap[$tournamentId])) {
            $tournament = $tournamentMap[$tournamentId];
            break;
        }
    }

    $homeTeam = $event['homeTeam']['name'] ?? 'Equipo local desconocido';
    $awayTeam = $event['awayTeam']['name'] ?? 'Equipo visitante desconocido';
    $startDate = formatLocalTime($event['startDate'] ?? '');

    $videos = [];
    foreach ($event['videos'] ?? [] as $video) {
        if (empty($video['id'])) {
            continue;
        }

        $videoId = (string) $video['id'];
        $streamName = $video['name']['es'] ?? ($video['name']['original'] ?? 'Stream');
        $stream = [
            'id' => $videoId,
            'name' => $streamName,
            'language' => $video['language'] ?? 'es',
            'streamUrl' => null,
        ];

        $videos[] = $stream;
        $videoRequests[] = [
            'videoId' => $videoId,
            'eventId' => $eventId,
        ];
    }

    $liveEvents[] = [
        'tournament' => $tournament ?: $defaultTournament,
        'homeTeam' => $homeTeam,
        'awayTeam' => $awayTeam,
        'startDate' => $startDate,
        'programmingStatus' => $event['programmingData']['status'] ?? '',
        'statusCategory' => $event['statusCategory'] ?? '',
        'eventStatusCategory' => $event['eventStatus']['category'] ?? '',
        'videos' => $videos,
    ];
}

if (!empty($videoRequests)) {
    $streamUrls = fetchStreamUrlsBatch($videoRequests, $sharedHeaders, $cookieJar);

    foreach ($liveEvents as &$liveEvent) {
        foreach ($liveEvent['videos'] as &$video) {
            $videoId = $video['id'];
            if ($videoId !== null && isset($streamUrls[$videoId])) {
                $video['streamUrl'] = $streamUrls[$videoId];
            } else {
                $video['streamUrl'] = 'URL no disponible';
            }
        }
    }
    unset($liveEvent, $video);
}

if (empty($liveEvents)) {
    echo "No hay eventos en vivo actualmente." . PHP_EOL;
    exit(0);
}

$jsonOutput = buildJsonList($liveEvents);

// Guardar JSON en archivo
try {
    $jsonString = json_encode($jsonOutput, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    file_put_contents(OUTPUT_FILE, $jsonString);
    echo "JSON guardado correctamente en: " . OUTPUT_FILE . PHP_EOL;
} catch (Exception $e) {
    fwrite(STDERR, "Error al guardar el archivo JSON: {$e->getMessage()}" . PHP_EOL);
    exit(1);
}

/**
 * Determine if an event is live based on the provided criteria.
 */
function isLiveEvent(array $event): bool
{
    $statusCategory = strtolower((string) ($event['statusCategory'] ?? ''));
    $programmingStatus = strtolower((string) ($event['programmingData']['status'] ?? ''));
    $eventStatusCategory = strtolower((string) ($event['eventStatus']['category'] ?? ''));

    return in_array('live', [$statusCategory, $programmingStatus, $eventStatusCategory], true);
}

/**
 * Convert an ISO timestamp to the server's local timezone if possible.
 */
function formatLocalTime(string $isoDate): string
{
    if ($isoDate === '') {
        return 'Sin fecha';
    }

    try {
        $date = new DateTimeImmutable($isoDate);
        $local = $date->setTimezone(new DateTimeZone(date_default_timezone_get()));
        return $local->format('Y-m-d H:i:s T');
    } catch (Exception $e) {
        return $isoDate;
    }
}

/**
 * Fetch JSON using cURL and decode the response.
 *
 * @throws RuntimeException when the request fails or JSON is invalid.
 */
function fetchJson(string $url, array $headers, string $cookieJar): array
{
    $response = makeCurlRequest($url, $headers, $cookieJar);
    $decoded = json_decode($response, true);

    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new RuntimeException('JSON inválido recibido desde ' . $url);
    }

    return $decoded;
}

/**
 * Fetch multiple HLS URLs concurrently using cURL multi.
 */
function fetchStreamUrlsBatch(array $requests, array $headers, string $cookieJar): array
{
    if (empty($requests)) {
        return [];
    }

    $multiHandle = curl_multi_init();
    $handles = [];

    foreach ($requests as $request) {
        if (empty($request['videoId'])) {
            continue;
        }

        $videoId = (string) $request['videoId'];
        $eventId = $request['eventId'] ?? null;
        $referer = $eventId ? sprintf('https://watch.fanatiz.com/match-detail/%s', $eventId) : 'https://watch.fanatiz.com/';
        $requestHeaders = array_merge($headers, [
            'referer: ' . $referer,
            'cookie: ' . $cookieJar,
        ]);

        $url = sprintf(VIDEO_URL_TEMPLATE, urlencode($videoId));
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $requestHeaders,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_FOLLOWLOCATION => true,
        ]);

        curl_multi_add_handle($multiHandle, $ch);
        $handles[] = [
            'handle' => $ch,
            'videoId' => $videoId,
        ];
    }

    if (empty($handles)) {
        curl_multi_close($multiHandle);
        return [];
    }

    $active = null;
    do {
        $status = curl_multi_exec($multiHandle, $active);
        if ($status === CURLM_CALL_MULTI_PERFORM) {
            continue;
        }

        if ($status !== CURLM_OK) {
            break;
        }

        if ($active) {
            $select = curl_multi_select($multiHandle, 1.0);
            if ($select === -1) {
                usleep(100000);
            }
        }
    } while ($active);

    $results = [];

    foreach ($handles as $entry) {
        $ch = $entry['handle'];
        $videoId = $entry['videoId'];

        $error = curl_errno($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $body = curl_multi_getcontent($ch);

        if ($error !== 0) {
            $results[$videoId] = 'URL no disponible (Error de cURL ' . $error . ')';
        } elseif ($httpCode >= 400) {
            $results[$videoId] = 'URL no disponible (HTTP ' . $httpCode . ')';
        } elseif ($body === false || $body === '') {
            $results[$videoId] = 'URL no disponible (Respuesta vacía)';
        } else {
            $decoded = json_decode($body, true);
            if (json_last_error() === JSON_ERROR_NONE && isset($decoded['data']['url'])) {
                $results[$videoId] = $decoded['data']['url'];
            } else {
                $results[$videoId] = 'URL no disponible (JSON inválido)';
            }
        }

        curl_multi_remove_handle($multiHandle, $ch);
        curl_close($ch);
    }

    curl_multi_close($multiHandle);

    return $results;
}

/**
 * Generic cURL helper returning the raw string response.
 *
 * @throws RuntimeException when cURL fails or HTTP error.
 */
function makeCurlRequest(string $url, array $headers, string $cookieJar): string
{
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => array_merge($headers, ['cookie: ' . $cookieJar]),
        CURLOPT_TIMEOUT => 30,
        CURLOPT_FOLLOWLOCATION => true,
    ]);

    $body = curl_exec($ch);
    $errno = curl_errno($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($errno !== 0) {
        throw new RuntimeException('Error de cURL (' . $errno . ')');
    }

    if ($status >= 400) {
        throw new RuntimeException('HTTP ' . $status);
    }

    if ($body === false) {
        throw new RuntimeException('Respuesta vacía de ' . $url);
    }

    return $body;
}

/**
 * Build a JSON-friendly structure with all live event streams.
 */
function buildJsonList(array $events): array
{
    $logo = 'https://play-lh.googleusercontent.com/gJ1aYRGr9aM6SP9Z7drQHstOrCFOs1o3jJ_SmXHll9HROEjr5DTTuqpM8bIJ0_9cKQM';
    $referer = 'https://watch.fanatiz.com/channels';

    $entries = [];

    foreach ($events as $event) {
        foreach ($event['videos'] as $video) {
            if (!isset($video['streamUrl']) || startsWith($video['streamUrl'], 'URL no disponible')) {
                continue;
            }

            $displayName = sprintf('%s vs %s (%s)', $event['homeTeam'], $event['awayTeam'], $video['language']);

            $entries[] = [
                'name' => $displayName,
                'uri' => $video['streamUrl'],
                'logo' => $logo,
                'drm_scheme' => 'clearkey',
                'drm_license_uri' => '',
                'headers' => [
                    'Referer' => $referer,
                ]
            ];
        }
    }

    return $entries;
}

/**
 * Polyfill for str_starts_with for older PHP versions.
 */
function startsWith(string $haystack, string $needle): bool
{
    return $needle === '' || strpos($haystack, $needle) === 0;
}