<?php

namespace App\Http\Controllers;

use App\Assets\Thumbnails;
use App\Models\Asset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Serve a creative back to the person who owns it.
 *
 * Uploads land on the local disk and generated creatives on the public one, so
 * half of them had no URL at all and the panel showed a placeholder instead of
 * the image. The alternative was moving uploads to the public disk, which would
 * make every file anyone uploads readable by URL to anyone who has it.
 *
 * This reads from whichever disk the asset records, behind the session, so
 * nothing is published to serve a thumbnail.
 */
class AssetPreviewController extends Controller
{
    public function __construct(private readonly Thumbnails $thumbnails) {}

    public function __invoke(Request $request, Asset $asset): StreamedResponse
    {
        $this->authoriseSignedIn($request);

        $disk = Storage::disk($asset->disk ?: 'local');

        // The generator writes multi-megabyte images and the panel shows them at
        // 56 pixels, so serve a small copy where one can be made.
        $thumbnail = $request->boolean('full') ? null : $this->thumbnails->for($asset);
        $path = $asset->path;

        if ($thumbnail) {
            [$thumbnailDisk, $path] = $thumbnail;
            $disk = Storage::disk($thumbnailDisk);
        } else {
            // Checked on what is actually being served, which used to be checked
            // first and unconditionally. A video's still comes from the platform
            // rather than from our copy of the file, so an asset whose source is
            // gone answered 404 while holding a perfectly good thumbnail. For an
            // image nothing changes: Thumbnails reads the source, so a missing
            // file yields no thumbnail and lands here exactly as before.
            abort_unless($disk->exists($asset->path), 404);
        }

        return $disk->response(
            $path,
            $asset->original_name,
            [
                'Content-Type' => $thumbnail ? 'image/jpeg' : ($asset->mime ?: 'application/octet-stream'),
                // Uploads are already constrained to images and video, so the
                // recorded mime cannot be text/html. nosniff keeps that true if
                // the upload rules are ever loosened.
                'X-Content-Type-Options' => 'nosniff',
                // Private: it is one user's creative, so no shared cache should
                // hold it, but the browser may while the tab is open.
                'Cache-Control' => 'private, max-age=300',
            ],
            'inline',
        );
    }

    /**
     * Signed in, and deliberately no further than that.
     *
     * Internal tool, shared ad accounts: colleagues need to see each other's
     * creatives. What is not open is the internet, which is why this is a route
     * behind the session rather than a file on a public disk.
     *
     * Named for what it checks. It was authoriseOwner(), which read as an
     * ownership check to anyone auditing the route and never was one.
     */
    private function authoriseSignedIn(Request $request): void
    {
        abort_unless($request->user() !== null, 404);
    }
}
