<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Http\Requests\Api\ForgotPasswordRequest;
use App\Http\Requests\Api\ResetPasswordWithOtpRequest;
use App\Http\Requests\Api\VerifyPasswordOtpRequest;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;

class PasswordResetController extends Controller
{
    public function forgot(ForgotPasswordRequest $request): JsonResponse
    {
        $email = strtolower((string) $request->validated('email'));
        $user = User::where('email', $email)->first();

        if ($user) {
            $otp = (string) random_int(100000, 999999);
            Cache::put(
                $this->otpKey($email),
                [
                    'otp_hash' => Hash::make($otp),
                    'attempts' => 0,
                ],
                now()->addMinutes(10)
            );

            Mail::raw(
                "Votre code OTP de recuperation Caddies+ est: {$otp}. Valable 10 minutes.",
                fn ($message) => $message
                    ->to($email)
                    ->subject('Code OTP de recuperation - Caddies+')
            );
        }

        return response()->json([
            'message' => __('Si ce mail existe, un code OTP a été envoyé.'),
        ]);
    }

    public function verifyOtp(VerifyPasswordOtpRequest $request): JsonResponse
    {
        $email = strtolower((string) $request->validated('email'));
        $otp = (string) $request->validated('otp');

        $payload = Cache::get($this->otpKey($email));
        if (! is_array($payload) || ! isset($payload['otp_hash'])) {
            return response()->json(['message' => __('Code OTP expiré ou invalide.')], 422);
        }

        $attempts = (int) ($payload['attempts'] ?? 0);
        if ($attempts >= 5) {
            Cache::forget($this->otpKey($email));

            return response()->json(['message' => __('Trop de tentatives. Veuillez recommencer.')], 429);
        }

        if (! Hash::check($otp, (string) $payload['otp_hash'])) {
            Cache::put($this->otpKey($email), [
                'otp_hash' => $payload['otp_hash'],
                'attempts' => $attempts + 1,
            ], now()->addMinutes(10));

            return response()->json(['message' => __('Code OTP incorrect.')], 422);
        }

        $resetToken = Str::random(64);
        Cache::put($this->resetKey($email, $resetToken), true, now()->addMinutes(15));
        Cache::forget($this->otpKey($email));

        return response()->json([
            'data' => [
                'reset_token' => $resetToken,
            ],
            'message' => __('Code OTP validé.'),
        ]);
    }

    public function reset(ResetPasswordWithOtpRequest $request): JsonResponse
    {
        $email = strtolower((string) $request->validated('email'));
        $resetToken = (string) $request->validated('reset_token');
        $password = (string) $request->validated('password');

        if (! Cache::get($this->resetKey($email, $resetToken))) {
            return response()->json(['message' => __('Session de réinitialisation invalide ou expirée.')], 422);
        }

        $user = User::where('email', $email)->first();
        if (! $user) {
            return response()->json(['message' => __('Utilisateur introuvable.')], 404);
        }

        $user->update([
            'password' => Hash::make($password),
        ]);
        $user->tokens()->delete();

        Cache::forget($this->resetKey($email, $resetToken));

        return response()->json([
            'message' => __('Mot de passe réinitialisé avec succès.'),
        ]);
    }

    private function otpKey(string $email): string
    {
        return 'password_reset:otp:' . $email;
    }

    private function resetKey(string $email, string $token): string
    {
        return 'password_reset:token:' . $email . ':' . $token;
    }
}

