<?php
require_once __DIR__ . '/includes/bootstrap.php';

@set_time_limit(0);
@ini_set('max_execution_time', '0');
@ini_set('output_buffering', 'off');
@ini_set('zlib.output_compression', '0');
while (ob_get_level() > 0) {
    @ob_end_clean();
}
ignore_user_abort(true);

$cfg = ugfx_get_stream_config();
$allowed = [];
foreach (['mount_main', 'mount_auto', 'mount_backup'] as $key) {
    $mount = ugfx_normalize_mount((string)($cfg[$key] ?? ''));
    if ($mount !== '') {
        $allowed[$mount] = true;
    }
}
$requested = isset($_GET['mount']) ? ugfx_normalize_mount((string)$_GET['mount']) : ugfx_normalize_mount((string)($cfg['mount_main'] ?? '/stream'));
if ($requested === '' || !isset($allowed[$requested])) {
    $requested = ugfx_normalize_mount((string)($cfg['mount_main'] ?? '/stream'));
}

$origin = sprintf('http://%s:%s%s', $cfg['server_ip'], $cfg['server_port'], $requested);
$range = trim((string)($_SERVER['HTTP_RANGE'] ?? ''));
$userAgent = trim((string)($_SERVER['HTTP_USER_AGENT'] ?? 'UGFXProxy/1.0')) ?: 'UGFXProxy/1.0';
$icyMeta = trim((string)($_SERVER['HTTP_ICY_METADATA'] ?? ''));

header_remove('X-Powered-By');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
header('Accept-Ranges: bytes');

$sentContentType = false;
$headerBuffer = '';
$statusCode = 200;

if (function_exists('curl_init')) {
    $ch = curl_init($origin);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6);
    curl_setopt($ch, CURLOPT_TIMEOUT, 0);
    curl_setopt($ch, CURLOPT_BUFFERSIZE, 16384);
    curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

    $forwardHeaders = ['Icy-MetaData: ' . ($icyMeta === '1' ? '1' : '0')];
    if ($range !== '') {
        $forwardHeaders[] = 'Range: ' . $range;
    }
    curl_setopt($ch, CURLOPT_HTTPHEADER, $forwardHeaders);

    curl_setopt($ch, CURLOPT_HEADERFUNCTION, static function ($ch, $headerLine) use (&$sentContentType, &$statusCode) {
        $len = strlen($headerLine);
        $line = trim($headerLine);
        if ($line === '') {
            return $len;
        }
        if (preg_match('#^HTTP/\S+\s+(\d{3})#i', $line, $m)) {
            $statusCode = (int)$m[1];
            http_response_code($statusCode >= 200 ? $statusCode : 200);
            return $len;
        }
        $parts = explode(':', $line, 2);
        if (count($parts) !== 2) {
            return $len;
        }
        $name = strtolower(trim($parts[0]));
        $value = trim($parts[1]);
        $passthrough = [
            'content-type', 'content-length', 'icy-br', 'icy-description', 'icy-genre',
            'icy-name', 'icy-pub', 'icy-url', 'icy-metaint', 'content-range'
        ];
        if (in_array($name, $passthrough, true)) {
            header($parts[0] . ': ' . $value, true);
            if ($name === 'content-type') {
                $sentContentType = true;
            }
        }
        return $len;
    });

    curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, $chunk) use (&$sentContentType) {
        if (!$sentContentType) {
            header('Content-Type: audio/mpeg');
            $sentContentType = true;
        }
        echo $chunk;
        @flush();
        if (function_exists('fastcgi_finish_request')) {
            // no-op: do not terminate streaming early
        }
        return strlen($chunk);
    });

    $ok = curl_exec($ch);
    $err = curl_error($ch);
    $http = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($ok !== false && $http < 500) {
        exit;
    }
}

$parts = @parse_url($origin);
$host = (string)($parts['host'] ?? '');
$port = (int)($parts['port'] ?? 80);
$path = (string)($parts['path'] ?? '/');
if ($host === '') {
    http_response_code(502);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['ok' => false, 'error' => 'Invalid upstream stream URL'], JSON_UNESCAPED_SLASHES);
    exit;
}

$fp = @fsockopen($host, $port, $errno, $errstr, 6);
if (!$fp) {
    http_response_code(502);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['ok' => false, 'error' => 'Unable to connect to upstream stream', 'detail' => $errstr], JSON_UNESCAPED_SLASHES);
    exit;
}

stream_set_timeout($fp, 0);
$requestHeaders = [
    'GET ' . $path . ' HTTP/1.0',
    'Host: ' . $host,
    'User-Agent: ' . $userAgent,
    'Icy-MetaData: ' . ($icyMeta === '1' ? '1' : '0'),
    'Connection: close',
];
if ($range !== '') {
    $requestHeaders[] = 'Range: ' . $range;
}
$requestHeaders[] = '';
$requestHeaders[] = '';
fwrite($fp, implode("\r\n", $requestHeaders));

$headersDone = false;
$responseHeaders = [];
while (!feof($fp) && !$headersDone) {
    $line = fgets($fp, 4096);
    if ($line === false) {
        break;
    }
    $trim = rtrim($line, "\r\n");
    if ($trim === '') {
        $headersDone = true;
        break;
    }
    $responseHeaders[] = $trim;
}

foreach ($responseHeaders as $line) {
    if (preg_match('#^HTTP/\S+\s+(\d{3})#i', $line, $m)) {
        $statusCode = (int)$m[1];
        http_response_code($statusCode >= 200 ? $statusCode : 200);
        continue;
    }
    $parts = explode(':', $line, 2);
    if (count($parts) !== 2) {
        continue;
    }
    $name = strtolower(trim($parts[0]));
    $value = trim($parts[1]);
    if (in_array($name, ['content-type', 'content-length', 'icy-br', 'icy-description', 'icy-genre', 'icy-name', 'icy-pub', 'icy-url', 'icy-metaint', 'content-range'], true)) {
        header($parts[0] . ': ' . $value, true);
        if ($name === 'content-type') {
            $sentContentType = true;
        }
    }
}
if (!$sentContentType) {
    header('Content-Type: audio/mpeg');
}

while (!feof($fp)) {
    $chunk = fread($fp, 16384);
    if ($chunk === false || $chunk === '') {
        $meta = stream_get_meta_data($fp);
        if (!empty($meta['timed_out'])) {
            break;
        }
        usleep(25000);
        continue;
    }
    echo $chunk;
    @flush();
}
fclose($fp);
