<?php

namespace App\Services\Agent;

use App\Models\AgentNode;
use App\Models\AgentProfile;
use App\Support\PlayerFundingMode;
use Illuminate\Support\Facades\DB;

/**
 * 同步代理「团队当前已用风险」（agent_profiles.used_credit）：
 * 聚合该代理子树下所有信用玩家的 player_credit_accounts.used_credit + frozen_credit。
 *
 * 不参与 available_credit 计算（available = credit_limit - allocated_credit），
 * 仅作风控展示指标。
 */
final class AgentUsedCreditSyncService
{
    /**
     * 为指定代理节点重算 used_credit。
     */
    public function syncForAgent(AgentNode $agent): void
    {
        $profile = AgentProfile::query()->where('agent_node_id', $agent->id)->first();
        if ($profile === null) {
            return;
        }

        $expected = $this->calculateUsedCredit($agent);
        if ((int) $profile->used_credit === $expected) {
            return;
        }

        $profile->used_credit = $expected;
        $profile->save();
    }

    public function syncForAgentId(int $agentNodeId): void
    {
        $agent = AgentNode::query()->find($agentNodeId);
        if ($agent === null) {
            return;
        }

        $this->syncForAgent($agent);
    }

    /**
     * 玩家信用变化后：同步直属代理及所有祖先代理的 used_credit。
     */
    public function syncForPlayerAgentChain(int $playerAgentNodeId): void
    {
        if ($playerAgentNodeId <= 0) {
            return;
        }

        $agent = AgentNode::query()->find($playerAgentNodeId);
        if ($agent === null) {
            return;
        }

        $ancestorIds = $this->ancestorAndSelfIds($agent);

        foreach ($ancestorIds as $nodeId) {
            $this->syncForAgentId($nodeId);
        }
    }

    /**
     * 计算代理子树下所有信用玩家的 used_credit + frozen_credit 之和。
     */
    public function calculateUsedCredit(AgentNode $agent): int
    {
        $subtreeIds = $this->subtreeNodeIds($agent);
        if ($subtreeIds === []) {
            return 0;
        }

        return (int) DB::table('player_credit_accounts as pca')
            ->join('players as p', 'p.id', '=', 'pca.player_id')
            ->whereIn('p.agent_node_id', $subtreeIds)
            ->where('p.funding_mode', PlayerFundingMode::CREDIT)
            ->sum(DB::raw('pca.used_credit + pca.frozen_credit'));
    }

    /**
     * @return list<int>
     */
    private function subtreeNodeIds(AgentNode $agent): array
    {
        return AgentNode::query()
            ->where('path', 'like', $agent->path.'%')
            ->pluck('id')
            ->map(static fn ($id): int => (int) $id)
            ->all();
    }

    /**
     * @return list<int>
     */
    private function ancestorAndSelfIds(AgentNode $agent): array
    {
        $path = (string) $agent->path;
        if ($path === '') {
            return [(int) $agent->id];
        }

        $parts = explode('/', trim($path, '/'));
        $ancestorIds = array_map('intval', $parts);
        $ancestorIds[] = (int) $agent->id;

        return $ancestorIds;
    }
}
