<?php

namespace App\Http\Controllers\Financial;

use App\Domain\Financial\Models\PaymentProof;
use App\Domain\Identity\Services\GuardianResolver;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Streams a transfer proof from the private disk, to the two parties entitled
 * to see it: the member who submitted it, and staff who may review it.
 *
 * It is a stream rather than a URL because the file is on the private disk and
 * must stay there. The deleted mobile API's DocumentController returned
 * `asset('storage/'.$file_path)` for documents on the private disk — a URL
 * that either 404s or, if the file were ever moved to make it work, serves
 * national IDs and medical records to anyone with the path.
 */
class PaymentProofFileController extends Controller
{
    public function __invoke(Request $request, string $uuid): StreamedResponse
    {
        $proof = PaymentProof::where('uuid', $uuid)->firstOrFail();

        abort_unless($this->mayView($request, $proof), 403);

        $disk = Storage::disk('local');

        abort_unless($disk->exists($proof->proof_path), 404);

        return $disk->response(
            $proof->proof_path,
            'proof-' . $proof->uuid . '.' . pathinfo($proof->proof_path, PATHINFO_EXTENSION),
            [
                // Never inline. An attachment cannot execute in the page's
                // origin, whatever the browser decides the file really is.
                'Content-Disposition' => 'attachment',
                'X-Content-Type-Options' => 'nosniff',
                'Cache-Control' => 'private, no-store',
            ]
        );
    }

    private function mayView(Request $request, PaymentProof $proof): bool
    {
        $user = $request->user();

        if (! $user) {
            return false;
        }

        if ((int) $proof->submitted_by === (int) $user->id) {
            return true;
        }

        if ($user->can('payments.approve_proof')) {
            return true;
        }

        // A second guardian of the same member may see it too — they share the
        // invoice it belongs to.
        return $proof->participant_id
            && in_array((int) $proof->participant_id, app(GuardianResolver::class)->participantIdsFor($user), true);
    }
}
