<?php

namespace App\Console\Commands;

use App\Models\User;
use App\Models\UserServerPermission;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

class UserManageCommand extends Command
{
    protected $signature = 'user:manage
        {action : create|reset-password|list|init-rbac|create-role|assign-role|remove-role|assign-permission|remove-permission|grant-server|set-admin|unset-admin}
        {--email= : User email}
        {--nickname= : User nickname}
        {--phone= : User phone}
        {--password= : User password}
        {--role= : Role name}
        {--permission= : Permission name}
        {--server-id= : Server resource id}
        {--ssh=0 : Grant ssh 1|0}
        {--sftp=0 : Grant sftp 1|0}
        {--rdp=0 : Grant rdp 1|0}';

    protected $description = 'User and permission management command';

    public function handle(): int
    {
        return match ($this->argument('action')) {
            'create' => $this->createUser(),
            'reset-password' => $this->resetPassword(),
            'list' => $this->listUsers(),
            'init-rbac' => $this->initRbac(),
            'create-role' => $this->createRole(),
            'assign-role' => $this->assignRole(),
            'remove-role' => $this->removeRole(),
            'assign-permission' => $this->assignPermission(),
            'remove-permission' => $this->removePermission(),
            'grant-server' => $this->grantServerPermission(),
            'set-admin' => $this->setAdmin(),
            'unset-admin' => $this->unsetAdmin(),
            default => $this->invalidAction(),
        };
    }

    private function createUser(): int
    {
        $email = (string) $this->option('email');
        $nickname = (string) $this->option('nickname');
        $password = (string) $this->option('password');

        if ($email === '' || $nickname === '' || $password === '') {
            $this->error('email, nickname, password are required.');

            return self::FAILURE;
        }

        $user = User::query()->create([
            'email' => $email,
            'nickname' => $nickname,
            'phone' => $this->option('phone') ?: null,
            'password' => $password,
        ]);

        if ($this->option('role')) {
            $role = Role::query()->firstOrCreate([
                'name' => (string) $this->option('role'),
                'guard_name' => 'api',
            ]);
            $user->assignRole($role);
        }

        $this->info("User created: {$user->id}");

        return self::SUCCESS;
    }

    private function resetPassword(): int
    {
        $user = $this->findUserByEmail();
        if (! $user) {
            return self::FAILURE;
        }

        $password = (string) $this->option('password');

        if ($password === '') {
            $this->error('password is required.');

            return self::FAILURE;
        }

        $user->password = Hash::make($password);
        $user->save();

        $this->info('Password reset success.');

        return self::SUCCESS;
    }

    private function listUsers(): int
    {
        $rows = User::query()->with(['roles', 'permissions'])->latest()->get()->map(fn (User $user): array => [
            'id' => $user->id,
            'nickname' => $user->nickname,
            'email' => $user->email,
            'roles' => $user->roles->pluck('name')->implode(','),
            'permissions' => $user->permissions->pluck('name')->implode(','),
        ])->toArray();

        $this->table(['ID', 'Nickname', 'Email', 'Roles', 'Permissions'], $rows);

        return self::SUCCESS;
    }

    private function initRbac(): int
    {
        $permissions = [
            'platform.users.view',
            'platform.users.manage',
            'platform.roles.view',
            'platform.roles.manage',
            'platform.permissions.view',
            'platform.permissions.manage',
            'platform.servers.view',
            'platform.servers.manage',
            'platform.accounts.view',
            'platform.accounts.manage',
            'platform.logs.view',
            'platform.logs.manage',
            'resource.servers.use',
        ];

        foreach ($permissions as $permissionName) {
            Permission::query()->firstOrCreate([
                'name' => $permissionName,
                'guard_name' => 'api',
            ]);
        }

        $adminRole = Role::query()->firstOrCreate([
            'name' => 'admin',
            'guard_name' => 'api',
        ]);

        $userRole = Role::query()->firstOrCreate([
            'name' => 'user',
            'guard_name' => 'api',
        ]);

        $adminRole->syncPermissions($permissions);
        $userRole->syncPermissions([
            'resource.servers.use',
        ]);

        Role::query()->where('guard_name', 'api')->whereIn('name', ['operator', 'member'])->delete();

        $this->info('RBAC initialized.');

        return self::SUCCESS;
    }

