<?php

namespace App\Models;

use Laravel\Sanctum\HasApiTokens;
use Illuminate\Support\Facades\DB;
use App\Support\AdminPermissionBridge;
use App\Support\AgentProfileCapabilityFilter;
use App\Models\AdminRole;
use App\Models\AgentNode;
use App\Models\AgentProfile;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Validation\ValidationException;

final class AdminUser extends Authenticatable
{
    use HasApiTokens;
    use Notifiable;

    public const ROLE_SUPER_ADMIN = 'super_admin';

    protected $table = 'admin_users';

    protected $fillable = [
        'username',
        'name',
        'email',
        'password',
        'status',
        'is_super_admin',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'last_login_at' => 'datetime',
            'password' => 'hashed',
            'is_super_admin' => 'boolean',
        ];
    }

    public static function defaultAdminSiteId(): ?int
    {
        static $cached = null;
        static $resolved = false;
        if ($resolved) {
            return $cached;
        }
        $resolved = true;

        $id = DB::table('admin_sites')->where('is_default', true)->value('id');
        if ($id === null) {
            $id = DB::table('admin_sites')->orderBy('id')->value('id');
        }
        $cached = $id !== null ? (int) $id : null;

        return $cached;
    }

    public static function requireDefaultAdminSiteId(): int
    {
        $siteId = self::defaultAdminSiteId();
        if ($siteId === null) {
            throw ValidationException::withMessages([
                'admin_site_id' => [__('admin.no_integration_site')],
            ]);
        }

        return $siteId;
    }

    /**
     * 用户在各站点上的角色（多站点 RBAC）。
     *
     * @return BelongsToMany<AdminRole, AdminUser>
     */
    public function roles(): BelongsToMany
    {
        return $this->belongsToMany(
            AdminRole::class,
            'admin_user_site_roles',
            'admin_user_id',
            'role_id',
        )->withPivot(['site_id', 'granted_at']);
    }

    /**
     * 将用户在默认站点上的角色设为指定 slug 集合（全量替换该站点 pivot）。
     *
     * @param  list<string>  $slugs
     */
    /**
     * @param  list<int>  $roleIds
     */
    public function syncAgentRoleIds(int $agentNodeId, array $roleIds): void
    {
        $roleIds = array_values(array_unique(array_map(static fn ($id): int => (int) $id, $roleIds)));

        DB::transaction(function () use ($agentNodeId, $roleIds): void {
            DB::table('admin_user_agent_roles')
                ->where('admin_user_id', $this->id)
                ->where('agent_node_id', $agentNodeId)
                ->delete();

            $now = now();
            foreach ($roleIds as $roleId) {
                DB::table('admin_user_agent_roles')->insert([
                    'admin_user_id' => $this->id,
                    'agent_node_id' => $agentNodeId,
                    'role_id' => $roleId,
                    'granted_at' => $now,
                ]);
            }

            $siteId = (int) (AgentNode::query()->where('id', $agentNodeId)->value('admin_site_id') ?? 0);
            if ($siteId > 0) {
                DB::table('admin_user_site_roles')
                    ->where('admin_user_id', $this->id)
                    ->where('site_id', $siteId)
                    ->delete();

                foreach ($roleIds as $roleId) {
                    DB::table('admin_user_site_roles')->insert([
                        'admin_user_id' => $this->id,
                        'site_id' => $siteId,
                        'role_id' => $roleId,
                        'granted_at' => $now,
                    ]);
                }
            }
        });
    }

    /** 经营代理主账号：仅平台角色 slug=agent（见 {@see \App\Support\AgentPlatformRole}）。 */
    public function syncPrimaryPlatformAgentRole(int $agentNodeId): void
    {
        $this->syncAgentRoleIds($agentNodeId, [\App\Support\AgentPlatformRole::id()]);
    }

    /**
     * @return list<string>
     */
    private function roleMenuActionPermissionCodes(): array
    {
        $agentId = $this->primaryAgentNodeId();
        if ($agentId !== null) {
            $fromAgent = $this->agentRoleMenuActionPermissionCodes($agentId);
            if ($fromAgent !== []) {
                return $fromAgent;
            }
        }

        return $this->siteRoleMenuActionPermissionCodes();
    }

    /**
     * @return list<string>
     */
    private function agentRoleMenuActionPermissionCodes(int $agentId): array
    {
        return DB::table('admin_user_agent_roles as uar')
            ->join('admin_role_menu_actions as rma', 'rma.role_id', '=', 'uar.role_id')
            ->join('admin_menu_actions as ma', 'ma.id', '=', 'rma.menu_action_id')
            ->where('uar.admin_user_id', $this->id)
            ->where('uar.agent_node_id', $agentId)
            ->where('ma.status', 1)
            ->pluck('ma.permission_code')
            ->all();
    }

    /**
     * @return list<string>
     */
    private function siteRoleMenuActionPermissionCodes(): array
    {
        return DB::table('admin_user_site_roles as usr')
            ->join('admin_role_menu_actions as rma', 'rma.role_id', '=', 'usr.role_id')
            ->join('admin_menu_actions as ma', 'ma.id', '=', 'rma.menu_action_id')
            ->where('usr.admin_user_id', $this->id)
            ->where('ma.status', 1)
            ->pluck('ma.permission_code')
            ->all();
    }

    public function effectiveRoleSource(): string
    {
        $agentId = $this->primaryAgentNodeId();
        if ($agentId !== null) {
            return $this->agentRoleMenuActionPermissionCodes($agentId) !== [] ? 'agent' : 'agent_empty';
        }

        return $this->siteRoleMenuActionPermissionCodes() !== [] ? 'site' : 'none';
    }

    public function syncRoleSlugsForDefaultSite(array $slugs): void
    {
        $siteId = self::requireDefaultAdminSiteId();
        $slugs = array_values(array_unique($slugs));
        $roleIds = DB::table('admin_roles')
            ->whereIn('slug', $slugs)
            ->pluck('id')
            ->all();

        DB::transaction(function () use ($siteId, $roleIds): void {
            DB::table('admin_user_site_roles')
                ->where('admin_user_id', $this->id)
                ->where('site_id', $siteId)
                ->delete();

            $now = now();
            foreach ($roleIds as $rid) {
                DB::table('admin_user_site_roles')->insert([
                    'admin_user_id' => $this->id,
                    'site_id' => $siteId,
                    'role_id' => (int) $rid,
                    'granted_at' => $now,
                ]);
            }

            $agentId = $this->primaryAgentNodeId();
            if ($agentId !== null) {
                $this->syncAgentRoleIds($agentId, array_map('intval', $roleIds));
            }
        });
    }

    /**
     * 平台账号角色同步：仅允许系统角色，不同步代理角色（默认站点，兼容旧调用）。
     *
     * @param  list<string>  $slugs
     */
    public function syncSystemRoleSlugs(array $slugs): void
    {
        $this->syncSystemRoleSlugsForSite(self::requireDefaultAdminSiteId(), $slugs);
    }

    /**
     * 平台账号在指定站点上的系统角色（全量替换该站点 pivot）。
     *
     * @param  list<string>  $slugs
     */
    public function syncSystemRoleSlugsForSite(int $siteId, array $slugs): void
    {
        $slugs = array_values(array_unique($slugs));
        \App\Support\SuperAdminAccount::assertNotSiteRoleAssignment($slugs);

        $roleIds = DB::table('admin_roles')
            ->where('scope_type', AdminRole::SCOPE_SYSTEM)
            ->whereIn('slug', $slugs)
            ->pluck('id')
            ->map(static fn ($id): int => (int) $id)
            ->all();

        if (count($roleIds) !== count($slugs)) {
            throw ValidationException::withMessages([
                'role_slugs' => [trans('admin.system_roles_only')],
            ]);
        }

        DB::transaction(function () use ($siteId, $roleIds): void {
            DB::table('admin_user_site_roles')
                ->where('admin_user_id', $this->id)
                ->where('site_id', $siteId)
                ->delete();

            $now = now();
            foreach ($roleIds as $rid) {
                DB::table('admin_user_site_roles')->insert([
                    'admin_user_id' => $this->id,
                    'site_id' => $siteId,
                    'role_id' => $rid,
                    'granted_at' => $now,
                ]);
            }
        });
    }

    public function isSuperAdmin(): bool
    {
        return (bool) $this->is_super_admin;
    }

    public function primaryAgentNodeId(): ?int
    {
        $id = DB::table('admin_user_agents')
            ->where('admin_user_id', $this->id)
            ->value('agent_node_id');

        return $id !== null ? (int) $id : null;
    }

    public function primaryAgentNode(): ?AgentNode
    {
        $id = $this->primaryAgentNodeId();
        if ($id === null) {
            return null;
        }

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

    public function hasPrimaryAgentBinding(): bool
    {
        return $this->primaryAgentNodeId() !== null;
    }

    public function isPlatformAccount(): bool
    {
        if ($this->isSuperAdmin()) {
            return true;
        }

        return ! $this->hasPrimaryAgentBinding();
    }

    public function isAgentAccount(): bool
    {
        return ! $this->isPlatformAccount();
    }

    /**
     * 可访问的 admin_sites.id 列表；`null` 表示不限制（超管）。
     *
     * @return list<int>|null
     */
    public function accessibleAdminSiteIds(): ?array
    {
        if ($this->isSuperAdmin()) {
            return null;
        }

        $agent = $this->primaryAgentNode();
        if ($agent !== null) {
            return [(int) $agent->admin_site_id];
        }

        $ids = DB::table('admin_user_site_roles')
            ->where('admin_user_id', $this->id)
            ->distinct()
            ->pluck('site_id')
            ->map(static fn ($id): int => (int) $id)
            ->values()
            ->all();

        return $ids;
    }

    /**
     * 仅来自「直接授权」的 menu_action.permission_code（默认站点，含 site_id 为 null 的历史行）。
     *
     * @return list<string>
     */
    public function directMenuActionPermissionCodes(): array
    {
        if ($this->isSuperAdmin()) {
            return [];
        }

        $siteId = self::defaultAdminSiteId();
        $query = DB::table('admin_user_menu_actions as uma')
            ->join('admin_menu_actions as ma', 'ma.id', '=', 'uma.menu_action_id')
            ->where('uma.admin_user_id', $this->id)
            ->where('ma.status', 1);

        if ($siteId !== null) {
            $query->where(function ($q) use ($siteId): void {
                $q->where('uma.site_id', $siteId)->orWhereNull('uma.site_id');
            });
        } else {
            $query->whereNull('uma.site_id');
        }

        $rows = $query->pluck('ma.permission_code')->all();

        $out = [];
        foreach ($rows as $code) {
            if (is_string($code) && $code !== '') {
                $out[$code] = true;
            }
        }

        return array_keys($out);
    }

    /**
     * 直接授权对应的 `prd.*` 展示列表（与 {@see self::directMenuActionPermissionCodes()} 桥接）。
     *
     * @return list<string>
     */
    public function directLegacyPermissionSlugs(): array
    {
        return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($this->directMenuActionPermissionCodes());
    }

    /**
     * 角色 + 直接授权合并后的 menu_action.permission_code。
     *
     * @return list<string>
     */
    public function effectiveMenuActionPermissionCodes(): array
    {
        if ($this->isSuperAdmin()) {
            $codes = DB::table('admin_menu_actions')->where('status', 1)->pluck('permission_code')->all();
            $out = [];
            foreach ($codes as $c) {
                if (is_string($c) && $c !== '') {
                    $out[$c] = true;
                }
            }

            return array_keys($out);
        }

        $fromRoles = $this->roleMenuActionPermissionCodes();

        $merged = [];
        foreach (array_merge($fromRoles, $this->directMenuActionPermissionCodes()) as $c) {
            if (is_string($c) && $c !== '') {
                $merged[$c] = true;
            }
        }

        $codes = array_keys($merged);

        // 平台账号（站点管理员等）不按线路内 AgentProfile 收紧；该过滤器仅作用于绑定代理节点的主账号。
        if (! $this->hasPrimaryAgentBinding()) {
            return $codes;
        }

        return AgentProfileCapabilityFilter::applyToMenuActionCodes(
            $codes,
            $this->primaryAgentProfile(),
            $this->primaryAgentNode(),
        );
    }

    private function primaryAgentProfile(): ?AgentProfile
    {
        $agentId = $this->primaryAgentNodeId();
        if ($agentId === null) {
            return null;
        }

        return AgentProfile::query()->where('agent_node_id', $agentId)->first();
    }

    /** 是否具备指定权限：`prd.*` 走 legacy_map；否则按 permission_code 精确匹配。含 `super_admin` 全放行。 */
    public function hasAdminPermission(string $slug): bool
    {
        if ($this->isSuperAdmin()) {
            return true;
        }

        $effective = $this->effectiveMenuActionPermissionCodes();
        if ($slug !== '' && in_array($slug, $effective, true)) {
            return true;
        }

        if (! str_starts_with($slug, 'prd.')) {
            return false;
        }

        $needed = AdminPermissionBridge::menuActionCodesForLegacy($slug);
        if ($needed === []) {
            return false;
        }

        return count(array_diff($needed, $effective)) === 0;
    }

    /**
     * 仅按 permission_code 判定（不处理 prd.* 映射）。
     */
    public function hasPermissionCode(string $permissionCode): bool
    {
        if ($permissionCode === '') {
            return false;
        }

        if ($this->isSuperAdmin()) {
            return true;
        }

        return in_array($permissionCode, $this->effectiveMenuActionPermissionCodes(), true);
    }

    /**
     * @return list<string> 与 Next 侧栏兼容的 `prd.*` slug 列表
     */
    public function adminPermissionSlugs(): array
    {
        if ($this->isSuperAdmin()) {
            return AdminPermissionBridge::allLegacySlugs();
        }

        return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($this->effectiveMenuActionPermissionCodes());
    }

    /**
     * @return list<string>
     */
    public function adminRoleSlugs(): array
    {
        $this->loadMissing('roles');

        $slugs = $this->roles
            ->pluck('slug')
            ->filter(static fn ($slug): bool => is_string($slug) && $slug !== '')
            ->unique()
            ->values()
            ->all();

        if ($this->isSuperAdmin()) {
            $slugs = array_values(array_unique(array_merge([self::ROLE_SUPER_ADMIN], $slugs)));
        }

        return $slugs;
    }
}
