<?php

namespace App\Http\Controllers\Owner;

use App\Http\Controllers\Controller;
use App\Models\StaffRole;
use App\Models\User;
use App\Support\Authorization\PermissionCatalog;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class RoleController extends Controller
{
    private function permissionModules(): array
    {
        return PermissionCatalog::ownerModules();
    }

    private function normalizePermissions(array $inputPermissions): array
    {
        return PermissionCatalog::normalizeSubmittedPermissions($inputPermissions, PermissionCatalog::PANEL_OWNER);
    }

    private function roleCode(int $organizationId, string $name): string
    {
        return 'ORG' . $organizationId . '_' . Str::upper(Str::of($name)->slug('_'));
    }

    private function membersCount(StaffRole $role, int $organizationId): int
    {
        return (int) DB::table(config('permission.table_names.model_has_roles') . ' as model_has_roles')
            ->join('users', 'users.id', '=', 'model_has_roles.model_id')
            ->where('model_has_roles.role_id', $role->id)
            ->where('model_has_roles.model_type', User::class)
            ->where('model_has_roles.organization_id', $organizationId)
            ->where('users.role', User::ROLE_OWNER_STAFF)
            ->where('users.organization_id', $organizationId)
            ->count();
    }

    public function index(Request $request)
    {
        $owner = $request->user();
        $organizationId = $owner->organization_id;

        if (empty($organizationId)) {
            return view('content.owner.settings.roles.index', [
                'roles' => [],
                'permissionModules' => $this->permissionModules(),
                'permissionActionLabels' => PermissionCatalog::actionLabels(),
                'canManageRoles' => false,
            ]);
        }

        $roles = StaffRole::query()
            ->forOrganization((int) $organizationId)
            ->custom()
            ->where('is_active', true)
            ->orderBy('name')
            ->get()
            ->map(function (StaffRole $role) use ($organizationId) {
                $members = $this->membersCount($role, (int) $organizationId);

                return [
                    'id' => $role->id,
                    'name' => $role->name,
                    'code' => $role->code,
                    'permissions' => $role->permissionCount() . ' permisos',
                    'permissions_map' => $role->permissionsMap(PermissionCatalog::PANEL_OWNER),
                    'members' => $members,
                    'type' => 'OWNER_ROLE',
                    'editable' => true,
                    'deletable' => $members === 0,
                ];
            })
            ->all();

        return view('content.owner.settings.roles.index', [
            'roles' => $roles,
            'permissionModules' => $this->permissionModules(),
            'permissionActionLabels' => PermissionCatalog::actionLabels(),
            'canManageRoles' => true,
        ]);
    }

    public function store(Request $request)
    {
        $owner = $request->user();
        $organizationId = $owner->organization_id;

        if (empty($organizationId)) {
            return redirect()->route('owner.settings.roles')->withErrors([
                'name' => 'Primero debes tener una organización asignada.',
            ]);
        }

        $request->validate([
            'name' => ['required', 'string', 'max:120'],
            'permissions' => ['nullable', 'array'],
        ]);

        $name = $request->input('name');
        $code = $this->roleCode((int) $organizationId, $name);

        if (StaffRole::query()->where('organization_id', $organizationId)->where('code', $code)->exists()) {
            return back()->withErrors([
                'name' => 'Ya existe un rol con ese nombre.',
            ])->withInput();
        }

        StaffRole::create([
            'name' => $name,
            'code' => $code,
            'organization_id' => $organizationId,
            'guard_name' => 'web',
            'is_system' => false,
            'is_active' => true,
        ])->syncPermissions($this->normalizePermissions($request->input('permissions', [])));

        return redirect()->route('owner.settings.roles')->with('status', 'Rol creado correctamente.');
    }

    public function update(Request $request, StaffRole $staffRole)
    {
        $owner = $request->user();
        $organizationId = $owner->organization_id;

        if (
            empty($organizationId)
            || $staffRole->organization_id !== $organizationId
            || $staffRole->is_system
        ) {
            abort(404);
        }

        $request->validate([
            'name' => ['required', 'string', 'max:120'],
            'permissions' => ['nullable', 'array'],
        ]);

        $name = $request->input('name');
        $newCode = $this->roleCode((int) $organizationId, $name);

        if (StaffRole::query()->where('organization_id', $organizationId)->where('code', $newCode)->whereKeyNot($staffRole->id)->exists()) {
            return back()->withErrors([
                'name' => 'Ya existe un rol con ese nombre.',
            ])->withInput();
        }

        $oldCode = $staffRole->code;
        $staffRole->update([
            'name' => $name,
            'code' => $newCode,
        ]);
        $staffRole->syncPermissions($this->normalizePermissions($request->input('permissions', [])));

        if ($oldCode !== $newCode) {
            User::where('role', User::ROLE_OWNER_STAFF)
                ->where('organization_id', $organizationId)
                ->where('staff_role', $oldCode)
                ->update(['staff_role' => $newCode]);
        }

        return redirect()->route('owner.settings.roles')->with('status', 'Rol actualizado correctamente.');
    }

    public function destroy(Request $request, StaffRole $staffRole)
    {
        $owner = $request->user();
        $organizationId = $owner->organization_id;

        if (
            empty($organizationId)
            || $staffRole->organization_id !== $organizationId
            || $staffRole->is_system
        ) {
            abort(404);
        }

        $members = $this->membersCount($staffRole, (int) $organizationId);

        if ($members > 0) {
            return redirect()->route('owner.settings.roles')->withErrors([
                'name' => 'No se puede eliminar el rol porque tiene usuarios asignados.',
            ]);
        }

        $staffRole->delete();

        return redirect()->route('owner.settings.roles')->with('status', 'Rol eliminado correctamente.');
    }
}
