<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\StoreRoleRequest;
use App\Http\Requests\Admin\UpdateRoleRequest;
use App\Models\AuditLog;
use Illuminate\Http\RedirectResponse;
use Inertia\Inertia;
use Inertia\Response;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

class RoleController extends Controller
{
    /**
     * Built-in roles that cannot be deleted or renamed.
     *
     * @var list<string>
     */
    private const PROTECTED_ROLES = ['admin', 'customer', 'super_admin'];

    public function index(): Response
    {
        $roles = Role::where('guard_name', 'web')
            ->withCount(['permissions', 'users'])
            ->orderBy('name')
            ->get()
            ->map(fn (Role $role): array => [
                'id' => $role->id,
                'name' => $role->name,
                'permissions_count' => $role->permissions_count,
                'users_count' => $role->users_count,
                'is_protected' => in_array($role->name, self::PROTECTED_ROLES, true),
                'created_at' => $role->created_at?->toISOString(),
            ]);

        return Inertia::render('Admin/Staff/Roles/Index', [
            'roles' => $roles,
        ]);
    }

    public function create(): Response
    {
        $permissions = $this->groupedPermissions();

        return Inertia::render('Admin/Staff/Roles/Create', [
            'permissionGroups' => $permissions,
        ]);
    }

    public function store(StoreRoleRequest $request): RedirectResponse
    {
        $role = Role::create([
            'name' => $request->validated('name'),
            'guard_name' => 'web',
        ]);

        $role->syncPermissions($request->validated('permissions'));

        AuditLog::create([
            'admin_id' => $request->user()->id,
            'action' => 'role_created',
            'resource_type' => 'role',
            'resource_id' => $role->id,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'changes' => [
                'name' => $role->name,
                'permissions' => $request->validated('permissions'),
            ],
        ]);

        return redirect()
            ->route('admin.roles.index')
            ->with('success', "Role \"{$role->name}\" created successfully.");
    }

    public function edit(Role $role): Response
    {
        $permissions = $this->groupedPermissions();
        $rolePermissions = $role->permissions->pluck('name')->toArray();
        $usersCount = $role->users()->count();

        return Inertia::render('Admin/Staff/Roles/Edit', [
            'role' => [
                'id' => $role->id,
                'name' => $role->name,
                'is_protected' => in_array($role->name, self::PROTECTED_ROLES, true),
                'permissions' => $rolePermissions,
            ],
            'permissionGroups' => $permissions,
            'usersCount' => $usersCount,
        ]);
    }

    public function update(UpdateRoleRequest $request, Role $role): RedirectResponse
    {
        $oldPermissions = $role->permissions->pluck('name')->toArray();

        // Only update name if not a protected role
        if (! in_array($role->name, self::PROTECTED_ROLES, true) && $request->has('name')) {
            $role->update(['name' => $request->validated('name')]);
        }

        $role->syncPermissions($request->validated('permissions'));

        AuditLog::create([
            'admin_id' => $request->user()->id,
            'action' => 'role_updated',
            'resource_type' => 'role',
            'resource_id' => $role->id,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'changes' => [
                'name' => $role->name,
                'old_permissions' => $oldPermissions,
                'new_permissions' => $request->validated('permissions'),
            ],
        ]);

        return redirect()
            ->route('admin.roles.index')
            ->with('success', "Role \"{$role->name}\" updated successfully.");
    }

    public function destroy(Role $role): RedirectResponse
    {
        if (in_array($role->name, self::PROTECTED_ROLES, true)) {
            return redirect()
                ->route('admin.roles.index')
                ->with('error', "Cannot delete the built-in \"{$role->name}\" role.");
        }

        // Check if any users are assigned to this role
        if ($role->users()->count() > 0) {
            return redirect()
                ->route('admin.roles.index')
                ->with('error', "Cannot delete \"{$role->name}\" — it still has assigned users.");
        }

        $roleName = $role->name;

        AuditLog::create([
            'admin_id' => auth()->id(),
            'action' => 'role_deleted',
            'resource_type' => 'role',
            'resource_id' => $role->id,
            'ip_address' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'changes' => ['name' => $roleName],
        ]);

        $role->delete();

        return redirect()
            ->route('admin.roles.index')
            ->with('success', "Role \"{$roleName}\" deleted successfully.");
    }

    /**
     * Get all permissions grouped by category.
     *
     * @return array<string, list<string>>
     */
    private function groupedPermissions(): array
    {
        $permissions = Permission::where('guard_name', 'web')
            ->orderBy('name')
            ->pluck('name')
            ->toArray();

        $grouped = [];

        foreach ($permissions as $permission) {
            if (str_contains($permission, '.')) {
                $parts = explode('.', $permission, 2);
                $grouped[$parts[0]][] = $permission;
            } else {
                $grouped['legacy'][] = $permission;
            }
        }

        // Sort groups alphabetically
        ksort($grouped);

        return $grouped;
    }
}
