import { Redis } from 'ioredis';
import { logger } from './logger';

/**
 * [CORE_DEV_12] 分布式 Redis 服务
 * @description 支持集群环境下的配置热更新 (Pub/Sub)
 */
export class RedisService {
  private static client: Redis;

  static getClient() {
    if (!this.client) {
      this.client = new Redis({
        host: process.env.REDIS_HOST || '127.0.0.1',
        port: parseInt(process.env.REDIS_PORT || '6379'),
        retryStrategy: (times) => Math.min(times * 50, 2000),
      });

      this.client.on('error', (err) => logger.error(`[Redis] Error: ${err.message}`));
    }
    return this.client;
  }

  /**
   * [CORE_DEV_12] LPush 队列
   */
  static async lPush(key: string, value: string) {
    try {
      await this.getClient().lpush(key, value);
    } catch (err: any) {
      logger.error(`[Redis] LPush Error: ${err.message}`);
    }
  }

  /**
   * [CORE_DEV_12] RPop 队列
   */
  static async rPop(key: string): Promise<string | null> {
    try {
      return await this.getClient().rpop(key);
    } catch (err: any) {
      logger.error(`[Redis] RPop Error: ${err.message}`);
      return null;
    }
  }

  /**
   * [CORE_DEV_12] Get 缓存
   */
  static async get(key: string): Promise<string | null> {
    try {
      return await this.getClient().get(key);
    } catch (err: any) {
      logger.error(`[Redis] Get Error: ${err.message}`);
      return null;
    }
  }

  /**
   * [CORE_DEV_12] Set 缓存
   */
  static async set(key: string, value: string, ttl?: number) {
    try {
      if (ttl) {
        await this.getClient().setex(key, ttl, value);
      } else {
        await this.getClient().set(key, value);
      }
    } catch (err: any) {
      logger.error(`[Redis] Set Error: ${err.message}`);
    }
  }

  /**
   * [CORE_DEV_12] 原子增加
   */
  static async increment(key: string, amount: number = 1): Promise<number> {
    try {
      return await this.getClient().incrby(key, amount);
    } catch (err: any) {
      logger.error(`[Redis] Increment Error: ${err.message}`);
      return 0;
    }
  }

  /**
   * 发布配置变更消息
   */
  static async publishConfigChange(tenantId: string, key: string, value: any) {
    try {
      const channel = `config:change:${tenantId}`;
      const message = JSON.stringify({ key, value, timestamp: Date.now() });
      
      await this.getClient().publish(channel, message);
      logger.info(`[Redis] Published config change to ${channel}: ${key}`);
    } catch (err: any) {
      logger.error(`[Redis] Failed to publish config change: ${err.message}`);
    }
  }

  /**
   * [CORE_EDGE_01] 订阅状态同步消息
   */
  static async subscribeToStateSync(nodeId: string, callback: (message: any) => void) {
    try {
      const subClient = new Redis({
        host: process.env.REDIS_HOST || '127.0.0.1',
        port: parseInt(process.env.REDIS_PORT || '6379'),
        retryStrategy: (times) => Math.min(times * 50, 2000),
      });

      // 添加错误处理
      subClient.on('error', (err) => {
        logger.warn(`[Redis] Subscription client error: ${err.message}`);
      });

      const channel = 'edge:state:sync';
      await subClient.subscribe(channel);
      
      subClient.on('message', (chan, msg) => {
        if (chan === channel) {
          try {
            const data = JSON.parse(msg);
            if (data.sourceNode !== nodeId) {
              callback(data);
            }
          } catch (err) {
            logger.error(`[Redis] Failed to parse message: ${(err as any).message}`);
          }
        }
      });

      logger.info(`[Redis] Node ${nodeId} subscribed to ${channel}`);
    } catch (err: any) {
      logger.error(`[Redis] Failed to subscribe to state sync: ${err.message}`);
      // 即使Redis连接失败，也不应该阻止应用启动
    }
  }

  /**
   * [CORE_EDGE_01] 广播本地状态变更
   */
  static async broadcastStateChange(nodeId: string, state: any) {
    try {
      const channel = 'edge:state:sync';
      const message = JSON.stringify({ sourceNode: nodeId, state, timestamp: Date.now() });
      
      await this.getClient().publish(channel, message);
      logger.info(`[Redis] Broadcasted state change from ${nodeId}`);
    } catch (err: any) {
      logger.error(`[Redis] Failed to broadcast state: ${err.message}`);
    }
  }
}