    private function createRole(): int
    {
        $roleName = (string) $this->option('role');

        if ($roleName === '') {
            $this->error('role is required.');

            return self::FAILURE;
        }

        Role::query()->firstOrCreate([
            'name' => $roleName,
            'guard_name' => 'api',
        ]);

        $this->info('Role created.');

        return self::SUCCESS;
    }

    private function assignRole(): int
    {
        $user = $this->findUserByEmail();
        if (! $user) {
            return self::FAILURE;
        }

        $roleName = (string) $this->option('role');

        if ($roleName === '') {
            $this->error('role is required.');

            return self::FAILURE;
        }

        $user->assignRole($roleName);
        $this->info('Role assigned.');

        return self::SUCCESS;
    }

    private function removeRole(): int
    {
        $user = $this->findUserByEmail();
        if (! $user) {
            return self::FAILURE;
        }

        $roleName = (string) $this->option('role');

        if ($roleName === '') {
            $this->error('role is required.');

            return self::FAILURE;
        }

        $user->removeRole($roleName);
        $this->info('Role removed.');

        return self::SUCCESS;
    }

    private function assignPermission(): int
    {
        $user = $this->findUserByEmail();
        if (! $user) {
            return self::FAILURE;
        }

        $permission = (string) $this->option('permission');

        if ($permission === '') {
            $this->error('permission is required.');

            return self::FAILURE;
        }

        $user->givePermissionTo($permission);
        $this->info('Permission assigned.');

        return self::SUCCESS;
    }

    private function removePermission(): int
    {
        $user = $this->findUserByEmail();
        if (! $user) {
            return self::FAILURE;
        }

        $permission = (string) $this->option('permission');

        if ($permission === '') {
            $this->error('permission is required.');

            return self::FAILURE;
        }

        $user->revokePermissionTo($permission);
        $this->info('Permission removed.');

        return self::SUCCESS;
    }

    private function grantServerPermission(): int
    {
        $user = $this->findUserByEmail();
        if (! $user) {
            return self::FAILURE;
        }

        $serverId = (int) $this->option('server-id');

        if ($serverId <= 0) {
            $this->error('server-id is required and must be > 0.');

            return self::FAILURE;
        }

        UserServerPermission::query()->updateOrCreate(
            ['user_id' => $user->id, 'server_resource_id' => $serverId],
            [
                'can_ssh' => (bool) ((int) $this->option('ssh')),
                'can_sftp' => (bool) ((int) $this->option('sftp')),
                'can_rdp' => (bool) ((int) $this->option('rdp')),
            ]
        );

        $this->info('Server permission updated.');

        return self::SUCCESS;
    }

    private function setAdmin(): int
    {
        $user = $this->findUserByEmail();
        if (! $user) {
            return self::FAILURE;
        }

        $adminRole = Role::query()->firstOrCreate([
            'name' => 'admin',
            'guard_name' => 'api',
        ]);

        $allPermissions = Permission::query()
            ->where('guard_name', 'api')
            ->pluck('name')
            ->all();

        $adminRole->syncPermissions($allPermissions);

        $user->syncRoles([$adminRole->name]);
        $user->syncPermissions([]);

        $this->info('User set as admin with all permissions.');

        return self::SUCCESS;
    }

    private function unsetAdmin(): int
    {
        $user = $this->findUserByEmail();
        if (! $user) {
            return self::FAILURE;
        }

        if (! $user->hasRole('admin', 'api')) {
            $this->warn('User is not admin.');

            return self::SUCCESS;
        }

        $user->removeRole('admin');

        $this->info('Admin role removed from user.');

        return self::SUCCESS;
    }

    private function findUserByEmail(): ?User
    {
        $email = (string) $this->option('email');

        if ($email === '') {
            $this->error('email is required.');

            return null;
        }

        $user = User::query()->where('email', $email)->first();

        if (! $user) {
            $this->error('User not found.');

            return null;
        }

        return $user;
    }

    private function invalidAction(): int
    {
        $this->error('Invalid action.');

        return self::FAILURE;
    }
}
