<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Response;

/**
 * Streams audio files from the configured music_dirs via HTTP so the
 * music-daemon page (served on http://127.0.0.1:8000/) can load them.
 *
 * Chromium blocks HTTP-origin pages from loading `file://` media as a
 * mixed-protocol security policy, which is why the old file://-only
 * setup worked but the new HTTP setup needs this.
 *
 * Path-traversal guard: the resolved file's real path must sit inside
 * one of the configured roots; the request 404s otherwise.
 */
class AudioController extends Controller
{
    public function stream(Request $request): Response
    {
        $b64 = (string) $request->query('p', '');
        if ($b64 === '') abort(400, 'missing p');

        // URL-safe base64; pad back to a multiple of 4 for strict decoding.
        $padded = $b64 . str_repeat('=', (4 - strlen($b64) % 4) % 4);
        $path = base64_decode(strtr($padded, '-_', '+/'), true);
        if ($path === false || $path === '') abort(400, 'bad encoding');

        $real = realpath($path);
        if ($real === false || !is_file($real)) abort(404, 'file not found: ' . $path);

        $allowed = false;
        foreach ((array) config('rig.music_dirs') as $root) {
            $rootReal = realpath($root);
            if ($rootReal && str_starts_with($real . '/', rtrim($rootReal, '/') . '/')) {
                $allowed = true;
                break;
            }
        }
        if (!$allowed) abort(403, 'outside allowed roots');

        $ext = strtolower(pathinfo($real, PATHINFO_EXTENSION));
        $mime = match ($ext) {
            'm4a', 'aac' => 'audio/mp4',
            'mp3'        => 'audio/mpeg',
            'opus', 'ogg'=> 'audio/ogg',
            'flac'       => 'audio/flac',
            'webm'       => 'audio/webm',
            'wav'        => 'audio/wav',
            default      => 'application/octet-stream',
        };

        // BinaryFileResponse handles Range requests automatically, which the
        // HTML5 <audio> element issues when seeking.
        $resp = new BinaryFileResponse($real, 200, [
            'Content-Type'  => $mime,
            'Accept-Ranges' => 'bytes',
            'Cache-Control' => 'public, max-age=86400',
        ]);
        return $resp;
    }

    /** Encode an absolute path into the URL-safe base64 used by the {@see stream} route. */
    public static function urlFor(string $absolutePath): string
    {
        return '/track?p=' . rtrim(strtr(base64_encode($absolutePath), '+/', '-_'), '=');
    }
}
