<?php
declare(strict_types=1);
require __DIR__ . '/../lib/bootstrap.php';

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'OPTIONS') {
    apply_cors_headers();
    header('Access-Control-Allow-Headers: Content-Type, Range');
    header('Access-Control-Expose-Headers: Content-Length, Content-Range, Accept-Ranges');
    http_response_code(204);
    exit;
}
require_method(['GET']);
rate_limit('stream-proxy', 240, 60);

$device = device_from_signed_token('proxy');
require_active_device($device);
$target = validate_proxy_url(trim((string) ($_GET['url'] ?? '')), $device, false);

if (!function_exists('curl_init')) {
    json_response(['ok' => false, 'error' => 'Proxy service is unavailable'], 503);
}

function stream_content_type(string $url): string
{
    $path = strtolower((string) parse_url($url, PHP_URL_PATH));
    $types = ['m3u8' => 'application/vnd.apple.mpegurl', 'ts' => 'video/mp2t', 'mkv' => 'video/x-matroska', 'avi' => 'video/x-msvideo', 'webm' => 'video/webm'];
    $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
    return $types[$extension] ?? 'video/mp4';
}

$requestHeaders = ['User-Agent: NexoPlayer/2.0', 'Accept: */*'];
if (!empty($_SERVER['HTTP_RANGE']) && preg_match('/^bytes=\d*-\d*$/', (string) $_SERVER['HTTP_RANGE'])) {
    $requestHeaders[] = 'Range: ' . $_SERVER['HTTP_RANGE'];
}

$status = 200;
$headers = [];
$sent = false;
$ch = curl_init($target['url']);
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => $requestHeaders,
    CURLOPT_RETURNTRANSFER => false,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
    CURLOPT_RESOLVE => curl_resolve_entries($target),
    CURLOPT_CONNECTTIMEOUT => 8,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_BUFFERSIZE => 64 * 1024,
    CURLOPT_HEADERFUNCTION => static function ($curl, string $line) use (&$status, &$headers): int {
        $trimmed = trim($line);
        if (preg_match('/^HTTP\/\S+\s+(\d+)/', $trimmed, $match)) {
            $status = (int) $match[1];
            $headers = [];
        } elseif (strpos($trimmed, ':') !== false) {
            [$name, $value] = explode(':', $trimmed, 2);
            $headers[strtolower(trim($name))] = trim($value);
        }
        return strlen($line);
    },
    CURLOPT_WRITEFUNCTION => static function ($curl, string $chunk) use (&$status, &$headers, &$sent, $target): int {
        if (!$sent) {
            http_response_code($status >= 200 && $status < 400 ? $status : 502);
            apply_cors_headers();
            header('Access-Control-Allow-Headers: Content-Type, Range');
            header('Access-Control-Expose-Headers: Content-Length, Content-Range, Accept-Ranges');
            header('Accept-Ranges: bytes');
            header('Cache-Control: no-store');
            header('Content-Type: ' . ($headers['content-type'] ?? stream_content_type($target['url'])));
            foreach (['content-length' => 'Content-Length', 'content-range' => 'Content-Range'] as $source => $destination) {
                if (!empty($headers[$source])) {
                    header($destination . ': ' . $headers[$source]);
                }
            }
            $sent = true;
        }
        echo $chunk;
        flush();
        return strlen($chunk);
    },
]);
$ok = curl_exec($ch);
$httpStatus = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if (!$sent) {
    if ($ok === false || $httpStatus < 200 || $httpStatus >= 400) {
        json_response(['ok' => false, 'error' => 'Upstream stream failed'], 502);
    }
    apply_cors_headers();
    header('Content-Type: ' . ($headers['content-type'] ?? stream_content_type($target['url'])));
}
