<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Branch;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use App\Services\Audit\AuditLogger;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

final class UserController extends Controller
{
    public function __construct(private readonly AuditLogger $audit) {}

    public function index(Request $request): View
    {
        $this->allow($request, 'users.view');
        $users = User::query()->with(['branch', 'roles'])
            ->where('company_id', $request->user()->company_id)
            ->when($request->filled('search'), function ($query) use ($request): void {
                $search = trim((string) $request->query('search'));
                $query->where(fn ($query) => $query->where('name', 'like', "%{$search}%")->orWhere('email', 'like', "%{$search}%"));
            })->orderBy('name')->paginate(25)->withQueryString();
        $roles = Role::query()->withCount('users')->where('company_id', $request->user()->company_id)->orderBy('name')->get();

        return view('admin.users.index', compact('users', 'roles'));
    }

    public function create(Request $request): View
    {
        $this->allow($request, 'users.create');
        return $this->form($request, new User());
    }

    public function store(Request $request): RedirectResponse
    {
        $this->allow($request, 'users.create');
        $data = $this->validateUser($request);
        $roleIds = $data['role_ids']; unset($data['role_ids']);
        $data['company_id'] = $request->user()->company_id;
        $data['is_active'] = $request->boolean('is_active');

        $user = DB::transaction(function () use ($data, $roleIds): User {
            $user = User::query()->create($data);
            $user->roles()->sync($roleIds);
            return $user;
        });
        $this->audit->log('created', 'users', $user, [], $user->only(['name','email','branch_id','is_active']));

        return redirect()->route('users.index')->with('success', 'User created successfully.');
    }

    public function edit(Request $request, User $user): View
    {
        $this->ensureCompany($request, $user);
        $this->allow($request, 'users.edit_draft');
        return $this->form($request, $user->load('roles'));
    }

    public function update(Request $request, User $user): RedirectResponse
    {
        $this->ensureCompany($request, $user);
        $this->allow($request, 'users.edit_draft');
        $data = $this->validateUser($request, $user);
        $roleIds = $data['role_ids']; unset($data['role_ids']);
        $data['is_active'] = $request->boolean('is_active');
        if (empty($data['password'])) unset($data['password']);
        if ($user->is($request->user())) $data['is_active'] = true;
        $old = $user->only(['name','email','branch_id','is_active']);

        DB::transaction(function () use ($user, $data, $roleIds): void {
            $user->update($data);
            $user->roles()->sync($roleIds);
        });
        $this->audit->log('updated', 'users', $user, $old, $user->only(['name','email','branch_id','is_active']));

        return redirect()->route('users.index')->with('success', 'User updated successfully.');
    }

    public function destroy(Request $request, User $user): RedirectResponse
    {
        $this->ensureCompany($request, $user);
        $this->allow($request, 'users.delete_draft');
        if ($user->is($request->user())) return back()->with('error', 'You cannot delete your own account.');
        if ($user->isSuperAdministrator() && User::query()->where('company_id', $user->company_id)->whereHas('roles', fn ($q) => $q->where('slug', 'super-administrator'))->count() <= 1) {
            return back()->with('error', 'The last super administrator cannot be deleted.');
        }
        $this->audit->log('deleted', 'users', $user, $user->only(['name','email']), []);
        $user->delete();
        return back()->with('success', 'User deleted.');
    }

    public function createRole(Request $request): View
    {
        $this->allow($request, 'roles.create');
        return $this->roleForm($request, new Role());
    }

    public function storeRole(Request $request): RedirectResponse
    {
        $this->allow($request, 'roles.create');
        $data = $this->validateRole($request);
        $permissionIds = $data['permission_ids'] ?? []; unset($data['permission_ids']);
        $data['company_id'] = $request->user()->company_id;
        $data['is_system'] = false;
        $role = DB::transaction(function () use ($data, $permissionIds): Role {
            $role = Role::query()->create($data);
            $role->permissions()->sync($permissionIds);
            return $role;
        });
        $this->audit->log('created', 'roles', $role, [], ['name'=>$role->name,'permissions'=>$permissionIds]);
        return redirect()->route('users.index')->with('success', 'Role created.');
    }

