<?php

namespace App\Livewire\Auth;

use App\Models\User;
use App\Support\PasswordPolicy;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;

/**
 * The other end of the link.
 *
 * The token is checked by Laravel's broker, not here: it holds the hash, the
 * expiry and the single-use deletion, and re-implementing any of that is how a
 * reset link ends up working twice.
 *
 * The user is NOT signed in afterwards. A reset link travels by email, and a
 * mailbox somebody else can read would otherwise be a session somebody else
 * gets. They type the new password once more on the sign-in page — which also
 * proves the reset did what they think it did.
 */
#[Layout('layouts.portal')]
class ResetPassword extends Component
{
    public string $token = '';

    #[Validate('required|email|max:255')]
    public string $email = '';

    public string $password = '';

    public string $password_confirmation = '';

    public bool $done = false;

    /**
     * Die Länge kommt aus PasswordPolicy, nicht aus einem Attribut.
     *
     * Ein `#[Validate]`-Attribut ist ein fester String und kann eine
     * Einstellung nicht lesen — genau deshalb stand hier `min:12`, während der
     * Rest der Anwendung acht verlangte.
     *
     * @return array<string, mixed>
     */
    protected function rules(): array
    {
        return ['password' => [...PasswordPolicy::rules(), 'confirmed']];
    }

    public function mount(string $token): void
    {
        $this->token = $token;
        $this->email = (string) request()->query('email', '');
    }

    /**
     * NOT called reset(): Livewire\Component::reset() clears properties, and
     * a component that redeclares it will not load at all.
     */
    public function save(): void
    {
        $this->validate();

        $status = Password::broker()->reset(
            [
                'email' => $this->email,
                'password' => $this->password,
                'password_confirmation' => $this->password_confirmation,
                'token' => $this->token,
            ],
            function (User $user, string $password) {
                $user->forceFill([
                    'password' => Hash::make($password),
                    // Every existing session dies with it. Whoever knew the old
                    // password may still be signed in somewhere, and a reset
                    // that leaves them there has fixed nothing.
                    'remember_token' => Str::random(60),
                ])->save();

                event(new PasswordReset($user));
            },
        );

        if ($status !== Password::PASSWORD_RESET) {
            $this->addError('email', __($status));

            return;
        }

        $this->done = true;
    }

    public function render()
    {
        return view('livewire.auth.reset-password');
    }
}
