<?php

namespace App\Master\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Master\Http\Requests\SiteRequest;
use App\Master\Http\Resources\RoleListResource;
use App\Master\Http\Resources\RoleViewResource;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;
use Mtc\MercuryDataModels\Permission;
use Mtc\MercuryDataModels\Role;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class RoleController extends Controller
{
    /**
     * Permissions that only a system owner may grant to or remove from a role.
     */
    private const PROTECTED_PERMISSIONS = [
        'system-owner',
    ];

    public function index(SiteRequest $request): RoleListResource
    {
        $term = $request->input('term');
        $roles = Role::query()
            ->when(
                $request->filled('tenant'),
                fn ($query) => $query->where('tenant', $request->input('tenant'))
            )
            ->when(
                $request->filled('term'),
                fn ($query) => $query->where('name', 'like', "%$term%")
            )
            ->orderBy('name')
            ->with([
                'permissions',
            ])
            ->paginate($request->input('per_page', 15));
        return new RoleListResource($roles);
    }

    public function store(SiteRequest $request): RoleViewResource
    {
        $this->validate($request, [
            'name' => [
                'required',
                'string',
                'max:255',
                Rule::unique(Role::class, 'name')->where('guard_name', 'web'),
            ],
        ]);

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

        return new RoleViewResource($role);
    }

    public function show(Role $role): RoleViewResource
    {
        return new RoleViewResource($role);
    }

    public function update(Request $request, Role $role): RoleViewResource
    {
        $this->validate($request, [
            'permissions' => ['sometimes', 'array'],
            'permissions.*' => [
                'string',
                Rule::exists(Permission::class, 'name')->where('guard_name', 'web'),
            ],
        ]);

        $permissions = $request->input('permissions', []);
        $this->authorizeProtectedPermissionChange($role, $permissions);

        $tenant = $request->input('tenant');
        if ($tenant == 'all') {
            $tenant = null;
        }
        $role->update(['tenant' => $tenant]);
        // todo check if there are users affected by tenant change for role

        $role->permissions()->detach();
        $role->unsetRelation('permissions');
        $role->givePermissionTo($permissions);
        return new RoleViewResource($role);
    }

    /**
     * Only system owners may change whether a role carries a protected permission,
     * otherwise any mtc user could escalate themselves via a role they already hold.
     */
    private function authorizeProtectedPermissionChange(Role $role, array $permissions): void
    {
        $requested = collect($permissions)->intersect(self::PROTECTED_PERMISSIONS)->sort()->values();
        $current = $role->permissions()->pluck('name')->intersect(self::PROTECTED_PERMISSIONS)->sort()->values();

        if ($requested->all() !== $current->all() && !Auth::user()->hasPermissionTo('system-owner')) {
            throw new AccessDeniedHttpException('You do not have permission to change protected permissions.');
        }
    }
}
