<?php

namespace App\Http\Controllers\Api;

use App\Auth\Permissions;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Spatie\Permission\PermissionRegistrar;

class RoleApiController extends Controller
{
    public function index(): JsonResponse
    {
        $this->authorize(Permissions::ADMIN_ROLES_MANAGE);
        $roles = Role::withCount('permissions')->orderBy('name')->get();
        return response()->json(['data' => $roles]);
    }

    public function show(Role $role): JsonResponse
    {
        $this->authorize(Permissions::ADMIN_ROLES_MANAGE);
        return response()->json(['data' => $role->load('permissions')]);
    }

    public function store(Request $request): JsonResponse
    {
        $this->authorize(Permissions::ADMIN_ROLES_MANAGE);
        $data = $request->validate([
            'name' => 'required|string|max:64',
            'permissions' => 'sometimes|array',
            'permissions.*' => 'string',
        ]);
        // Disallow overwriting system roles
        if (in_array($data['name'], array_keys(Permissions::roleMatrix()), true)) {
            return response()->json(['error' => 'System role name is reserved'], 422);
        }
        $role = Role::create(['name' => $data['name'], 'guard_name' => 'web']);
        if (! empty($data['permissions'])) {
            $role->syncPermissions($data['permissions']);
        }
        return response()->json(['data' => $role->load('permissions')], 201);
    }

    public function update(Request $request, Role $role): JsonResponse
    {
        $this->authorize(Permissions::ADMIN_ROLES_MANAGE);
        if (in_array($role->name, array_keys(Permissions::roleMatrix()), true)) {
            return response()->json(['error' => 'Cannot rename system role'], 422);
        }
        $data = $request->validate(['name' => 'required|string|max:64']);
        $role->update($data);
        return response()->json(['data' => $role]);
    }

    public function destroy(Role $role): JsonResponse
    {
        $this->authorize(Permissions::ADMIN_ROLES_MANAGE);
        if (in_array($role->name, array_keys(Permissions::roleMatrix()), true)) {
            return response()->json(['error' => 'Cannot delete system role'], 422);
        }
        $role->delete();
        return response()->json(['deleted' => true]);
    }

    public function permissions(Role $role): JsonResponse
    {
        $this->authorize(Permissions::ADMIN_ROLES_MANAGE);
        return response()->json(['data' => $role->permissions->pluck('name')]);
    }

    public function syncPermissions(Request $request, Role $role): JsonResponse
    {
        $this->authorize(Permissions::ADMIN_ROLES_MANAGE);
        $data = $request->validate([
            'permissions' => 'required|array',
            'permissions.*' => 'string',
        ]);
        $role->syncPermissions($data['permissions']);
        app(PermissionRegistrar::class)->forgetCachedPermissions();
        return response()->json(['data' => $role->fresh()->permissions->pluck('name')]);
    }

    public function permissionCatalog(): JsonResponse
    {
        return response()->json([
            'data' => Permission::orderBy('name')->get(['id', 'name']),
            'grouped' => Permissions::grouped(),
            'labels' => Permissions::labels(),
            'roles' => Permissions::roleLabels(),
        ]);
    }

    private function authorize(string $permission): void
    {
        if (! auth()->user() || ! auth()->user()->canDo($permission)) {
            abort(403, "Missing permission: $permission");
        }
    }
}
