<?php

namespace App\Http\Controllers;

use App\Domain\Document\Models\Document;
use App\Domain\Identity\Services\GuardianResolver;
use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Models\ServiceRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Streams the files members upload — request attachments and documents — from
 * the private disk, to the people entitled to see them.
 *
 * There is one rule and everything else follows from it: **a member upload is
 * never a URL**. The deleted mobile API returned
 * `asset('storage/'.$file_path)` for documents stored on the private disk,
 * which either 404s or — if anyone "fixed" it by moving the files to the public
 * disk — serves national IDs and medical records to whoever guesses a path. The
 * parent excuse form did move its medical attachments to the public disk, which
 * is why S0 had to sweep them.
 *
 * Every response is an attachment with `nosniff` and `no-store`: a file the
 * browser decides is HTML must not execute in the academy's own origin, and a
 * medical certificate must not sit in a shared proxy's cache.
 */
class MemberFileController extends Controller
{
    public function requestAttachment(Request $request, string $uuid): StreamedResponse
    {
        $serviceRequest = ServiceRequest::where('uuid', $uuid)->firstOrFail();

        abort_unless($this->maySee($request, $serviceRequest->participant_id, $serviceRequest->user_id), 403);
        abort_unless($serviceRequest->attachment_path, 404);

        return $this->stream($serviceRequest->attachment_path, 'request-' . $serviceRequest->uuid);
    }

    public function document(Request $request, string $uuid): StreamedResponse
    {
        $document = Document::where('uuid', $uuid)->firstOrFail();

        $participantId = $document->documentable_type === Participant::class
            ? (int) $document->documentable_id
            : null;

        abort_unless($this->maySee($request, $participantId, $document->uploaded_by), 403);

        return $this->stream($document->file_path, 'document-' . $document->uuid);
    }

    /**
     * The uploader, anyone who speaks for the member the file is about, and
     * staff holding the matching view permission.
     */
    private function maySee(Request $request, ?int $participantId, ?int $uploaderId): bool
    {
        $user = $request->user();

        if (! $user) {
            return false;
        }

        if ($uploaderId && (int) $uploaderId === (int) $user->id) {
            return true;
        }

        if ($user->can('documents.view') || $user->can('participants.update')) {
            return true;
        }

        return $participantId
            && in_array((int) $participantId, app(GuardianResolver::class)->participantIdsFor($user), true);
    }

    private function stream(string $path, string $name): StreamedResponse
    {
        $disk = Storage::disk('local');

        abort_unless($disk->exists($path), 404);

        return $disk->response($path, $name . '.' . pathinfo($path, PATHINFO_EXTENSION), [
            'Content-Disposition' => 'attachment',
            'X-Content-Type-Options' => 'nosniff',
            'Cache-Control' => 'private, no-store',
        ]);
    }
}