    public function editRole(Request $request, Role $role): View
    {
        $this->ensureRoleCompany($request, $role);
        $this->allow($request, 'roles.edit_draft');
        return $this->roleForm($request, $role->load('permissions'));
    }

    public function updateRole(Request $request, Role $role): RedirectResponse
    {
        $this->ensureRoleCompany($request, $role);
        $this->allow($request, 'roles.edit_draft');
        $data = $this->validateRole($request, $role);
        $permissionIds = $data['permission_ids'] ?? []; unset($data['permission_ids']);
        $old = ['name'=>$role->name,'permissions'=>$role->permissions()->pluck('permissions.id')->all()];
        if ($role->is_system) unset($data['slug']);
        DB::transaction(function () use ($role, $data, $permissionIds): void {
            $role->update($data);
            $role->permissions()->sync($permissionIds);
        });
        $this->audit->log('updated', 'roles', $role, $old, ['name'=>$role->name,'permissions'=>$permissionIds]);
        return redirect()->route('users.index')->with('success', 'Role updated.');
    }

    public function destroyRole(Request $request, Role $role): RedirectResponse
    {
        $this->ensureRoleCompany($request, $role);
        $this->allow($request, 'roles.delete_draft');
        if ($role->is_system || $role->users()->exists()) return back()->with('error', 'System roles or roles assigned to users cannot be deleted.');
        $this->audit->log('deleted', 'roles', $role, ['name'=>$role->name], []);
        $role->delete();
        return back()->with('success', 'Role deleted.');
    }

    private function form(Request $request, User $user): View
    {
        $branches = Branch::query()->where('company_id', $request->user()->company_id)->where('is_active', true)->orderBy('code')->get();
        $roles = Role::query()->where('company_id', $request->user()->company_id)->orderBy('name')->get();
        return view('admin.users.form', compact('user', 'branches', 'roles'));
    }

    private function roleForm(Request $request, Role $role): View
    {
        $permissions = Permission::query()->orderBy('module')->orderBy('action')->get()->groupBy('module');
        return view('admin.roles.form', compact('role', 'permissions'));
    }

    private function validateUser(Request $request, ?User $user = null): array
    {
        return $request->validate([
            'name' => ['required','string','max:150'],
            'email' => ['required','email','max:190',Rule::unique('users','email')->ignore($user?->id)],
            'branch_id' => ['nullable','integer',Rule::exists('branches','id')->where('company_id', $request->user()->company_id)],
            'password' => [$user?->exists ? 'nullable' : 'required','string','min:10','confirmed'],
            'role_ids' => ['required','array','min:1'],
            'role_ids.*' => ['integer',Rule::exists('roles','id')->where('company_id', $request->user()->company_id)],
            'is_active' => ['nullable','boolean'],
        ]);
    }

    private function validateRole(Request $request, ?Role $role = null): array
    {
        return $request->validate([
            'name'=>['required','string','max:150'],
            'slug'=>['required','alpha_dash','max:150',Rule::unique('roles','slug')->where('company_id',$request->user()->company_id)->ignore($role?->id)],
            'permission_ids'=>['nullable','array'],
            'permission_ids.*'=>['integer','exists:permissions,id'],
        ]);
    }

    private function allow(Request $request, string $permission): void { abort_unless($request->user()->hasPermission($permission), 403); }
    private function ensureCompany(Request $request, User $user): void { abort_unless((int)$user->company_id === (int)$request->user()->company_id, 404); }
    private function ensureRoleCompany(Request $request, Role $role): void { abort_unless((int)$role->company_id === (int)$request->user()->company_id, 404); }
}
