<?php

namespace App\Http\Controllers\Admin;

use App\Actions\Roles\CreateRole;
use App\Actions\Roles\DeleteRole;
use App\Actions\Roles\ExportRoles;
use App\Actions\Roles\UpdateRole;
use App\Exceptions\RoleNotDeletable;
use App\Http\Controllers\Concerns\RespondsJsonOrRedirect;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\RoleRequest;
use App\Models\Permission;
use App\Models\Role;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

/**
 * Role builder — list + dedicated create/edit pages carrying the
 * permission matrix. Gated by the `roles.manage` permission
 * (docs/features/auth-users.md §6); super admins bypass via Gate::before.
 */
class RoleController extends Controller
{
    use RespondsJsonOrRedirect;

    public function index(): View
    {
        $this->authorize('roles.manage');

        $roles = Role::query()
            ->withCount('permissions')
            ->ordered()
            ->get()
            ->map(function (Role $role) {
                $role->assignments = $role->assignmentCount();
                return $role;
            });

        return view('admin.roles.index', ['roles' => $roles]);
    }

    public function create(): View
    {
        $this->authorize('roles.manage');

        return view('admin.roles.create', $this->formContext());
    }

    public function store(RoleRequest $request, CreateRole $create): JsonResponse|RedirectResponse
    {
        $this->authorize('roles.manage');

        $role = ($create)($request->roleAttributes(), $request->permissionIds());

        return $this->jsonOrRedirect(
            $request,
            __('roles.flash.created', ['name' => $role->name]),
            route('admin.roles.index'),
        );
    }

    public function edit(Role $role): View
    {
        $this->authorize('roles.manage');

        return view('admin.roles.edit', array_merge($this->formContext(), [
            'role'        => $role,
            'selectedIds' => $role->permissions()->pluck('permissions.id')->all(),
        ]));
    }

    public function update(RoleRequest $request, Role $role, UpdateRole $update): JsonResponse|RedirectResponse
    {
        $this->authorize('roles.manage');

        ($update)($role, $request->roleAttributes(), $request->permissionIds());

        return $this->jsonOrRedirect(
            $request,
            __('roles.flash.updated', ['name' => $role->name]),
            route('admin.roles.index'),
        );
    }

    public function destroy(Request $request, Role $role, DeleteRole $delete): JsonResponse|RedirectResponse
    {
        $this->authorize('roles.manage');

        if (pos_is_demo()) {
            return $this->demoBlocked($request, route('admin.roles.index'));
        }

        $name = $role->name;

        try {
            ($delete)($role);
        } catch (RoleNotDeletable $e) {
            return $this->jsonOrError(
                $request,
                __('roles.errors.'.$e->reason, ['name' => $name, 'count' => $e->count]),
                route('admin.roles.index'),
            );
        }

        return $this->jsonOrRedirect(
            $request,
            __('roles.flash.deleted', ['name' => $name]),
            route('admin.roles.index'),
        );
    }

    public function export(Request $request, ExportRoles $export): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        $this->authorize('viewAny', Role::class);

        $format = $request->query('format', 'csv');
        if (! in_array($format, ['csv', 'xlsx'], true)) {
            abort(422, 'Unsupported export format. Use csv or xlsx.');
        }

        return ($export)($format);
    }

    /* ── Helpers ────────────────────────────────────────────────── */

    /** @return array<string, mixed> */
    private function formContext(): array
    {
        return [
            // Permissions grouped for the collapsible matrix.
            'groups' => Permission::query()->ordered()->get()->groupBy('group'),
        ];
    }
}
