<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class RateLimit extends Model
{
    use HasFactory;

    protected $fillable = [
        'key',
        'endpoint',
        'limit',
        'window_seconds',
        'current_count',
        'last_reset',
        'blocked_until',
        'created_at',
        'updated_at',
    ];

    protected $casts = [
        'limit' => 'integer',
        'window_seconds' => 'integer',
        'current_count' => 'integer',
        'last_reset' => 'datetime',
        'blocked_until' => 'datetime',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    // Predefined rate limit configurations
    const CONFIG_USER_ORDERS = [
        'limit' => 100,
        'window_seconds' => 3600, // 1 hour
        'key_prefix' => 'user_orders'
    ];

    const CONFIG_USER_WITHDRAWALS = [
        'limit' => 10,
        'window_seconds' => 3600, // 1 hour
        'key_prefix' => 'user_withdrawals'
    ];

    const CONFIG_IP_REQUESTS = [
        'limit' => 1000,
        'window_seconds' => 3600, // 1 hour
        'key_prefix' => 'ip_requests'
    ];

    const CONFIG_API_KEY_ORDERS = [
        'limit' => 1000,
        'window_seconds' => 3600, // 1 hour
        'key_prefix' => 'api_key_orders'
    ];

    /**
     * Get rate limit by key
     */
    public static function getByKey(string $key): ?self
    {
        return self::where('key', $key)->first();
    }

    /**
     * Check if a request is allowed
     */
    public static function isAllowed(string $key, array $config = null): bool
    {
        $config = $config ?? self::CONFIG_IP_REQUESTS;
        $rateLimit = self::getOrCreate($key, $config);
        
        // Check if currently blocked
        if ($rateLimit->blocked_until && $rateLimit->blocked_until->isFuture()) {
            return false;
        }

        // Reset counter if window has expired
        if ($rateLimit->last_reset->addSeconds($rateLimit->window_seconds)->isPast()) {
            $rateLimit->reset();
        }

        // Check if limit exceeded
        if ($rateLimit->current_count >= $rateLimit->limit) {
            $rateLimit->block();
            return false;
        }

        // Increment counter
        $rateLimit->increment('current_count');
        $rateLimit->touch();

        return true;
    }

    /**
     * Get or create rate limit record
     */
    public static function getOrCreate(string $key, array $config): self
    {
        $existing = self::getByKey($key);
        
        if ($existing) {
            return $existing;
        }

        return self::create([
            'key' => $key,
            'endpoint' => $config['key_prefix'] ?? 'default',
            'limit' => $config['limit'] ?? 1000,
            'window_seconds' => $config['window_seconds'] ?? 3600,
            'current_count' => 0,
            'last_reset' => now(),
        ]);
    }

    /**
     * Reset the rate limit counter
     */
    public function reset(): void
    {
        $this->update([
            'current_count' => 0,
            'last_reset' => now(),
            'blocked_until' => null
        ]);
    }

    /**
     * Block the rate limit
     */
    public function block(int $durationMinutes = 60): void
    {
        $this->update([
            'blocked_until' => now()->addMinutes($durationMinutes)
        ]);
    }

    /**
     * Unblock the rate limit
     */
    public function unblock(): void
    {
        $this->update([
            'blocked_until' => null
        ]);
    }

    /**
     * Get remaining requests
     */
    public function getRemaining(): int
    {
        return max(0, $this->limit - $this->current_count);
    }

    /**
     * Get reset time
     */
    public function getResetTime(): \Carbon\Carbon
    {
        return $this->last_reset->addSeconds($this->window_seconds);
    }

    /**
     * Check if rate limit is currently blocked
     */
    public function isBlocked(): bool
    {
        return $this->blocked_until && $this->blocked_until->isFuture();
    }

    /**
     * Get rate limit statistics
     */
    public function getStats(): array
    {
        return [
            'key' => $this->key,
            'limit' => $this->limit,
            'current_count' => $this->current_count,
            'remaining' => $this->getRemaining(),
            'reset_time' => $this->getResetTime(),
            'is_blocked' => $this->isBlocked(),
            'blocked_until' => $this->blocked_until,
            'window_seconds' => $this->window_seconds,
        ];
    }

    /**
     * Scope for active (not blocked) rate limits
     */
    public function scopeActive($query)
    {
        return $query->where(function ($q) {
            $q->whereNull('blocked_until')
              ->orWhere('blocked_until', '<=', now());
        });
    }

    /**
     * Scope for blocked rate limits
     */
    public function scopeBlocked($query)
    {
        return $query->where('blocked_until', '>', now());
    }

    /**
     * Clean up expired rate limit records
     */
    public static function cleanupExpired(): int
    {
        return self::where('last_reset', '<', now()->subDays(7))
            ->where('current_count', 0)
            ->whereNull('blocked_until')
            ->delete();
    }
}