<?php

namespace App\Http\Controllers\Settings;

use App\Http\Controllers\Controller;
use App\Models\Branch;
use App\Models\Dealer;
use App\Models\Department;
use App\Models\User;
use App\Services\UserDepartmentResolver;
use App\Services\RoleDepartmentResolver;
use App\Support\DealerContext;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Spatie\Permission\PermissionRegistrar;

class UserController extends Controller
{
    public function index(Request $request): View
    {
        $currentUser = $request->user();

        $query = User::query()
            ->with(['department', 'branch', 'dealer', 'roles'])
            ->orderBy('name')
            ->orderBy('surname');

        if (!$currentUser->hasSystemAdminRole()) {
            $query->where('dealer_id', $currentUser->dealer_id);
        }

        if ($search = $request->string('q')->trim()->toString()) {
            $query->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('surname', 'like', "%{$search}%")
                    ->orWhere('email', 'like', "%{$search}%");
            });
        }

        if ($status = $request->string('status')->trim()->toString()) {
            if ($status === 'disabled') {
                $query->where('is_disabled', true);
            } elseif ($status === 'active') {
                $query->where('is_disabled', false);
            }
        }

        $users = $query->paginate(20)->withQueryString();

        $deletedQuery = User::onlyTrashed()
            ->with(['department', 'branch', 'dealer', 'roles'])
            ->orderBy('name')
            ->orderBy('surname');

        if (!$currentUser->hasSystemAdminRole()) {
            $deletedQuery->where('dealer_id', $currentUser->dealer_id);
        }

        $deletedUsers = $deletedQuery->get();

        $departments = Department::query()
            ->when(!$currentUser->hasSystemAdminRole(), function ($q) use ($currentUser) {
                $q->where('dealer_id', $currentUser->dealer_id);
            })
            ->orderBy('name')
            ->get();

        $branches = Branch::query()
            ->when(!$currentUser->hasSystemAdminRole(), function ($q) use ($currentUser) {
                $q->where('dealer_id', $currentUser->dealer_id);
            })
            ->orderBy('name')
            ->get();

        $roles = config('mtt_roles', []);

        return view('settings.users.index', compact('users', 'departments', 'branches', 'roles', 'deletedUsers'));
    }

    public function create(Request $request): View
    {
        $currentUser = $request->user();

        $departments = Department::query()
            ->when(!$currentUser->hasSystemAdminRole(), function ($q) use ($currentUser) {
                $q->where('dealer_id', $currentUser->dealer_id);
            })
            ->orderBy('name')
            ->get();

        $branches = Branch::query()
            ->when(!$currentUser->hasSystemAdminRole(), function ($q) use ($currentUser) {
                $q->where('dealer_id', $currentUser->dealer_id);
            })
            ->orderBy('name')
            ->get();

        $defaultBranchId = $branches->firstWhere('is_default', true)?->id;

        $roles = $this->rolesAllowedForCurrentDealer($request);

        return view('settings.users.create', compact('departments', 'branches', 'defaultBranchId', 'roles'));
    }

    public function store(Request $request, RoleDepartmentResolver $resolver, UserDepartmentResolver $userDepartmentResolver): RedirectResponse
    {
        $currentUser = $request->user();
        $dealerId = $currentUser->dealer_id;

        if (in_array('System Admin', (array) $request->input('roles', []), true) && ! $this->currentDealerIsDefault($request)) {
            return back()
                ->withErrors(['roles' => 'The System Admin role cannot be assigned for this dealer.'])
                ->withInput($request->except('password'));
        }

        $allowedRoleNames = $this->rolesAllowedForCurrentDealer($request);

        $data = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'surname' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', 'unique:users,email'],
            'phone' => ['nullable', 'string', 'max:50'],
            'department_id' => [
                'required',
                'integer',
                Rule::exists('departments', 'id')->where(static function ($q) use ($dealerId) {
                    $q->where('dealer_id', $dealerId);
                }),
            ],
            'branch_id' => [
                'required',
                'integer',
                Rule::exists('branches', 'id')->where(static function ($q) use ($dealerId) {
                    $q->where('dealer_id', $dealerId);
                }),
            ],
            'roles' => ['required', 'array', 'min:1'],
            'roles.*' => ['string', Rule::in($allowedRoleNames)],
        ]);

        $tempPassword = Str::password(16);

        $roles = $data['roles'] ?? [];
        $resolvedDepartmentId = $resolver->resolveDepartmentIdForRoles($roles, $dealerId);

        $user = new User();
        $user->dealer_id = $dealerId;
        $user->department_id = $resolvedDepartmentId ?? ($data['department_id'] ?? null);
        $user->branch_id = $data['branch_id'];
        $user->name = $data['name'];
        $user->surname = $data['surname'];
        $user->email = $data['email'];
        $user->phone = $data['phone'] ?? null;
        $user->password = Hash::make($tempPassword);
        $user->force_password_change = true;
        $user->is_disabled = false;
        $user->save();
        if (!empty($roles)) {
            /** @var PermissionRegistrar $registrar */
            $registrar = app(PermissionRegistrar::class);
            $registrar->setPermissionsTeamId($user->dealer_id);
            $user->syncRoles($roles);
            $registrar->setPermissionsTeamId(null);
            $registrar->forgetCachedPermissions();
        }

        // Ensure department is aligned with roles using tenant baseline mapping.
        $userDepartmentResolver->enforce($user->fresh());

        return redirect()
            ->route('settings.users.index')
            ->with('status', 'User created. Temporary password: '.$tempPassword);
    }

    public function edit(Request $request, int $user): View
    {
        $currentUser = $request->user();

        $userModel = $this->findScopedUser($currentUser, $user);

        $departments = Department::query()
            ->when(!$currentUser->hasSystemAdminRole(), function ($q) use ($currentUser) {
                $q->where('dealer_id', $currentUser->dealer_id);
            })
            ->orderBy('name')
            ->get();

        $branches = Branch::query()
            ->when(!$currentUser->hasSystemAdminRole(), function ($q) use ($currentUser) {
                $q->where('dealer_id', $currentUser->dealer_id);
            })
            ->orderBy('name')
            ->get();

        $roles = $this->rolesAllowedForCurrentDealer($request);

        return view('settings.users.edit', [
            'managedUser' => $userModel,
            'departments' => $departments,
            'branches' => $branches,
            'roles' => $roles,
        ]);
    }

    public function update(Request $request, int $user, RoleDepartmentResolver $resolver, UserDepartmentResolver $userDepartmentResolver): RedirectResponse
    {
        $currentUser = $request->user();

        $userModel = $this->findScopedUser($currentUser, $user);

        if (in_array('System Admin', (array) $request->input('roles', []), true) && ! $this->currentDealerIsDefault($request)) {
            return back()
                ->withErrors(['roles' => 'The System Admin role cannot be assigned for this dealer.'])
                ->withInput($request->except('password'));
        }

        $allowedRoleNames = $this->rolesAllowedForCurrentDealer($request);

        $data = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'surname' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', 'unique:users,email,'.$userModel->id],
            'phone' => ['nullable', 'string', 'max:50'],
            'department_id' => [
                'required',
                'integer',
                Rule::exists('departments', 'id')->where(static function ($q) use ($userModel) {
                    $q->where('dealer_id', $userModel->dealer_id);
                }),
            ],
            'branch_id' => [
                'required',
                'integer',
                Rule::exists('branches', 'id')->where(static function ($q) use ($userModel) {
                    $q->where('dealer_id', $userModel->dealer_id);
                }),
            ],
            'roles' => ['required', 'array', 'min:1'],
            'roles.*' => ['string', Rule::in($allowedRoleNames)],
        ]);

        if ($userModel->is_builtin) {
            // Prevent changing email for built-in users to keep login identity stable.
            if (isset($data['email']) && strtolower($data['email']) !== strtolower((string) $userModel->email)) {
                return back()
                    ->withErrors(['email' => 'Built-in user email cannot be changed.'])
                    ->withInput($request->except('email'));
            }
        }

        $roles = $data['roles'] ?? [];
        $resolvedDepartmentId = $resolver->resolveDepartmentIdForRoles($roles, $userModel->dealer_id);

        $userModel->name = $data['name'];
        $userModel->surname = $data['surname'];
        $userModel->email = $data['email'];
        $userModel->phone = $data['phone'] ?? null;
        $userModel->department_id = $resolvedDepartmentId ?? ($data['department_id'] ?? null);
        $userModel->branch_id = $data['branch_id'];
        $userModel->save();

        if (!empty($roles)) {
            /** @var PermissionRegistrar $registrar */
            $registrar = app(PermissionRegistrar::class);
            $registrar->setPermissionsTeamId($userModel->dealer_id);
            $userModel->syncRoles($roles);
            $registrar->setPermissionsTeamId(null);
            $registrar->forgetCachedPermissions();
        }

        // Re-align department after any role changes.
        $userDepartmentResolver->enforce($userModel->fresh());

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

    public function toggleDisabled(Request $request, int $user): RedirectResponse
    {
        $currentUser = $request->user();

        $userModel = $this->findScopedUser($currentUser, $user);

        if ($currentUser->id === $userModel->id) {
            return back()->withErrors(['is_disabled' => 'You cannot disable your own account.']);
        }

        $userModel->is_disabled = !$userModel->is_disabled;

        // When toggling status, always reset failed-login tracking so a re-enabled user
        // does not immediately get disabled again on the next mistake.
        $userModel->failed_login_attempts = 0;
        $userModel->failed_login_first_at = null;
        $userModel->last_failed_login_at = null;

        $userModel->save();

        $message = $userModel->is_disabled
            ? 'User disabled.'
            : 'User enabled and security counters reset.';

        return back()->with('status', $message);
    }

    public function resetPassword(Request $request, int $user): RedirectResponse
    {
        $currentUser = $request->user();

        $userModel = $this->findScopedUser($currentUser, $user);

        if ($currentUser->id === $userModel->id) {
            return back()->withErrors(['password' => 'Use your profile to change your own password.']);
        }

        $tempPassword = Str::password(16);
        $userModel->password = Hash::make($tempPassword);
        $userModel->force_password_change = true;
        $userModel->save();

        return back()->with('status', 'Password reset. Temporary password: '.$tempPassword);
    }

    public function destroy(Request $request, int $user): RedirectResponse
    {
        $currentUser = $request->user();

        $userModel = $this->findScopedUser($currentUser, $user);

        if ($currentUser->id === $userModel->id) {
            return back()->withErrors(['delete' => 'You cannot delete your own account.']);
        }

        if ($userModel->is_builtin) {
            return back()->withErrors(['delete' => 'Built-in users cannot be deleted. Disable the user instead.']);
        }

        $userModel->delete();

        return redirect()
            ->route('settings.users.index')
            ->with('status', 'User deleted.');
    }

    public function restore(Request $request, int $user): RedirectResponse
    {
        $currentUser = $request->user();

        $query = User::withTrashed();

        if (! $currentUser->hasSystemAdminRole()) {
            $query->where('dealer_id', $currentUser->dealer_id);
        }

        $userModel = $query->whereKey($user)->firstOrFail();

        if (! $userModel->trashed()) {
            return back()->with('status', 'User is not deleted.');
        }

        $userModel->restore();
        $userModel->is_disabled = false;
        $userModel->failed_login_attempts = 0;
        $userModel->failed_login_first_at = null;
        $userModel->last_failed_login_at = null;
        $userModel->save();

        return back()->with('status', 'User restored.');
    }

    protected function findScopedUser(User $currentUser, int $id): User
    {
        $query = User::query();

        if (!$currentUser->hasSystemAdminRole()) {
            $query->where('dealer_id', $currentUser->dealer_id);
        }

        return $query->whereKey($id)->firstOrFail();
    }

    /**
     * Roles that the current dealer is allowed to assign (e.g. on user create/edit).
     * Non-default dealers cannot assign the System Admin role.
     */
    protected function rolesAllowedForCurrentDealer(Request $request): array
    {
        $allRoles = config('mtt_roles', []);
        $dealerId = DealerContext::currentDealerId();
        if ($dealerId === null) {
            return $allRoles;
        }
        $dealer = Dealer::query()->select(['id', 'is_default'])->find($dealerId);
        if ($dealer && $dealer->is_default) {
            return $allRoles;
        }
        return array_values(array_filter($allRoles, static fn (string $role): bool => $role !== 'System Admin'));
    }

    protected function currentDealerIsDefault(Request $request): bool
    {
        $dealerId = DealerContext::currentDealerId();
        if ($dealerId === null) {
            return false;
        }
        $dealer = Dealer::query()->select(['id', 'is_default'])->find($dealerId);

        return $dealer && $dealer->is_default;
    }
}

