<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\StaffRole;
use App\Models\User;
use App\Support\Authorization\PermissionCatalog;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class RoleController extends Controller
{
    private function permissionModules(): array
    {
        return PermissionCatalog::adminModules();
    }

    private function normalizePermissions(array $inputPermissions): array
    {
        return PermissionCatalog::normalizeSubmittedPermissions($inputPermissions, PermissionCatalog::PANEL_ADMIN);
    }

    private function membersCount(StaffRole $role): int
    {
        return (int) DB::table(config('permission.table_names.model_has_roles') . ' as model_has_roles')
            ->join('users', 'users.id', '=', 'model_has_roles.model_id')
            ->where('model_has_roles.role_id', $role->id)
            ->where('model_has_roles.model_type', User::class)
            ->whereNull('model_has_roles.organization_id')
            ->where('users.role', User::ROLE_STAFF)
            ->count();
    }

    public function index()
    {
        $roles = [
            [
                'id' => null,
                'name' => 'ADMIN',
                'permissions' => 'Acceso total al panel y configuración global.',
                'members' => User::where('role', User::ROLE_ADMIN)->count(),
                'type' => 'BASE',
                'editable' => false,
                'deletable' => false,
            ],
        ];

        $customStaffRoles = StaffRole::query()
            ->platform()
            ->custom()
            ->where('is_active', true)
            ->orderBy('name')
            ->get()
            ->map(function (StaffRole $role) {
                $members = $this->membersCount($role);
                return [
                    'id' => $role->id,
                    'name' => $role->name,
                    'code' => $role->code,
                    'permissions' => $role->permissionCount() . ' permisos',
                    'permissions_map' => $role->permissionsMap(PermissionCatalog::PANEL_ADMIN),
                    'members' => $members,
                    'type' => 'STAFF_ROLE',
                    'editable' => true,
                    'deletable' => $members === 0,
                ];
            })
            ->all();

        return view('content.admin.roles.index', [
            'roles' => array_merge($roles, $customStaffRoles),
            'permissionModules' => $this->permissionModules(),
            'permissionActionLabels' => PermissionCatalog::actionLabels(),
        ]);
    }

    public function store(Request $request)
    {
        $request->validate([
            'name' => ['required', 'string', 'max:120'],
            'permissions' => ['nullable', 'array'],
        ]);

        $code = Str::upper(Str::of($request->input('name'))->slug('_'));

        if (in_array($code, [User::ROLE_ADMIN], true)) {
            return back()->withErrors([
                'name' => 'Ese nombre está reservado.',
            ])->withInput();
        }

        if (StaffRole::query()->where('code', $code)->whereNull('organization_id')->exists()) {
            return back()->withErrors([
                'name' => 'Ya existe un rol con ese nombre.',
            ])->withInput();
        }

        StaffRole::create([
            'name' => $request->input('name'),
            'code' => $code,
            'organization_id' => null,
            'guard_name' => 'web',
            'is_system' => false,
            'is_active' => true,
        ])->syncPermissions($this->normalizePermissions($request->input('permissions', [])));

        return redirect()->route('admin.roles.index')->with('status', 'Rol creado correctamente.');
    }

    public function update(Request $request, StaffRole $staffRole)
    {
        if ($staffRole->is_system) {
            return redirect()->route('admin.roles.index')->withErrors([
                'name' => 'No se puede editar un rol del sistema.',
            ]);
        }

        $request->validate([
            'name' => ['required', 'string', 'max:120'],
            'permissions' => ['nullable', 'array'],
        ]);

        $newCode = Str::upper(Str::of($request->input('name'))->slug('_'));

        if (in_array($newCode, [User::ROLE_ADMIN], true)) {
            return back()->withErrors([
                'name' => 'Ese nombre está reservado.',
            ])->withInput();
        }

        if (StaffRole::query()->where('code', $newCode)->whereNull('organization_id')->whereKeyNot($staffRole->id)->exists()) {
            return back()->withErrors([
                'name' => 'Ya existe un rol con ese nombre.',
            ])->withInput();
        }

        $oldCode = $staffRole->code;

        $staffRole->update([
            'name' => $request->input('name'),
            'code' => $newCode,
        ]);
        $staffRole->syncPermissions($this->normalizePermissions($request->input('permissions', [])));

        if ($oldCode !== $newCode) {
            User::where('staff_role', $oldCode)->update(['staff_role' => $newCode]);
        }

        return redirect()->route('admin.roles.index')->with('status', 'Rol actualizado correctamente.');
    }

    public function destroy(StaffRole $staffRole)
    {
        if ($staffRole->is_system) {
            return redirect()->route('admin.roles.index')->withErrors([
                'name' => 'No se puede eliminar un rol del sistema.',
            ]);
        }

        $members = $this->membersCount($staffRole);

        if ($members > 0) {
            return redirect()->route('admin.roles.index')->withErrors([
                'name' => 'No se puede eliminar el rol porque tiene usuarios asignados.',
            ]);
        }

        $staffRole->delete();

        return redirect()->route('admin.roles.index')->with('status', 'Rol eliminado correctamente.');
    }
}
