<?php

namespace Maestro\Core\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Maestro\Core\Support\PermissionGuardName;
use Spatie\Permission\Models\Role;

class RoleSettingsController extends Controller
{
    public function index(Request $request): JsonResponse
    {
        abort_unless($request->user()?->can('roles.view'), 403);

        $guardName = PermissionGuardName::forApp();

        $roles = Role::query()->where('guard_name', $guardName)
            ->with('permissions:id,name')
            ->orderBy('name')
            ->get()
            ->map(fn (Role $role) => [
                'id' => $role->id,
                'name' => $role->name,
                'guard_name' => $role->guard_name,
                'permissions' => $role->permissions->pluck('name')->values()->all(),
            ]);

        return response()->json(['data' => $roles]);
    }

    public function store(Request $request): JsonResponse
    {
        abort_unless($request->user()?->can('roles.create'), 403);

        $guardName = PermissionGuardName::forApp();

        $validator = Validator::make($request->all(), [
            'name' => ['required', 'string', 'max:255', Rule::unique('roles', 'name')->where('guard_name', $guardName)],
            'permissions' => ['nullable', 'array'],
            'permissions.*' => ['string', Rule::exists('permissions', 'name')->where('guard_name', $guardName)],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $role = Role::create([
            'name' => $request->input('name'),
            'guard_name' => $guardName,
        ]);

        $permissions = $request->input('permissions', []);
        if ($permissions !== []) {
            $role->syncPermissions($permissions);
        }

        $role->load('permissions:id,name');

        activity('roles')
            ->causedBy($request->user())
            ->performedOn($role)
            ->withProperties([
                'name' => $role->name,
                'permissions' => $role->permissions->pluck('name')->values()->all(),
            ])
            ->event('created')
            ->log('created');

        return response()->json([
            'data' => [
                'id' => $role->id,
                'name' => $role->name,
                'permissions' => $role->permissions->pluck('name')->values()->all(),
            ],
        ], 201);
    }

    public function update(Request $request, Role $role): JsonResponse
    {
        abort_unless($request->user()?->can('roles.edit'), 403);

        $guardName = PermissionGuardName::forApp();

        abort_unless($role->guard_name === $guardName, 404);

        $validator = Validator::make($request->all(), [
            'name' => ['sometimes', 'string', 'max:255', Rule::unique('roles', 'name')->where('guard_name', $guardName)->ignore($role->id)],
            'permissions' => ['nullable', 'array'],
            'permissions.*' => ['string', Rule::exists('permissions', 'name')->where('guard_name', $guardName)],
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed.',
                'errors' => $validator->errors(),
            ], 422);
        }

        $mutated = false;

        if ($request->filled('name') && $request->input('name') !== $role->name) {
            abort_if($role->name === 'admin', 422, 'Renaming built-in roles is disabled.');
            $role->update(['name' => $request->input('name')]);
            $mutated = true;
        }

        if ($request->has('permissions')) {
            abort_if($role->name === 'admin', 422, 'Permissions for admin are managed in seeds.');
            $role->syncPermissions($request->input('permissions', []));
            $mutated = true;
        }

        $role->load('permissions:id,name');

        if ($mutated) {
            activity('roles')
                ->causedBy($request->user())
                ->performedOn($role)
                ->withProperties([
                    'name' => $role->name,
                    'permissions' => $role->permissions->pluck('name')->values()->all(),
                ])
                ->event('updated')
                ->log('updated');
        }

        return response()->json([
            'data' => [
                'id' => $role->id,
                'name' => $role->name,
                'permissions' => $role->permissions->pluck('name')->values()->all(),
            ],
        ]);
    }

    public function destroy(Request $request, Role $role): JsonResponse
    {
        abort_unless($request->user()?->can('roles.delete'), 403);

        $guardName = PermissionGuardName::forApp();

        abort_unless($role->guard_name === $guardName, 404);
        abort_if($role->name === 'admin', 422, 'Cannot delete admin role.');
        abort_if($role->users()->exists(), 422, 'Users still have this role.');

        activity('roles')
            ->causedBy($request->user())
            ->withProperties([
                'role_id' => $role->id,
                'name' => $role->name,
            ])
            ->event('deleted')
            ->log('deleted');

        $role->delete();

        return response()->json(['message' => 'Deleted.']);
    }
}
