<?php

namespace Kylin\Payment\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
use Illuminate\Contracts\Container\Container;

/**
 * 支付接口限流服务类
 */
class PaymentRateLimitService
{
    /**
     * 默认限流配置
     */
    const DEFAULT_RATE_LIMIT_CONFIG = [
        'enabled' => true, // 是否启用限流
        'driver' => 'cache', // 限流驱动：cache 或 redis
        'prefix' => 'payment_rate_limit:', // 缓存前缀
        'limits' => [
            'pay' => [
                'max_attempts' => 60, // 每分钟最大请求数
                'decay_minutes' => 1, // 衰减时间（分钟）
            ],
            'query' => [
                'max_attempts' => 120, // 每分钟最大请求数
                'decay_minutes' => 1, // 衰减时间（分钟）
            ],
            'refund' => [
                'max_attempts' => 30, // 每分钟最大请求数
                'decay_minutes' => 1, // 衰减时间（分钟）
            ],
            'close' => [
                'max_attempts' => 60, // 每分钟最大请求数
                'decay_minutes' => 1, // 衰减时间（分钟）
            ],
            'callback' => [
                'max_attempts' => 180, // 每分钟最大请求数
                'decay_minutes' => 1, // 衰减时间（分钟）
            ],
        ],
    ];

    /**
     * 构造函数
     */
    public function __construct(
        protected readonly Container $app,
        protected array $config = []
    ) {
        $this->config = array_merge(self::DEFAULT_RATE_LIMIT_CONFIG, $config);
    }

    /**
     * 检查是否超过限流
     */
    public function check(string $action, string $key, array $options = []): bool
    {
        // 如果未启用限流，直接返回通过
        if (!$this->config['enabled']) {
            return true;
        }

        // 获取限流配置
        $limitConfig = $this->getLimitConfig($action);
        $limitConfig = array_merge($limitConfig, $options);

        // 生成缓存键
        $cacheKey = $this->generateCacheKey($action, $key);

        // 获取当前请求次数
        $attempts = $this->getAttempts($cacheKey);

        // 检查是否超过限流
        if ($attempts >= $limitConfig['max_attempts']) {
            return false;
        }

        // 增加请求次数
        $this->incrementAttempts($cacheKey, $limitConfig['decay_minutes']);

        return true;
    }

    /**
     * 检查并抛出异常
     */
    public function checkOrFail(string $action, string $key, array $options = []): void
    {
        if (!$this->check($action, $key, $options)) {
            $limitConfig = $this->getLimitConfig($action);
            $limitConfig = array_merge($limitConfig, $options);

            throw new \Exception(sprintf(
                'Rate limit exceeded. Maximum %d requests per %d minutes.',
                $limitConfig['max_attempts'],
                $limitConfig['decay_minutes']
            ));
        }
    }

    /**
     * 获取当前请求次数
     */
    protected function getAttempts(string $cacheKey): int
    {
        if ($this->config['driver'] === 'redis' && class_exists('Illuminate\Support\Facades\Redis')) {
            return (int) Redis::get($cacheKey) ?: 0;
        } else {
            return (int) Cache::get($cacheKey) ?: 0;
        }
    }

    /**
     * 增加请求次数
     */
    protected function incrementAttempts(string $cacheKey, int $decayMinutes): void
    {
        if ($this->config['driver'] === 'redis' && class_exists('Illuminate\Support\Facades\Redis')) {
            Redis::pipeline(function ($pipe) use ($cacheKey, $decayMinutes) {
                $pipe->incr($cacheKey);
                $pipe->expire($cacheKey, $decayMinutes * 60);
            });
        } else {
            if (!Cache::has($cacheKey)) {
                Cache::put($cacheKey, 0, $decayMinutes);
            }
            Cache::increment($cacheKey);
        }
    }

    /**
     * 重置请求次数
     */
    public function reset(string $action, string $key): void
    {
        $cacheKey = $this->generateCacheKey($action, $key);

        if ($this->config['driver'] === 'redis' && class_exists('Illuminate\Support\Facades\Redis')) {
            Redis::del($cacheKey);
        } else {
            Cache::forget($cacheKey);
        }
    }

    /**
     * 获取限流配置
     */
    protected function getLimitConfig(string $action): array
    {
        return $this->config['limits'][$action] ?? [
            'max_attempts' => 60,
            'decay_minutes' => 1,
        ];
    }

    /**
     * 生成缓存键
     */
    protected function generateCacheKey(string $action, string $key): string
    {
        return "{$this->config['prefix']}{$action}:{$key}";
    }

    /**
     * 根据IP地址限流
     */
    public function checkByIp(string $action, string $ip, array $options = []): bool
    {
        return $this->check($action, "ip:{$ip}", $options);
    }

    /**
     * 根据用户ID限流
     */
    public function checkByUserId(string $action, int $userId, array $options = []): bool
    {
        return $this->check($action, "user:{$userId}", $options);
    }

    /**
     * 根据订单号限流
     */
    public function checkByOrderNo(string $action, string $orderNo, array $options = []): bool
    {
        return $this->check($action, "order:{$orderNo}", $options);
    }

    /**
     * 获取限流配置
     */
    public function getConfig(): array
    {
        return $this->config;
    }

    /**
     * 设置限流配置
     */
    public function setConfig(array $config): self
    {
        $this->config = array_merge($this->config, $config);
        return $this;
    }

    /**
     * 启用限流
     */
    public function enable(): self
    {
        $this->config['enabled'] = true;
        return $this;
    }

    /**
     * 禁用限流
     */
    public function disable(): self
    {
        $this->config['enabled'] = false;
        return $this;
    }

    /**
     * 获取限流状态
     */
    public function getStatus(string $action, string $key): array
    {
        $cacheKey = $this->generateCacheKey($action, $key);
        $limitConfig = $this->getLimitConfig($action);

        $attempts = $this->getAttempts($cacheKey);

        return [
            'attempts' => $attempts,
            'max_attempts' => $limitConfig['max_attempts'],
            'remaining' => max(0, $limitConfig['max_attempts'] - $attempts),
            'decay_minutes' => $limitConfig['decay_minutes'],
        ];
    }
}
