<?php

namespace App\Support;

use App\Models\AdminUser;
use App\Models\AgentNode;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

final class AgentDelegationAuthorization
{
    /**
     * @return list<string> menu_action.permission_code
     */
    public static function delegationMenuActionCodesForAgent(AgentNode $agent): array
    {
        if ($agent->isRoot()) {
            return DB::table('admin_menu_actions')->where('status', 1)->pluck('permission_code')->all();
        }

        return DB::table('agent_delegation_grants as g')
            ->join('admin_menu_actions as ma', 'ma.id', '=', 'g.menu_action_id')
            ->where('g.child_agent_id', $agent->id)
            ->where('ma.status', 1)
            ->pluck('ma.permission_code')
            ->all();
    }

    /**
     * @return list<string> prd.*
     */
    public static function delegationLegacySlugsForAgent(AgentNode $agent): array
    {
        $codes = self::delegationMenuActionCodesForAgent($agent);

        return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($codes);
    }

    /**
     * @return list<string> prd.*
     */
    public static function delegationLegacySlugsForAdminUser(AdminUser $admin): array
    {
        if ($admin->isSuperAdmin()) {
            return AdminPermissionBridge::allLegacySlugs();
        }

        // Check if admin is a platform account (bound via admin_user_site_roles)
        $accessibleSiteIds = $admin->accessibleAdminSiteIds();
        if ($accessibleSiteIds !== null) {
            // Platform account (site admin) - return their actual permissions
            return $admin->adminPermissionSlugs();
        }

        // Agent account (bound via agent node)
        $node = $admin->primaryAgentNode();
        if ($node === null) {
            return [];
        }

        return self::delegationLegacySlugsForAgent($node);
    }

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

        if (! AdminAgentScope::nodeVisibleTo($admin, $child)) {
            return false;
        }

        $parentId = $child->parent_id;
        if ($parentId === null) {
            return false;
        }

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

        // Agent account (bound via agent node)
        $actor = AdminAgentScope::primaryAgentNode($admin);
        if ($actor === null) {
            return false;
        }

        return (int) $parentId === (int) $actor->id
            || AdminAgentScope::nodeManageableBy($admin, AgentNode::query()->find($parentId) ?? $child);
    }

    /**
     * @param  list<array{menu_action_id: int, can_delegate?: bool}>  $grants
     */
    public static function assertGrantsAllowed(AdminUser $actor, AgentNode $child, array $grants): void
    {
        if ($actor->isSuperAdmin()) {
            return;
        }

        if (! self::childIsManageableBy($actor, $child)) {
            throw ValidationException::withMessages(['child_agent_id' => ['not_manageable']]);
        }

        $actorCodes = $actor->effectiveMenuActionPermissionCodes();
        $actorCodeSet = array_fill_keys($actorCodes, true);

        $parent = $child->parent;
        if ($parent === null && $child->parent_id !== null) {
            $parent = AgentNode::query()->find($child->parent_id);
        }

        $parentCeiling = $parent !== null
            ? self::delegationMenuActionCodesForAgent($parent)
            : $actorCodes;

        if ($parent !== null && ! $parent->isRoot() && $parentCeiling === []) {
            $parentCeiling = $actorCodes;
        }

        $parentCeilingSet = array_fill_keys($parentCeiling, true);

        foreach ($grants as $grant) {
            $actionId = (int) ($grant['menu_action_id'] ?? 0);
            $code = DB::table('admin_menu_actions')->where('id', $actionId)->value('permission_code');
            if (! is_string($code) || $code === '') {
                throw ValidationException::withMessages(['grants' => ['invalid_menu_action']]);
            }

            if (! isset($actorCodeSet[$code])) {
                throw ValidationException::withMessages(['grants' => ['exceeds_actor: '.$code]]);
            }

            if ($parent !== null && ! $parent->isRoot() && ! isset($parentCeilingSet[$code])) {
                throw ValidationException::withMessages(['grants' => ['exceeds_parent_ceiling: '.$code]]);
            }
        }
    }

    /**
     * @param  list<string>  $permissionSlugs
     */
    public static function assertRoleSlugsWithinAgentCeiling(
        AgentNode $ownerAgent,
        array $permissionSlugs,
        AdminUser $actor,
    ): void {
        $ceiling = self::delegationLegacySlugsForAgent($ownerAgent);
        if ($ceiling === []) {
            // 尚未配置下放上限：与 P2 一致，仅校验操作者自身权限
            AgentRoleAuthorization::assertSlugsWithinActor($actor, $permissionSlugs);

            return;
        }

        $invalid = array_values(array_diff($permissionSlugs, $ceiling));
        if ($invalid !== []) {
            throw ValidationException::withMessages([
                'permission_slugs' => ['exceeds_delegation_ceiling: '.implode(', ', $invalid)],
            ]);
        }
    }
}
