<?php

namespace App\Livewire\Modals;

use App\Enums\Role;
use App\Models\AuditEvent;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Password;
use LivewireUI\Modal\ModalComponent;

/**
 * Form modal: add a new account with an RBAC role (admin > operator > viewer; new accounts
 * default to `operator` — least privilege). Restricted to admins (`manage-users`). The creator
 * may set a password directly, or leave it blank — then a strong initial password is generated, the
 * account is flagged `must_change_password`, and the clear-text password is revealed ONCE in the modal
 * (never persisted or logged). An operator-set password is the user's own, so no forced rotation.
 */
class CreateUser extends ModalComponent
{
    public string $name = '';

    public string $email = '';

    /** New account role (admin > operator > viewer). Defaults to operator — least privilege. */
    public string $role = 'operator';

    /** Optional operator-set password; blank = generate an initial password and reveal it once. */
    public string $password = '';

    /** Clear-text temp password, held in component state only to reveal it once. */
    public ?string $tempPassword = null;

    /** Name of the account just created, used in the reveal copy. */
    public string $createdName = '';

    public function mount(): void
    {
        abort_unless(auth()->user()?->can('manage-users'), 403);
    }

    public static function modalMaxWidth(): string
    {
        return 'lg';
    }

    /**
     * @return array<string, array<int, mixed>>
     */
    protected function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:120'],
            'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users', 'email')],
            // Optional — when set it must meet the same strength as a self-chosen password.
            'password' => ['nullable', 'string', Password::min(6)],
            'role' => ['required', Rule::enum(Role::class)],
        ];
    }

    /**
     * @return array<string, string>
     */
    protected function validationAttributes(): array
    {
        return [
            'name' => __('accounts.name_label'),
            'email' => __('accounts.email_label'),
            'password' => __('accounts.password_label'),
            'role' => __('accounts.role_label'),
        ];
    }

    public function save(): void
    {
        abort_unless(auth()->user()?->can('manage-users'), 403);

        $data = $this->validate();

        // Re-check uniqueness server-side regardless of the live validator state.
        $email = mb_strtolower(trim($data['email']));
        if (User::where('email', $email)->exists()) {
            $this->addError('email', __('validation.unique', ['attribute' => __('accounts.email_label')]));

            return;
        }

        // Operator-set password = the user's own login (no forced rotation). Blank = generate a strong
        // initial password that the user is prompted to rotate, and reveal it once below.
        $operatorSet = $this->password !== '';
        $password = $operatorSet ? $this->password : Str::password(16);

        $user = User::create([
            'name' => trim($data['name']),
            'email' => $email,
            'password' => Hash::make($password),
            'must_change_password' => ! $operatorSet,
            'role' => $data['role'],
        ]);

        AuditEvent::create([
            'user_id' => Auth::id(),
            'actor' => Auth::user()?->name ?? 'system',
            'action' => 'user.create',
            'target' => $user->email,
            'ip' => request()->ip(),
        ]);

        // Operator already knows the password they set → no reveal; just confirm and close.
        if ($operatorSet) {
            $this->dispatch('usersChanged');
            $this->dispatch('notify', message: __('accounts.created_notify', ['name' => $user->name]));
            $this->closeModal();

            return;
        }

        // Reveal the generated initial password ONCE — swaps the form for the reveal panel.
        $this->createdName = $user->name;
        $this->tempPassword = $password;
    }

    /** "Fertig": reload the list and close. */
    public function finish(): void
    {
        $this->dispatch('usersChanged');
        $this->closeModal();
    }

    public function render()
    {
        return view('livewire.modals.create-user');
    }
}
