<?php

namespace App\Domain\Shared\Scopes;

use App\Domain\Shared\Context\BranchScopeState;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

/**
 * Narrow every query on a branch-owning model to the active branch.
 *
 * This is the counterpart of the academy scope in BelongsToAcademy, and it
 * exists for the same reason: a rule that has to be re-applied by hand in 212
 * Livewire components and 355 Blade views is a rule that will be missed. The
 * missed cases were never the list queries — those were mostly filtered — but
 * the <select> option sources, the exists: validation rules, the count badges
 * and the ->findOrFail() on an id out of the URL.
 *
 * Two modes, because branch_id carries two different meanings:
 *
 *   strict  — the row belongs to exactly one branch. A player, a group, an
 *             invoice, a cash session. NULL is a data bug, not a value.
 *   shared  — the row is catalogue or configuration, and NULL means "every
 *             branch uses this". Products, base prices, pricing rules,
 *             receipt templates. Hard-filtering these would hide the shared
 *             price rows, and PricingService treats a missing base price as a
 *             hard failure — so a strict scope here would not show fewer
 *             prices, it would stop the tills selling.
 */
class BranchScope implements Scope
{
    public function __construct(private bool $allowShared = false)
    {
    }

    public function apply(Builder $builder, Model $model): void
    {
        $state = app(BranchScopeState::class);

        if (! $state->shouldFilter()) {
            return;
        }

        $branchId = $state->branchId();
        $column = $model->getTable().'.'.$model->getBranchColumn();

        if ($this->allowShared) {
            // Grouped so it cannot be broken apart by a later orWhere on the
            // same builder — an un-parenthesised OR here would leak every
            // branch's rows the moment a screen added a search filter.
            $builder->where(function (Builder $query) use ($column, $branchId) {
                $query->where($column, $branchId)->orWhereNull($column);
            });

            return;
        }

        $builder->where($column, $branchId);
    }
}
