<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Carbon\Carbon;

class RegistrationOtp extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'session_token',
        'phone_number',
        'otp_code',
        'ip_address',
        'user_agent',
        'status', // pending, verified, expired, failed
        'attempts_count',
        'max_attempts',
        'expires_at',
        'verified_at'
    ];

    protected $casts = [
        'expires_at' => 'datetime',
        'verified_at' => 'datetime',
        'attempts_count' => 'integer',
        'max_attempts' => 'integer'
    ];

    protected $hidden = [
        'otp_code' // Hide encrypted OTP from JSON/array output
    ];

    /**
     * Set the OTP code (no encryption)
     */
    public function setOtpCodeAttribute($value)
    {
        $this->attributes['otp_code'] = $value;
    }

    /**
     * Get the OTP code
     */
    public function getOtpCodeAttribute($value)
    {
        return $value;
    }

    /**
     * Check if OTP is expired
     */
    public function isExpired(): bool
    {
        return $this->expires_at->isPast();
    }

    /**
     * Check if OTP is verified
     */
    public function isVerified(): bool
    {
        return $this->status === 'verified';
    }

    /**
     * Check if OTP is pending
     */
    public function isPending(): bool
    {
        return $this->status === 'pending';
    }

    /**
     * Check if maximum attempts exceeded
     */
    public function isMaxAttemptsExceeded(): bool
    {
        return $this->attempts_count >= $this->max_attempts;
    }

    /**
     * Increment attempts count
     */
    public function incrementAttempts(): void
    {
        $this->increment('attempts_count');

        if ($this->isMaxAttemptsExceeded()) {
            $this->update(['status' => 'failed']);
        }
    }

    /**
     * Mark OTP as verified
     */
    public function markAsVerified(): void
    {
        $this->update([
            'status' => 'verified',
            'verified_at' => now()
        ]);
    }

    /**
     * Mark OTP as expired
     */
    public function markAsExpired(): void
    {
        $this->update(['status' => 'expired']);
    }

    /**
     * Verify OTP code
     */
    public function verifyOtp(string $userOtp): bool
    {
        if ($this->isExpired() || $this->isVerified() || $this->isMaxAttemptsExceeded()) {
            return false;
        }

        $this->incrementAttempts();

        if ($this->otp_code === $userOtp) {
            $this->markAsVerified();
            return true;
        }

        return false;
    }

    /**
     * Scope for pending OTPs
     */
    public function scopePending($query)
    {
        return $query->where('status', 'pending');
    }

    /**
     * Scope for verified OTPs
     */
    public function scopeVerified($query)
    {
        return $query->where('status', 'verified');
    }

    /**
     * Scope for expired OTPs
     */
    public function scopeExpired($query)
    {
        return $query->where('status', 'expired');
    }

    /**
     * Scope for failed OTPs
     */
    public function scopeFailed($query)
    {
        return $query->where('status', 'failed');
    }

    /**
     * Scope for active OTPs (not expired)
     */
    public function scopeActive($query)
    {
        return $query->where('expires_at', '>', now());
    }

    /**
     * Scope for expired OTPs by time
     */
    public function scopeExpiredByTime($query)
    {
        return $query->where('expires_at', '<', now());
    }

    /**
     * Get OTP by session token
     */
    public static function findBySessionToken(string $sessionToken): ?self
    {
        return static::where('session_token', $sessionToken)->first();
    }

    /**
     * Get OTP by phone number and session token
     */
    public static function findByPhoneAndSession(string $phoneNumber, string $sessionToken): ?self
    {
        return static::where('phone_number', $phoneNumber)
            ->where('session_token', $sessionToken)
            ->first();
    }

    /**
     * Create new OTP record
     */
    public static function createOtp(
        string $sessionToken,
        string $phoneNumber,
        string $otpCode,
        string $ipAddress = null,
        string $userAgent = null,
        int $expirationMinutes = 5
    ): self {
        return static::create([
            'session_token' => $sessionToken,
            'phone_number' => $phoneNumber,
            'otp_code' => $otpCode,
            'ip_address' => $ipAddress,
            'user_agent' => $userAgent,
            'expires_at' => now()->addMinutes($expirationMinutes),
            'status' => 'pending',
            'attempts_count' => 0,
            'max_attempts' => 3
        ]);
    }

    /**
     * Clean up expired OTPs
     */
    public static function cleanupExpired(): int
    {
        return static::expiredByTime()->delete();
    }

    /**
     * Get rate limit info for phone number
     */
    public static function getRateLimitInfo(string $phoneNumber, int $windowMinutes = 60): array
    {
        $windowStart = now()->subMinutes($windowMinutes);

        // Only count failed and pending attempts, not successful ones
        $recentOtps = static::where('phone_number', $phoneNumber)
            ->where('created_at', '>=', $windowStart)
            ->whereIn('status', ['failed', 'pending'])
            ->count();

        $failedAttempts = static::where('phone_number', $phoneNumber)
            ->where('created_at', '>=', $windowStart)
            ->where('status', 'failed')
            ->count();

        return [
            'total_requests' => $recentOtps,
            'failed_attempts' => $failedAttempts,
            'window_minutes' => $windowMinutes
        ];
    }
}
