<?php

namespace App\Support;

use App\Models\AdminUser;
use App\Models\AgentNode;
use App\Models\Player;
use Illuminate\Database\Eloquent\Builder;

/**
 * 代理子树数据范围（P1：节点访问；P2 起叠加玩家 agent_node_id）。
 */
final class AdminAgentScope
{
    public static function primaryAgentNode(AdminUser $admin): ?AgentNode
    {
        if ($admin->isSuperAdmin()) {
            return null;
        }

        $agentId = $admin->primaryAgentNodeId();
        if ($agentId === null) {
            return null;
        }

        return AgentNode::query()->find($agentId);
    }

    /** 仅站点运营（admin_user_site_roles），未绑定代理节点。 */
    public static function isSiteOnlyOperator(AdminUser $admin): bool
    {
        if ($admin->isSuperAdmin() || self::primaryAgentNode($admin) !== null) {
            return false;
        }

        $siteIds = $admin->accessibleAdminSiteIds();

        return $siteIds !== null && $siteIds !== [];
    }

    public static function nodeVisibleTo(AdminUser $admin, AgentNode $node): bool
    {
        if ($admin->isSuperAdmin()) {
            return true;
        }

        // Agent account (bound via agent node) - check first
        // Even if they also have site roles, agent binding takes precedence for visibility
        $actor = self::primaryAgentNode($admin);
        if ($actor !== null) {
            return $node->isSameOrDescendantOf($actor);
        }

        // Check if admin is a platform account (bound via admin_user_site_roles)
        $accessibleSiteIds = $admin->accessibleAdminSiteIds();
        if ($accessibleSiteIds !== null) {
            // Platform account (site admin) can see all nodes in the site
            return in_array((int) $node->admin_site_id, $accessibleSiteIds, true);
        }

        return false;
    }

    public static function playerAccessible(AdminUser $admin, Player $player): bool
    {
        if ($admin->isSuperAdmin()) {
            return true;
        }

        $actor = self::primaryAgentNode($admin);
        if ($actor !== null) {
            if ($player->agent_node_id === null) {
                return false;
            }

            $playerAgent = AgentNode::query()->find((int) $player->agent_node_id);
            if ($playerAgent === null) {
                return false;
            }

            return $playerAgent->isSameOrDescendantOf($actor);
        }

        return self::isSiteOnlyOperator($admin);
    }

    public static function nodeManageableBy(AdminUser $admin, AgentNode $node): bool
    {
        if ($admin->isSuperAdmin()) {
            return true;
        }

        if (! $admin->hasPermissionCode('agent.node.manage')) {
            return false;
        }

        return self::nodeVisibleTo($admin, $node);
    }

    /** 占成/授信/回水仅可由上级或平台修改，代理本人不可改自己的 profile。 */
    public static function nodeProfileEditableBy(AdminUser $admin, AgentNode $node): bool
    {
        if ($admin->isSuperAdmin()) {
            return true;
        }

        // 一级代理 profile 仅超管可维护（站点总额度、占成、回水等）
        if ($node->isRoot()) {
            return false;
        }

        if (
            ! $admin->hasPermissionCode('agent.profile.manage')
            && ! $admin->hasPermissionCode('agent.node.manage')
        ) {
            return false;
        }

        $actor = self::primaryAgentNode($admin);
        if ($actor !== null) {
            if ((int) $actor->id === (int) $node->id) {
                return false;
            }

            return $node->isDescendantOf($actor);
        }

        if (! self::isSiteOnlyOperator($admin)) {
            return false;
        }

        $accessibleSiteIds = $admin->accessibleAdminSiteIds();

        return in_array((int) $node->admin_site_id, $accessibleSiteIds ?? [], true);
    }

    /**
     * @return Builder<AgentNode>
     */
    public static function visibleNodesQuery(AdminUser $admin, int $adminSiteId): Builder
    {
        $query = AgentNode::query()
            ->where('admin_site_id', $adminSiteId)
            ->orderBy('path');

        if ($admin->isSuperAdmin()) {
            return $query;
        }

        // Agent account (bound via agent node) - check first
        // Even if they also have site roles, agent binding takes precedence
        $actor = self::primaryAgentNode($admin);
        if ($actor !== null) {
            if ((int) $actor->admin_site_id !== $adminSiteId) {
                return $query->whereRaw('0 = 1');
            }
            return $query->where('path', 'like', $actor->path.'%');
        }

        // Check if admin is a platform account (bound via admin_user_site_roles)
        $accessibleSiteIds = $admin->accessibleAdminSiteIds();
        if ($accessibleSiteIds !== null) {
            // Platform account (site admin) can see all nodes in the site
            if (in_array($adminSiteId, $accessibleSiteIds, true)) {
                return $query;
            }
            return $query->whereRaw('0 = 1');
        }

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

    /**
     * 玩家必须落在当前代理子树（agent_node_id 必填，由迁移回填根代理）。
     *
     * 性能优化：原实现先 pluck('id') 全表扫代理树再 whereIn，N+1 + 中间数组大。
     * 改用子查询（SELECT id FROM agent_nodes WHERE path LIKE ?），单次 SQL，
     * 走 idx_agent_nodes_path 索引。
     *
     * @param  Builder<Player>  $query
     */
    public static function applyToPlayerQuery(Builder $query, AdminUser $admin): void
    {
        if ($admin->isSuperAdmin()) {
            return;
        }

        $actor = self::primaryAgentNode($admin);
        if ($actor === null) {
            if (! self::isSiteOnlyOperator($admin)) {
                $query->whereRaw('0 = 1');
            }

            return;
        }

        if (! \Illuminate\Support\Facades\Schema::hasColumn('players', 'agent_node_id')) {
            return;
        }

        // 委托给统一子树过滤：先 count 一次（带 LIMIT 1 优化）确认子树非空，再用子查询展开。
        self::applySubtreeFilter($query, 'agent_node_id', (string) $actor->path);
    }

    /**
     * 在已有站点/代理范围上，再按指定节点子树收窄（超管筛选用）。
     *
     * @param  Builder<Player>  $query
     */
    public static function applyRequestedAgentNodeFilter(Builder $query, AdminUser $admin, int $agentNodeId): void
    {
        $node = AgentNode::query()->find($agentNodeId);
        if ($node === null || ! self::nodeVisibleTo($admin, $node)) {
            $query->whereRaw('0 = 1');

            return;
        }

        if (! \Illuminate\Support\Facades\Schema::hasColumn('players', 'agent_node_id')) {
            return;
        }

        self::applySubtreeFilter($query, 'agent_node_id', (string) $node->path);
    }

    /**
     * 按 path 前缀对指定列应用代理子树过滤。
     *
     * 用子查询 `IN (SELECT id FROM agent_nodes WHERE path LIKE ?)` 替代
     * 「全表 pluck 再 whereIn」：单次 SQL 走 idx_agent_nodes_path 索引，
     * 避免 N+1 与中间大数组。子查询天然短路空子树（返回空集 → IN 永不命中）。
     *
     * 注意 $path / $column 由调用方控制且为已知安全值，不走用户输入；
     * LIKE 绑定值仍用参数化。path 中 % / _ / \ 也已做转义，防止 LIKE 元字符注入。
     *
     * @param  Builder<Player>  $query
     */
    private static function applySubtreeFilter(Builder $query, string $column, string $path): void
    {
        $escaped = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $path);
        $prefix = $escaped.'%';

        $query->whereIn($column, function ($subQuery) use ($prefix): void {
            $subQuery->from('agent_nodes')->select('id')->whereRaw('path LIKE ?', [$prefix]);
        });
    }
}
