<?php
namespace App\Services;

use App\Models\Otp;
use App\Notifications\SendOtp;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Hash;
class OtpService
{
    private function maskEmail(?string $email): string
    {
        if (empty($email) || !str_contains($email, '@')) {
            return '';
        }

        [$name, $domain] = explode('@', $email, 2);

        $nameMasked = substr($name, 0, 1) . str_repeat('*', max(strlen($name) - 1, 1));

        $domainMasked = substr($domain, 0, 1)
            . str_repeat('*', max(strlen($domain) - 4, 1))
            . substr($domain, -4);

        return $nameMasked . '@' . $domainMasked;
    }


    public function generateAndSendOtp($email)
    {
        $email = Str::lower(trim($email));
        $key = 'send-otp:' . Str::lower($email);
        $lastOtp = Otp::where('email', $email)->latest()->first();
        if ($lastOtp && $lastOtp->created_at->diffInSeconds(now()) < 30) {
            throw new \Exception('Please wait before requesting a new OTP.');
        }

        if (RateLimiter::tooManyAttempts($key, 5)) {
            $seconds = RateLimiter::availableIn($key);
            Log::warning('OtpService: OTP rate limit hit.', [
                'email' => $email,
                'retry_in' => $seconds,
            ]);

            throw new \Exception("Too many OTP requests. Try again in {$seconds} seconds.");
        }

        RateLimiter::hit($key, 60);
        try {
            $otp = random_int(100000, 999999);
            Log::info('OtpService: Generated OTP.', ['email' => $this->maskEmail($email)]);
            DB::transaction(function () use ($email, $otp) {
                Otp::where('email', $email)->delete();
                Otp::create([
                    'email' => $email,
                    'otp' => Hash::make($otp),
                    'expires_at' => now()->addMinutes(10),
                ]);
            });

            Notification::route('mail', $email)->notify(new SendOtp($otp));
            Log::info('OtpService: OTP notification sent.', ['email' => $email]);
        } catch (\Exception $e) {
            Log::error('OtpService: Error generating or sending OTP.', [
                'email' => $email,
                'error' => $e->getMessage(),
            ]);
            throw $e;
        }
    }

    public function verifyOtp($email, $otp)
    {
        $email = Str::lower(trim($email));

        $key = 'verify-otp:' . Str::lower($email);

        if (RateLimiter::tooManyAttempts($key, 5)) {
            $seconds = RateLimiter::availableIn($key);
            throw new \Exception("Too many attempts. Try again in {$seconds} seconds.");
        }

        RateLimiter::hit($key, 60);

        $otpRecord = Otp::where('email', $email)
            ->where('expires_at', '>', now())
            ->first();

        if (!$otpRecord || !Hash::check($otp, $otpRecord->otp)) {
            throw new \Exception('Invalid or expired OTP.');
        }

        $otpRecord->delete();
        RateLimiter::clear($key);

        return true;
    }

}