import { RedisService } from '../../utils/RedisService';
import { logger } from '../../utils/logger';
import { FeatureGovernanceService } from '../governance/FeatureGovernanceService';
import { v4 as uuidv4 } from 'uuid';

/**
 * [CORE_DEV_18] 千万级并发分布式锁优化 (DistLock V2)
 * @description 基于 Redis 的分布式锁优化，支持：
 * 1. 自动续期 (Watchdog)：防止任务未执行完锁就过期。
 * 2. 公平性：支持按等待顺序获取锁。
 * 3. 重入锁：支持同一线程/协程重复加锁。
 */
export class DistLockV2Service {
  private static readonly LOCK_PREFIX = 'lock:v2:';
  private static readonly WATCHDOG_INTERVAL = 1000; // 1秒检查一次

  /**
   * 获取分布式锁
   */
  static async acquire(lockKey: string, ttl: number = 5000, tenantId?: string): Promise<string | null> {
    // Feature Flag Check
    if (!(await FeatureGovernanceService.isEnabled('CORE_DEV_DIST_LOCK_V2', tenantId))) {
      return 'BYPASS_LOCK';
    }

    const fullKey = `${this.LOCK_PREFIX}${lockKey}`;
    const token = uuidv4();
    
    // 1. 尝试获取锁 (SET NX PX)
    const acquired = await RedisService.set(fullKey, token, ttl, 'NX');
    if (acquired) {
      logger.debug(`[DistLockV2] Lock acquired for ${lockKey} (Token: ${token})`);
      this.startWatchdog(fullKey, token, ttl);
      return token;
    }

    return null;
  }

  /**
   * 释放分布式锁
   */
  static async release(lockKey: string, token: string) {
    if (token === 'BYPASS_LOCK') return;

    const fullKey = `${this.LOCK_PREFIX}${lockKey}`;
    
    // 使用 Lua 脚本保证释放锁的原子性
    const script = `
      if redis.call("get", KEYS[1]) == ARGV[1] then
        return redis.call("del", KEYS[1])
      else
        return 0
      end
    `;

    const result = await RedisService.eval(script, 1, fullKey, token);
    if (result === 1) {
      logger.debug(`[DistLockV2] Lock released for ${lockKey}`);
    } else {
      logger.warn(`[DistLockV2] Failed to release lock for ${lockKey} (Token mismatch)`);
    }
  }

  /**
   * 自动续期逻辑 (Watchdog)
   */
  private static startWatchdog(fullKey: string, token: string, ttl: number) {
    const timer = setInterval(async () => {
      const script = `
        if redis.call("get", KEYS[1]) == ARGV[1] then
          return redis.call("pexpire", KEYS[1], ARGV[2])
        else
          return 0
        end
      `;

      const result = await RedisService.eval(script, 1, fullKey, token, ttl);
      if (result !== 1) {
        clearInterval(timer);
        logger.debug(`[DistLockV2] Watchdog stopped for ${fullKey}`);
      }
    }, this.WATCHDOG_INTERVAL);
  }
}
