import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { JsonRpcProvider } from 'ethers';
import { ChainConfigService } from './chain-config.service';
import { ChainTypeEnum } from '@/domain/enums';

/**
 * 每条链的 RPC 端点健康状态
 */
interface RpcHealthState {
  /** 当前活跃的 JsonRpcProvider 实例 */
  provider: JsonRpcProvider;
  /** 该链可用的所有 RPC URL 列表（第一个为默认主端点） */
  urls: string[];
  /** 当前使用的 URL 在 urls 数组中的索引 */
  currentIndex: number;
  /** 该链的 chainId（用于创建新 provider） */
  chainId: number;
  /** 首次连续失败的时间戳（null 表示当前健康） */
  firstFailureAt: number | null;
  /** 连续失败次数（用于日志） */
  consecutiveFailures: number;
}

/**
 * RPC Provider 管理器 — 自动故障转移
 *
 * 集中管理各链的 JsonRpcProvider 实例。当某条链的 RPC 端点
 * 持续失败超过 FAILOVER_THRESHOLD_MS（默认 3 分钟）时，
 * 自动切换到下一个备选端点。
 *
 * 使用方式:
 *   - EvmProviderAdapter 和 Erc20TransferService 通过此服务获取 provider
 *   - RPC 调用成功后调用 reportSuccess(chain) 重置失败状态
 *   - RPC 调用失败后调用 reportFailure(chain, error) 记录失败
 *   - 超过阈值后自动轮转到下一个 URL
 *
 * 环境变量:
 *   - KAVA_RPC_URLS: 逗号分隔的多个 Kava RPC URL（可选，默认使用 KAVA_RPC_URL）
 *   - BSC_RPC_URLS:  逗号分隔的多个 BSC RPC URL（可选，默认使用 BSC_RPC_URL）
 */
@Injectable()
export class RpcProviderManager implements OnModuleInit {
  private readonly logger = new Logger(RpcProviderManager.name);
  private readonly healthStates: Map<ChainTypeEnum, RpcHealthState> = new Map();

  /** 持续失败多久后触发端点切换（毫秒），默认 3 分钟 */
  private readonly FAILOVER_THRESHOLD_MS = 3 * 60 * 1000;

  constructor(private readonly chainConfig: ChainConfigService) {}

  onModuleInit(): void {
    this.initializeAllChains();
  }

  /**
   * 初始化所有支持链的 provider
   * 从 ChainConfig 中读取 rpcUrls 列表，创建初始 provider
   */
  private initializeAllChains(): void {
    for (const chainType of this.chainConfig.getSupportedChains()) {
      const config = this.chainConfig.getConfig(
        // ChainType value object 需要从 enum 创建
        { value: chainType, toString: () => chainType } as any,
      );

      const urls = config.rpcUrls;
      const primaryUrl = urls[0];
      const provider = new JsonRpcProvider(primaryUrl, config.chainId);

      this.healthStates.set(chainType, {
        provider,
        urls,
        currentIndex: 0,
        chainId: config.chainId,
        firstFailureAt: null,
        consecutiveFailures: 0,
      });

      if (urls.length > 1) {
        this.logger.log(
          `[INIT] ${chainType} RPC 端点列表 (${urls.length} 个): ${urls.join(', ')}`,
        );
      } else {
        this.logger.log(
          `[INIT] ${chainType} RPC 端点: ${primaryUrl}（未配置备选端点）`,
        );
      }
    }
  }

  /**
   * 获取某条链当前活跃的 provider
   *
   * @param chain 链类型枚举
   * @returns 当前活跃的 JsonRpcProvider
   * @throws Error 如果该链未初始化
   */
  getProvider(chain: ChainTypeEnum): JsonRpcProvider {
    const state = this.healthStates.get(chain);
    if (!state) {
      throw new Error(`RPC Provider 未初始化: ${chain}`);
    }
    return state.provider;
  }

