<?php
require __DIR__ . '/../lib/bootstrap.php';

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: Content-Type, Range');
    header('Access-Control-Allow-Methods: GET, OPTIONS');
    header('Access-Control-Expose-Headers: Content-Length, Content-Range, Accept-Ranges');
    http_response_code(204);
    exit;
}

$url = trim($_GET['url'] ?? '');

if ($url === '' || !filter_var($url, FILTER_VALIDATE_URL)) {
    json_response(['ok' => false, 'error' => 'Valid url parameter is required'], 422);
}

$scheme = strtolower(parse_url($url, PHP_URL_SCHEME) ?? '');
if (!in_array($scheme, ['http', 'https'], true)) {
    json_response(['ok' => false, 'error' => 'Only http and https URLs are allowed'], 422);
}

function guess_stream_content_type(string $url): string
{
    $path = strtolower((string) parse_url($url, PHP_URL_PATH));
    $endsWith = static function (string $value, string $suffix): bool {
        return substr($value, -strlen($suffix)) === $suffix;
    };

    if ($endsWith($path, '.m3u8')) {
        return 'application/vnd.apple.mpegurl';
    }

    if ($endsWith($path, '.ts')) {
        return 'video/mp2t';
    }

    if ($endsWith($path, '.mkv')) {
        return 'video/x-matroska';
    }

    if ($endsWith($path, '.avi')) {
        return 'video/x-msvideo';
    }

    if ($endsWith($path, '.webm')) {
        return 'video/webm';
    }

    return 'video/mp4';
}

function send_stream_headers(int $status, array $headers, string $url): void
{
    http_response_code($status >= 200 && $status < 500 ? $status : 200);
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: Content-Type, Range');
    header('Access-Control-Allow-Methods: GET, OPTIONS');
    header('Access-Control-Expose-Headers: Content-Length, Content-Range, Accept-Ranges');
    header('Accept-Ranges: bytes');
    header('Cache-Control: no-store');

    if (empty($headers['content-type'])) {
        $headers['content-type'] = guess_stream_content_type($url);
    }

    foreach (['content-type', 'content-length', 'content-range'] as $name) {
        if (!empty($headers[$name])) {
            header(ucwords($name, '-') . ': ' . $headers[$name]);
        }
    }
}

if (!function_exists('curl_init')) {
    json_response(['ok' => false, 'error' => 'cURL is required for stream proxy'], 500);
}

$requestHeaders = [
    'User-Agent: NexoPlayer/1.0',
    'Accept: */*',
];

if (!empty($_SERVER['HTTP_RANGE'])) {
    $requestHeaders[] = 'Range: ' . $_SERVER['HTTP_RANGE'];
}

$status = 200;
$headers = [];
$headersSent = false;
$ch = curl_init($url);
$options = [
    CURLOPT_HTTPHEADER => $requestHeaders,
    CURLOPT_RETURNTRANSFER => false,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_BUFFERSIZE => 1024 * 64,
    CURLOPT_HEADERFUNCTION => function ($curl, string $line) use (&$status, &$headers): int {
        $trimmed = trim($line);

        if ($trimmed === '') {
            return strlen($line);
        }

        if (preg_match('/^HTTP\/\S+\s+(\d+)/', $trimmed, $match)) {
            $status = (int) $match[1];
            $headers = [];
            return strlen($line);
        }

        $parts = explode(':', $trimmed, 2);
        if (count($parts) === 2) {
            $headers[strtolower(trim($parts[0]))] = trim($parts[1]);
        }

        return strlen($line);
    },
    CURLOPT_WRITEFUNCTION => function ($curl, string $chunk) use (&$status, &$headers, &$headersSent, $url): int {
        if (!$headersSent) {
            send_stream_headers($status, $headers, $url);
            $headersSent = true;
        }

        echo $chunk;
        if (function_exists('fastcgi_finish_request')) {
            flush();
        } else {
            @ob_flush();
            flush();
        }

        return strlen($chunk);
    },
];

if (!ini_get('open_basedir')) {
    $options[CURLOPT_FOLLOWLOCATION] = true;
}

curl_setopt_array($ch, $options);
$ok = curl_exec($ch);
$error = curl_error($ch);
$httpStatus = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if (!$headersSent) {
    if ($ok === false || $httpStatus >= 400) {
        json_response(['ok' => false, 'error' => $error ?: ('Upstream HTTP ' . $httpStatus)], 502);
    }

    send_stream_headers($httpStatus ?: $status, $headers, $url);
}
