<?php

namespace App\Services\Support;

use App\Entities\User;

/**
 * Request-scoped holder of "who is making this request, and which agency
 * are they scoped to". Populated once by the Auth filter, read everywhere
 * else (Repositories, Services) — never re-derived ad hoc in a controller.
 *
 * This is the single seam that makes multi-tenant isolation structural
 * rather than a per-query convention. See 01-system-architecture.md §4.
 */
class TenantContext
{
    private static ?TenantContext $instance = null;

    private ?User $user = null;
    private ?int $agencyId = null;
    private bool $bypassActive = false;

    public static function getInstance(): self
    {
        return self::$instance ??= new self();
    }

    /**
     * Called once by the Auth filter after successful authentication.
     */
    public function setUser(User $user): void
    {
        $this->user = $user;
        $this->agencyId = $user->agency_id;
    }

    public function getUser(): ?User
    {
        return $this->user;
    }

    public function getAgencyId(): ?int
    {
        return $this->agencyId;
    }

    public function isSuperAdmin(): bool
    {
        return $this->user?->isSuperAdmin() ?? false;
    }

    /**
     * Executes $callback with tenant scoping suspended. ONLY callable when
     * the current user is Super Admin — enforced here, not left to caller
     * discipline, so a bug in an Agency-scoped controller can never
     * accidentally see cross-tenant data.
     *
     * @throws \RuntimeException if called by a non-super-admin context.
     */
    public function runWithoutTenantScope(callable $callback): mixed
    {
        if (! $this->isSuperAdmin()) {
            throw new \RuntimeException(
                'runWithoutTenantScope() may only be invoked in a Super Admin context.'
            );
        }

        $this->bypassActive = true;
        try {
            return $callback();
        } finally {
            $this->bypassActive = false;
        }
    }

    public function isBypassActive(): bool
    {
        return $this->bypassActive;
    }

    /**
     * Test/CLI helper — never called from HTTP request handling.
     */
    public static function reset(): void
    {
        self::$instance = null;
    }
}
