<?php

namespace App\Services;

use App\Enums\MemberStatusEnum;
use App\Models\Member;
use App\Models\OtpCode;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Validation\ValidationException;

class MemberAuthService extends BaseService
{
    private const OTP_LIFETIME_MINUTES = 5;
    private const RESEND_THROTTLE_MINUTES = 5;
    private const MAX_VERIFY_ATTEMPTS = 5;

    /**
     * Send OTP to member phone.
     */
    public function sendOtp(string $phone): array
    {
        $phone = $this->normalizePhone($phone);

        $member = Member::where('phone', $phone)->first();

        if (! $member) {
            throw ValidationException::withMessages([
                'phone' => [__('Account not found.')],
            ]);
        }

        // التأكد من الـ throttle
        $this->ensureNotThrottled($phone);

        $startedAt = now();
        $expiresAt = $startedAt->copy()->addMinutes(self::OTP_LIFETIME_MINUTES);

        DB::transaction(function () use ($phone, $expiresAt) {

            // حذف أي OTP سابق
            OtpCode::where('phone', $phone)
                ->where('type', 'login')
                ->delete();

            // $code = $this->generateOtp();
            $code = '123456';

            OtpCode::create([
                'phone'      => $phone,
                'code'       => $code,
                'type'       => 'login',
                'expires_at' => $expiresAt,
            ]);
        });

        return [
            'message'     => __('Verification code sent successfully.'),
            'starts_at'   => $startedAt->toDateTimeString(),
            'expires_at'  => $expiresAt->toDateTimeString(),
        ];
    }

    /**
     * Verify OTP and generate token for member.
     */
    public function verifyOtp(string $phone, string $code, string $deviceName = 'api-token'): array
    {
        $phone = $this->normalizePhone($phone);

        $member = Member::where('phone', $phone)->first();

        if (! $member) {
            throw ValidationException::withMessages([
                'phone' => [__('Account not found.')],
            ]);
        }

        $this->ensureMemberIsActive($member);

        $rateKey = 'verify-otp:' . $phone;

        if (RateLimiter::tooManyAttempts($rateKey, self::MAX_VERIFY_ATTEMPTS)) {
            throw ValidationException::withMessages([
                'code' => [__('Too many attempts. Try again later.')],
            ]);
        }

        $otp = OtpCode::where('phone', $phone)
            ->where('type', 'login')
            ->latest()
            ->first();

        if (! $otp || $code !== $otp->code) {
            RateLimiter::hit($rateKey, 60);

            throw ValidationException::withMessages([
                'code' => [__('Invalid verification code.')],
            ]);
        }

        if ($otp->verified_at) {
            throw ValidationException::withMessages([
                'code' => [__('Code already used.')],
            ]);
        }

        if ($otp->expires_at->isPast()) {
            throw ValidationException::withMessages([
                'code' => [__('Verification code has expired.')],
            ]);
        }

        return DB::transaction(function () use ($otp, $member, $deviceName, $rateKey) {

            $otp->update(['verified_at' => now()]);

            RateLimiter::clear($rateKey);

            // حذف التوكنات السابقة
            $member->tokens()->delete();

            return [
                'user'  => new \App\Http\Resources\Api\MemberResource($member),
                'token' => $member->createToken($deviceName)->plainTextToken,
            ];
        });
    }

    /**
     * Prevent OTP resend within throttle period.
     */
    private function ensureNotThrottled(string $phone): void
    {
        $latestOtp = OtpCode::where('phone', $phone)
            ->where('type', 'login')
            ->latest()
            ->first();

        if ($latestOtp && $latestOtp->created_at->gt(now()->subMinutes(self::RESEND_THROTTLE_MINUTES))) {
            $startedAt = $latestOtp->created_at->copy();
            $endAt = $startedAt->copy()->addMinutes(self::RESEND_THROTTLE_MINUTES);

            throw ValidationException::withMessages([
                'phone' => [__('Please wait until :end_date before requesting a new code. Start: :start_date', [
                    'start_date' => $startedAt->toDateTimeString(),
                    'end_date'   => $endAt->toDateTimeString(),
                ])],
            ]);
        }
    }

    /**
     * Ensure the member account is active.
     */
    private function ensureMemberIsActive(Member $member): void
    {
        if ($member->status === MemberStatusEnum::SUSPENDED->value) {
            throw ValidationException::withMessages([
                'phone' => [__('Your account is suspended.')],
            ]);
        }

        if ($member->status === MemberStatusEnum::EXPIRED->value) {
            throw ValidationException::withMessages([
                'phone' => [__('Your membership has expired.')],
            ]);
        }
    }

    /**
     * Generate a 6-digit OTP.
     */
    private function generateOtp(): string
    {
        return str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
    }

    /**
     * Normalize phone numbers.
     */
    private function normalizePhone(string $phone): string
    {
        return preg_replace('/[^0-9+]/', '', $phone);
    }
}