  /**
   * 获取某条链当前使用的 RPC URL（用于日志/调试）
   */
  getCurrentUrl(chain: ChainTypeEnum): string {
    const state = this.healthStates.get(chain);
    return state ? state.urls[state.currentIndex] : 'unknown';
  }

  /**
   * 获取某条链的所有可用 RPC URL 数量
   */
  getUrlCount(chain: ChainTypeEnum): number {
    const state = this.healthStates.get(chain);
    return state ? state.urls.length : 0;
  }

  /**
   * 报告 RPC 调用成功
   * 重置该链的失败计时和连续失败次数
   */
  reportSuccess(chain: ChainTypeEnum): void {
    const state = this.healthStates.get(chain);
    if (!state) return;

    // 如果之前处于失败状态，记录恢复日志
    if (state.firstFailureAt !== null) {
      this.logger.log(
        `[${chain}] RPC 恢复正常: ${state.urls[state.currentIndex]}` +
          ` (之前连续失败 ${state.consecutiveFailures} 次)`,
      );
    }
    state.firstFailureAt = null;
    state.consecutiveFailures = 0;
  }

  /**
   * 报告 RPC 调用失败
   *
   * 首次失败时记录时间戳。后续持续失败超过 FAILOVER_THRESHOLD_MS 后，
   * 自动切换到下一个备选端点。
   *
   * @param chain 链类型枚举
   * @param error 可选的错误对象（用于日志）
   */
  reportFailure(chain: ChainTypeEnum, error?: Error): void {
    const state = this.healthStates.get(chain);
    if (!state) return;

    const now = Date.now();
    state.consecutiveFailures++;

    // 首次失败：记录起始时间
    if (state.firstFailureAt === null) {
      state.firstFailureAt = now;
      this.logger.warn(
        `[${chain}] RPC 开始失败: ${state.urls[state.currentIndex]}` +
          ` — ${error?.message || 'unknown error'}`,
      );
      return;
    }

    // 检查是否超过故障转移阈值
    const elapsedMs = now - state.firstFailureAt;
    if (elapsedMs >= this.FAILOVER_THRESHOLD_MS) {
      this.switchToNextUrl(chain, state);
    } else {
      // 每 30 秒输出一条持续失败日志，避免日志洪水
      if (state.consecutiveFailures % 6 === 0) {
        this.logger.warn(
          `[${chain}] RPC 持续失败中 (${Math.round(elapsedMs / 1000)}s / ` +
            `${this.FAILOVER_THRESHOLD_MS / 1000}s): ` +
            `${state.urls[state.currentIndex]}`,
        );
      }
    }
  }

  /**
   * 切换到下一个 RPC URL
   *
   * 轮转到 urls 列表中的下一个端点，创建新的 JsonRpcProvider 实例。
   * 如果只有一个 URL，则重新创建 provider（处理临时网络问题）。
   */
  private switchToNextUrl(chain: ChainTypeEnum, state: RpcHealthState): void {
    const oldUrl = state.urls[state.currentIndex];

    // 轮转到下一个 URL
    state.currentIndex = (state.currentIndex + 1) % state.urls.length;
    const newUrl = state.urls[state.currentIndex];

    if (state.urls.length === 1) {
      this.logger.error(
        `[${chain}] 仅有一个 RPC URL，无法切换到备选端点，将重新创建 provider: ${newUrl}`,
      );
    } else {
      this.logger.warn(
        `[${chain}] === RPC 端点切换 === ${oldUrl} → ${newUrl}`,
      );
    }

    // 创建新的 provider 实例（ethers.js v6 的 JsonRpcProvider 创建后 URL 不可变）
    state.provider = new JsonRpcProvider(newUrl, state.chainId);

    // 重置失败状态，给新端点一个全新的 3 分钟窗口
    state.firstFailureAt = null;
    state.consecutiveFailures = 0;
  }
}
