<?php

namespace Modules\Auth\Services;

use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
use Modules\Auth\Actions\Fortify\PasswordValidationRules;
use Modules\Auth\Emails\PasswordResetOtpEmail;
use Modules\Auth\Entities\PasswordResetOtp;
use Modules\Auth\Entities\User;

class PasswordResetOtpService
{
    use PasswordValidationRules;

    public function send(User $user): void
    {
        $otp = $this->generateOtp();

        PasswordResetOtp::query()
            ->where('user_id', $user->id)
            ->delete();

        PasswordResetOtp::create([
            'user_id' => $user->id,
            'code' => Hash::make($otp),
            'expires_at' => now()->addMinutes($this->expiryMinutes()),
            'attempts' => 0,
        ]);

        $user->notify(new PasswordResetOtpEmail($otp));
    }

    public function reset(User $user, string $otp, array $input): void
    {
        validator($input, [
            'password' => $this->passwordRules(),
        ])->validate();

        $record = PasswordResetOtp::query()
            ->where('user_id', $user->id)
            ->latest()
            ->first();

        if (! $record) {
            throw ValidationException::withMessages([
                'otp' => ['Kod OTP tidak dijumpai. Sila minta kod baharu.'],
            ]);
        }

        if ($record->isExpired()) {
            $record->delete();

            throw ValidationException::withMessages([
                'otp' => ['Kod OTP telah tamat tempoh. Sila minta kod baharu.'],
            ]);
        }

        if ($record->hasExceededMaxAttempts()) {
            $record->delete();

            throw ValidationException::withMessages([
                'otp' => ['Terlalu banyak percubaan. Sila minta kod baharu.'],
            ]);
        }

        if (! Hash::check($otp, $record->code)) {
            $record->increment('attempts');

            if ($record->fresh()->hasExceededMaxAttempts()) {
                $record->delete();
            }

            throw ValidationException::withMessages([
                'otp' => ['Kod OTP tidak sah.'],
            ]);
        }

        $user->forceFill([
            'password' => Hash::make($input['password']),
        ])->save();

        $user->tokens()->delete();

        PasswordResetOtp::query()
            ->where('user_id', $user->id)
            ->delete();
    }

    protected function generateOtp(): string
    {
        return str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
    }

    protected function expiryMinutes(): int
    {
        return (int) config('auth.password_reset.expiry_minutes', 10);
    }
}
