<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use App\Services\AuditService;
use App\Services\TenantRoleService;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class TenantRoleController extends Controller
{
    public function __construct(
        protected AuditService $audit
    ) {}
    public function index()
    {
        $this->authorizeView();
        $roles = Role::query()
            ->withCount('permissions')
            ->orderBy('name')
            ->paginate(20);

        return view('tenant-roles.index', compact('roles'));
    }

    public function create()
    {
        abort_unless(auth()->user()?->hasPermission('roles_create'), 403);
        $permissions = Permission::query()->orderBy('name')->get();
        $permissionGroups = TenantRoleService::groupPermissionsForUi($permissions);
        $roleGroups = TenantRoleService::mapPermissionGroupsForView($permissionGroups);

        return view('tenant-roles.create', compact('roleGroups'));
    }

    public function store(Request $request)
    {
        abort_unless(auth()->user()?->hasPermission('roles_create'), 403);
        $validated = $request->validate([
            'name' => [
                'required',
                'string', 'max:100',
                'alpha_dash',
                Rule::notIn(TenantRoleService::RESERVED_ROLE_NAMES),
            ],
            'display_name' => 'required|string|max:191',
            'description' => 'nullable|string|max:2000',
            'permission_ids' => 'nullable|array',
            'permission_ids.*' => 'integer|exists:permissions,id',
        ]);
        if (TenantRoleService::isReservedRoleName($validated['name'])) {
            return back()->withErrors(['name' => 'This role name is reserved.'])->withInput();
        }
        $companyId = (int) auth()->user()->company_id;
        if (! $companyId) {
            abort(403);
        }
        $role = Role::query()->create([
            'company_id' => $companyId,
            'name' => strtolower($validated['name']),
            'display_name' => $validated['display_name'],
            'description' => $validated['description'] ?? null,
        ]);
        $role->permissions()->sync($validated['permission_ids'] ?? []);
        $this->audit->log('role.created', $role, ['permission_ids' => $validated['permission_ids'] ?? []]);

        return redirect()->route('tenant-roles.index')->with('success', 'Role created.');
    }

    public function edit(Role $role)
    {
        $this->assertTenantRole($role);
        abort_unless(auth()->user()?->hasPermission('roles_edit'), 403);
        $allPermissions = Permission::query()->orderBy('name')->get();
        $role->load('permissions');
        $isSystem = $this->isSystemRole($role);
        $permissionGroups = $isSystem
            ? TenantRoleService::groupPermissionsForUi($role->permissions)
            : TenantRoleService::groupPermissionsForUi($allPermissions);
        $roleGroups = TenantRoleService::mapPermissionGroupsForView($permissionGroups);

        return view('tenant-roles.edit', compact('role', 'isSystem', 'roleGroups'));
    }

    public function update(Request $request, Role $role)
    {
        $this->assertTenantRole($role);
        abort_unless(auth()->user()?->hasPermission('roles_edit'), 403);
        $isSystem = $this->isSystemRole($role);

        if ($isSystem) {
            $validated = $request->validate([
                'display_name' => 'required|string|max:191',
                'description' => 'nullable|string|max:2000',
            ]);
            $role->update([
                'display_name' => $validated['display_name'],
                'description' => $validated['description'] ?? null,
            ]);

            return redirect()->route('tenant-roles.index')->with('success', 'Role label updated. System role permissions are managed when running hrms:reset-permissions.');
        }

        $validated = $request->validate([
            'display_name' => 'required|string|max:191',
            'description' => 'nullable|string|max:2000',
            'permission_ids' => 'nullable|array',
            'permission_ids.*' => 'integer|exists:permissions,id',
        ]);
        $role->update([
            'display_name' => $validated['display_name'],
            'description' => $validated['description'] ?? null,
        ]);
        $role->permissions()->sync($validated['permission_ids'] ?? []);
        $this->audit->log('role.updated', $role, ['permission_ids' => $validated['permission_ids'] ?? []]);

        return redirect()->route('tenant-roles.index')->with('success', 'Role updated.');
    }

    public function destroy(Role $role)
    {
        $this->assertTenantRole($role);
        abort_unless(auth()->user()?->hasPermission('roles_delete'), 403);
        if ($this->isSystemRole($role)) {
            return redirect()->route('tenant-roles.index')->with('error', 'System roles cannot be deleted.');
        }
        if (User::query()->where('role_id', $role->id)->exists()) {
            return redirect()->route('tenant-roles.index')->with('error', 'Cannot delete a role that is still assigned to users.');
        }
        $role->delete();
        $this->audit->log('role.deleted', null, ['role_id' => $role->id, 'role_name' => $role->name]);

        return redirect()->route('tenant-roles.index')->with('success', 'Role deleted.');
    }

    private function authorizeView(): void
    {
        abort_unless(auth()->user()?->hasPermission('roles_view'), 403);
    }

    private function assertTenantRole(Role $role): void
    {
        if ((int) $role->company_id !== (int) (auth()->user()?->company_id ?? 0)) {
            abort(404);
        }
    }

    private function isSystemRole(Role $role): bool
    {
        return TenantRoleService::isReservedRoleName((string) $role->name);
    }
}
