<?php

namespace App\Services\Agent;

use App\Models\AgentNode;
use App\Models\AgentProfile;
use App\Support\AgentOverdueGuard;
use Illuminate\Validation\ValidationException;

final class CreditAllocationValidator
{
    public function __construct(
        private readonly AgentCreditAllocatedSyncService $allocatedSync,
    ) {}

    public function assertChildCreditLimitWithinParent(
        AgentNode $parent,
        int $previousChildLimit,
        int $newChildLimit,
        bool $isNewChild,
    ): void {
        if ($newChildLimit < 0) {
            throw ValidationException::withMessages([
                'credit_limit' => ['invalid'],
            ]);
        }

        $this->allocatedSync->syncForAgent($parent);

        $profile = AgentProfile::query()->where('agent_node_id', $parent->id)->first();
        if ($profile === null) {
            throw ValidationException::withMessages([
                'credit_limit' => ['parent_profile_required'],
            ]);
        }

        $available = max(0, (int) $profile->credit_limit - (int) $profile->allocated_credit);
        $floor = $isNewChild ? 0 : max(0, $previousChildLimit);
        $maxAllowed = $floor + $available;

        if ($newChildLimit > $maxAllowed) {
            throw ValidationException::withMessages([
                'credit_limit' => ['exceeds_available'],
            ]);
        }
    }

    public function assertAllocationWithinParent(AgentNode $parent, int $additionalCredit): void
    {
        $this->allocatedSync->syncForAgent($parent);

        $profile = AgentProfile::query()->where('agent_node_id', $parent->id)->first();
        if ($profile === null) {
            throw ValidationException::withMessages([
                'credit_limit' => ['parent_profile_required'],
            ]);
        }

        $available = max(0, (int) $profile->credit_limit - (int) $profile->allocated_credit);
        if ($additionalCredit > $available) {
            throw ValidationException::withMessages([
                'credit_limit' => ['exceeds_available'],
            ]);
        }
    }

    public function assertPlayerCreditWithinAgent(AgentNode $agent, int $playerCreditLimit): void
    {
        if ($playerCreditLimit < 0) {
            throw ValidationException::withMessages([
                'credit_limit' => ['invalid'],
            ]);
        }

        $this->assertPlayerCreditDeltaWithinAgent($agent, $playerCreditLimit);
    }

    public function assertPlayerCreditDeltaWithinAgent(AgentNode $agent, int $additionalCredit): void
    {
        if ($additionalCredit <= 0) {
            return;
        }

        AgentOverdueGuard::assertAgentMayGrantCredit((int) $agent->id);

        $this->allocatedSync->syncForAgent($agent);

        $profile = AgentProfile::query()->where('agent_node_id', $agent->id)->first();
        if ($profile === null) {
            throw ValidationException::withMessages([
                'credit_limit' => ['agent_profile_required'],
            ]);
        }

        $available = max(0, (int) $profile->credit_limit - (int) $profile->allocated_credit);
        if ($additionalCredit > $available) {
            throw ValidationException::withMessages([
                'credit_limit' => ['exceeds_available'],
            ]);
        }
    }
}
