<?php

namespace App\Domain\Documents\Policies;

use App\Domain\Applicants\Models\Applicant;
use App\Domain\Applicants\Policies\ApplicantPolicy;
use App\Domain\Documents\Models\Document;
use App\Domain\Identity\Models\User;
use App\Domain\Identity\Services\ScopeGuard;

/**
 * Document access follows the owner: whoever may see the applicant may see
 * their documents, subject to the document permissions themselves.
 */
class DocumentPolicy
{
    public function __construct(private readonly ScopeGuard $scopes) {}

    public function viewAny(User $user): bool
    {
        return $user->can('document.view');
    }

    public function view(User $user, Document $document): bool
    {
        return $user->can('document.view') && $this->reachesOwner($user, 'document.view', $document);
    }

    public function upload(User $user, Document $document): bool
    {
        return $user->can('document.upload') && $this->reachesOwner($user, 'document.upload', $document);
    }

    public function download(User $user, Document $document): bool
    {
        if (! $user->can('document.download') || ! $this->reachesOwner($user, 'document.download', $document)) {
            return false;
        }

        // Sensitive types need their own permission on top (§17.3).
        if ($document->loadMissing('documentType')->documentType?->is_sensitive) {
            return $user->can('document.download_sensitive');
        }

        return true;
    }

    public function verify(User $user, Document $document): bool
    {
        return $user->can('document.verify') && $this->reachesOwner($user, 'document.verify', $document);
    }

    public function reject(User $user, Document $document): bool
    {
        return $user->can('document.reject') && $this->reachesOwner($user, 'document.reject', $document);
    }

    public function waive(User $user, Document $document): bool
    {
        return $user->can('document.waive') && $this->reachesOwner($user, 'document.waive', $document);
    }

    /**
     * Applies the permission's scope against the owning record. Ownership
     * columns differ per owner type, so applicants are handled explicitly.
     */
    private function reachesOwner(User $user, string $permission, Document $document): bool
    {
        // Explicitly loaded: the policy runs from resources where the relation
        // may not have been eager loaded.
        $owner = $document->loadMissing('owner')->owner;

        if ($owner === null) {
            return false;
        }

        $ownerColumns = $owner instanceof Applicant
            ? ApplicantPolicy::OWNER_COLUMNS
            : ['user_id', 'created_by'];

        return $this->scopes->allows($user, $permission, $owner, $ownerColumns);
    }
}
