import { BizPlugin } from '../core/runtime/PluginManager';
import { RedisService } from '../utils/RedisService';
import { logger } from '../utils/logger';

/**
 * [BIZ_INF_101] Redis 租户级配额热插拔 (RedisQuotaPlugin)
 * @description 核心逻辑：基于 Redis 滑动窗口实现的动态限流插件。
 * 支持按租户 (TenantId) 与 接口/资源 (Resource) 进行细粒度限流。
 * 限流规则可在 Redis 中热更新，无需重启。
 */
export default class RedisQuotaPlugin implements BizPlugin {
  name = 'RedisQuotaPlugin';
  version = '1.0.0';
  description = 'Tenant-level dynamic rate limiting based on Redis sliding window';

  /**
   * 滑动窗口 Lua 脚本
   * ARGV[1]: 当前毫秒时间戳
   * ARGV[2]: 窗口大小 (ms)
   * ARGV[3]: 最大限制数
   */
  private static readonly SLIDING_WINDOW_SCRIPT = `
    local key = KEYS[1]
    local now = tonumber(ARGV[1])
    local window = tonumber(ARGV[2])
    local limit = tonumber(ARGV[3])
    
    -- 1. 移除过期的记录
    redis.call('zremrangebyscore', key, 0, now - window)
    
    -- 2. 获取当前窗口内的请求数
    local current_count = redis.call('zcard', key)
    
    -- 3. 判断是否超过限制
    if current_count < limit then
      redis.call('zadd', key, now, now)
      redis.call('expire', key, math.ceil(window / 1000) + 1)
      return 1 -- Allowed
    else
      return 0 -- Blocked
    end
  `;

  async onLoad(): Promise<void> {
    logger.info(`[${this.name}] Initialized and monitoring quotas.`);
  }

  async onUnload(): Promise<void> {
    logger.info(`[${this.name}] Unloaded.`);
  }

  hooks = {
    /**
     * 网关前置校验钩子
     */
    'gateway:pre_check': async (payload: { tenantId: string, resource: string, limit?: number, windowMs?: number }) => {
      const { tenantId, resource, limit = 100, windowMs = 60000 } = payload;
      
      // 1. 生成 Redis Key: quota:{tenantId}:{resource}
      const redisKey = `quota:${tenantId}:${resource}`;
      const now = Date.now();

      try {
        // 2. 执行滑动窗口 Lua 脚本
        const result = await RedisService.getClient().eval(
          RedisQuotaPlugin.SLIDING_WINDOW_SCRIPT,
          1,
          redisKey,
          now.toString(),
          windowMs.toString(),
          limit.toString()
        );

        if (result === 0) {
          logger.warn(`[RedisQuotaPlugin] Rate limit exceeded for ${tenantId} on ${resource}`);
          throw new Error(`RATE_LIMIT_EXCEEDED:${resource}`);
        }
      } catch (err: any) {
        if (err.message.startsWith('RATE_LIMIT_EXCEEDED')) throw err;
        logger.error(`[RedisQuotaPlugin] Script execution failed: ${err.message}`);
        // 容错：脚本失败时不拦截请求 (Fail-Open)
      }

      return payload;
    }
  };
}
