<?php

namespace App\Models\Scopes;

use App\Jobs\Concerns\WithTenantContext;
use App\Services\CurrentContext;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use RuntimeException;

/**
 * The hard tenant-isolation boundary for the single-database, shared-schema model.
 *
 * Unlike {@see BranchScope}, this scope NEVER bypasses for a tenant's own full
 * admin — a tenant super-admin is still confined to their tenant. Only the
 * landlord ("central") context bypasses, and only via {@see CurrentContext::isCentral()}
 * (set by the central guard middleware or {@see CurrentContext::runWithoutTenant()}).
 *
 * Resolution:
 *   1. Tenant bound     → filter `tenant_id = X`.
 *   2. Central context  → pass through (landlord operates on central tables, or on
 *                         tenant tables only inside runForTenant()/runWithoutTenant()).
 *   3. Web, no tenant   → FAIL CLOSED (`1 = 0`). The IdentifyTenant middleware
 *                         guarantees a tenant on tenant domains, so this only
 *                         trips on a misconfiguration — and then leaks nothing.
 *   4. Console, no tenant, not central → THROW.
 *
 * Migration-plan.md §1 hardening: unlike the source Inertia app (which silently
 * passes through unscoped for ANY console/CLI execution — commands, queue
 * workers, and test runs alike), a queued job or scheduled command touching
 * tenant-scoped data has no subdomain to resolve a tenant from, so an
 * ambiguous context there is either a real bug or a genuinely cross-tenant
 * sweep. The former must fail loudly, not leak; the latter must opt in
 * explicitly via {@see CurrentContext::runWithoutTenant()} or
 * {@see CurrentContext::runForTenant()} (see {@see WithTenantContext}
 * for the job-dispatch side of this). Tests must bind a tenant explicitly too.
 *
 * Bypass deliberately: `Model::withoutGlobalScope(TenantScope::class)`.
 */
class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        $context = app(CurrentContext::class);
        $tenantId = $context->tenantId();

        if ($tenantId !== null) {
            $builder->where($model->getTable().'.tenant_id', $tenantId);

            return;
        }

        if ($context->isCentral()) {
            return;
        }

        if (app()->runningInConsole()) {
            throw new RuntimeException(sprintf(
                'Ambiguous tenant context: a query against tenant-scoped model [%s] ran in a console/queue/test process with no tenant bound and no explicit CurrentContext::runWithoutTenant() opt-in. Bind a tenant (CurrentContext::setTenant()/runForTenant()) or explicitly opt into an unscoped sweep (CurrentContext::runWithoutTenant()).',
                $model::class,
            ));
        }

        $builder->whereRaw('1 = 0');
    }
}
