<?php

declare(strict_types=1);

namespace App\Http\Requests\Auth;

use App\Models\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password;
use Illuminate\Validation\Validator;

/**
 * Validates a user changing their own password.
 *
 * The current password is required for a voluntary change, and skipped when the
 * change is forced: a forced change means a master has just issued a temporary
 * password, and asking the user to re-enter the value the master chose adds
 * friction without adding security — they signed in with it moments ago.
 */
class ChangePasswordRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user() instanceof User;
    }

    /**
     * @return array<string, array<int, mixed>>
     */
    public function rules(): array
    {
        $rules = [
            'password' => ['required', 'string', 'confirmed', Password::defaults()],
        ];

        if (! $this->isForced()) {
            $rules['current_password'] = ['required', 'string', 'current_password'];
        }

        return $rules;
    }

    /**
     * @return array<string, string>
     */
    public function messages(): array
    {
        return [
            'current_password.current_password' => __('The current password you entered is incorrect.'),
            'password.confirmed' => __('The new password and its confirmation do not match.'),
        ];
    }

    /**
     * @return array<string, string>
     */
    public function attributes(): array
    {
        return [
            'password' => 'new password',
        ];
    }

    public function withValidator(Validator $validator): void
    {
        $validator->after(function (Validator $validator): void {
            /** @var User $user */
            $user = $this->user();

            $new = (string) $this->string('password');

            if ($new !== '' && Hash::check($new, $user->password)) {
                $validator->errors()->add(
                    'password',
                    __('The new password must be different from your current password.'),
                );
            }
        });
    }

    /**
     * Whether this change is being forced after a master-initiated reset.
     */
    public function isForced(): bool
    {
        $user = $this->user();

        return $user instanceof User && $user->must_change_password;
    }
}
