<?php

namespace App\Services\Care\Workflow;

use App\Models\Organization;
use App\Models\Visit;
use App\Models\VisitStageAdvance;
use App\Models\WorkflowStage;
use App\Services\Care\CareFeatures;
use App\Services\Care\CareQueueContexts;

/**
 * Single boundary for deciding whether a visit may be exposed to a Queue
 * service line. Every direct issue and background backfill passes through it.
 */
class WorkflowQueueGate
{
    public function __construct(
        protected CareFeatures $features,
        protected WorkflowEngine $engine,
    ) {}

    public function enabled(Organization $organization): bool
    {
        return $this->features->enabled($organization, CareFeatures::WORKFLOW_ENGINE);
    }

    /**
     * Mutating gate used when issuing tickets or starting encounters.
     * May start a workflow or refresh financial gate state.
     */
    public function canRelease(
        Organization $organization,
        ?Visit $visit,
        string $queueContext,
    ): bool {
        if (! $this->enabled($organization) || $visit === null) {
            return true;
        }

        $advance = $this->engine->currentAdvance($visit)
            ? $this->engine->refreshGate($visit)
            : $this->engine->start($visit);

        return $this->evaluateAdvance($organization, $visit, $advance, $queueContext, mutate: true);
    }

    /**
     * Read-only gate for waiting-list / board rendering.
     * Does not start workflows, create obligations, or refresh gate rows.
     */
    public function isReleasedForDisplay(
        Organization $organization,
        ?Visit $visit,
        string $queueContext,
    ): bool {
        if (! $this->enabled($organization) || $visit === null) {
            return true;
        }

        $advance = $this->engine->currentAdvance($visit);

        // No materialized advance yet — keep legacy visibility (do not start on GET).
        if ($advance === null) {
            return true;
        }

        return $this->evaluateAdvance($organization, $visit, $advance, $queueContext, mutate: false);
    }

    /**
     * Whether the visit is blocked on a before-service financial gate.
     */
    public function isAwaitingFinancialGate(
        Organization $organization,
        Visit $visit,
        ?WorkflowStage $stage = null,
    ): bool {
        if (! $this->features->enabled($organization, CareFeatures::FINANCIAL_GATES)) {
            return false;
        }

        $stage ??= $this->engine->currentStage($visit);
        if ($stage === null || ! $stage->gatesBeforeService()) {
            return false;
        }

        return ! $this->engine->isQueueReleasable($visit);
    }

    protected function evaluateAdvance(
        Organization $organization,
        Visit $visit,
        ?VisitStageAdvance $advance,
        string $queueContext,
        bool $mutate,
    ): bool {
        $stage = $advance?->stage;

        // An enabled org without a materialized workflow keeps legacy behavior.
        if ($stage === null) {
            return true;
        }

        // Pay-before-service holds belong on the cashier / billing line, not the
        // clinical service queue for the current stage.
        if ($queueContext === CareQueueContexts::BILLING) {
            if ($mutate
                ? $this->isAwaitingFinancialGate($organization, $visit, $stage)
                : ($advance->isBlocked() && $stage->gatesBeforeService())) {
                return true;
            }
        }

        if (! $this->contextsMatch($stage->queue_context, $stage->type, $queueContext)) {
            return false;
        }

        if (! $this->features->enabled($organization, CareFeatures::FINANCIAL_GATES)) {
            return true;
        }

        // Display path trusts advance status (payment clear paths refresh the gate).
        if (! $mutate) {
            return ! $advance->isBlocked();
        }

        return $this->engine->isQueueReleasable($visit);
    }

    protected function contextsMatch(?string $stageContext, string $stageType, string $queueContext): bool
    {
        if ($stageContext === $queueContext) {
            return true;
        }

        if ($stageType === 'payment' && $queueContext === CareQueueContexts::BILLING) {
            return true;
        }

        // Specialty appointments still fulfill a consultation workflow stage.
        return $stageType === 'consultation'
            && CareQueueContexts::isSpecialty($queueContext)
            && $stageContext === CareQueueContexts::CONSULTATION;
    }
}
