/**
 * LLM Provider 注册表
 * 
 * 将 models.json 中的每家供应商抽象为独立的 ILlmProvider 实例，
 * 提供供应商级别的优先级调度和熔断保护。
 * 
 * 优先级由 models.json 中每个 vendor 的 priority 字段决定，越小越优先。
 * 同一供应商多个 Key 注册为不同的逻辑供应商（如 minimax / minimax-key2），
 * 通过相邻 priority 实现同供应商 Key 优先轮转，再跨供应商降级。
 * 
 * 统一使用 OpenAI 兼容 API（ChatOpenAI），通过 baseURL 切换供应商。
 */

import { ProviderRegistry } from '../../common/provider-registry';
import { ILlmProvider, LlmModelConfig } from './provider.interface';
import { ChatOpenAI } from '@langchain/openai';
import { config } from '../../config';
import { MockLlmProvider } from './mock.provider';

// ============ OpenAI 兼容 Provider 实现 ============

class OpenAiCompatibleLlmProvider implements ILlmProvider {
  readonly name: string;
  readonly vendor: string;
  readonly displayName: string;
  readonly baseUrl: string;
  readonly textModels: string[];
  readonly modelConfigs: Map<string, LlmModelConfig>;

  constructor(
    vendorKey: string,
    displayName: string,
    baseUrl: string,
    apiKey: string,
    models: LlmModelConfig[],
  ) {
    this.name = `${vendorKey}-llm`;
    this.vendor = vendorKey;
    this.displayName = displayName;
    this.baseUrl = baseUrl;
    this._apiKey = apiKey;
    this.textModels = models.filter(m => m.enabled).map(m => m.id);
    this.modelConfigs = new Map(models.map(m => [m.id, m]));
  }

  private _apiKey: string;

  createClient(modelId: string, options?: { temperature?: number; maxTokens?: number }): ChatOpenAI {
    const modelCfg = this.modelConfigs.get(modelId);
    if (!modelCfg) {
      throw new Error(`模型 ${modelId} 不属于供应商 ${this.name}`);
    }

    return new ChatOpenAI({
      model: modelId,
      apiKey: this._apiKey,
      temperature: options?.temperature ?? modelCfg.temperature,
      maxTokens: options?.maxTokens ?? modelCfg.maxTokens,
      configuration: { baseURL: this.baseUrl },
    });
  }

  hasModel(modelId: string): boolean {
    return this.modelConfigs.has(modelId);
  }

  getModelConfig(modelId: string): LlmModelConfig | undefined {
    return this.modelConfigs.get(modelId);
  }

  async healthCheck(): Promise<boolean> {
    try {
      if (this.textModels.length === 0) return false;
      const client = this.createClient(this.textModels[0], { maxTokens: 10 });
      await client.invoke('ping');
      return true;
    } catch {
      return false;
    }
  }
}

// ============ 注册表管理 ============

let _registry: ProviderRegistry<ILlmProvider> | null = null;

/** 将 models.json 中的模型转为 LlmModelConfig */
function toLlmModelConfig(m: any): LlmModelConfig {
  return {
    id: m.id,
    name: m.name || m.id,
    contextWindow: m.contextWindow || 128000,
    maxTokens: m.maxTokens || 4096,
    temperature: m.temperature ?? 0.7,
    supportsToolCall: m.supportsToolCall ?? false,
    enabled: m.enabled ?? true,
    canonicalModel: m.canonicalModel,
  };
}

/** 初始化 LLM Provider 注册表 */
export function initLlmRegistry(): ProviderRegistry<ILlmProvider> {
  if (_registry) return _registry;

  _registry = new ProviderRegistry<ILlmProvider>();

  const vendors = (config.models as any).vendors;
  if (!vendors) {
    console.warn('[LLM Registry] models.json 中无 vendors 配置');
    return _registry;
  }

  for (const [vendorKey, vendorData] of Object.entries(vendors) as [string, any][]) {
    if (vendorData.apiType !== 'openai-chat') continue;
    if (!vendorData.apiKey) {
      console.warn(`[LLM Registry] ${vendorKey} 缺少 apiKey，跳过`);
      continue;
    }

    const textModels = (vendorData.models || [])
      .filter((m: any) => m.input?.includes('text'))
      .map(toLlmModelConfig);

    if (textModels.length === 0) {
      console.log(`[LLM Registry] ${vendorKey} 无文本模型，跳过`);
      continue;
    }

    const provider = new OpenAiCompatibleLlmProvider(
      vendorKey,
      vendorData.name,
      vendorData.baseUrl,
      vendorData.apiKey,
      textModels,
    );

    _registry.register(provider, {
      enabled: true,
      breakerConfig: {
        name: `${vendorKey}-llm`,
        failureThreshold: 3,
        cooldownMs: 60000,
      },
    });

    console.log(`[LLM Registry] ${provider.displayName} 已注册 (${textModels.length}个模型)`);
  }

  // Mock Provider 已禁用（生产环境使用真实 LLM 供应商）
  // if (!_registry.get('mock-llm')) {
  //   const mockProvider = new MockLlmProvider();
  //   _registry.register(mockProvider, {
  //     enabled: true,
  //     breakerConfig: { name: 'mock-llm', failureThreshold: 999, cooldownMs: 1000 },
  //   });
  //   console.log('[LLM Registry] Mock LLM 已注册（兜底，返回固定内容，不调外部API）');
  // }

  console.log(`[LLM Registry] 共注册 ${_registry.size} 个供应商`);
  return _registry;
}

/** 获取 LLM Provider 注册表 */
export function getLlmRegistry(): ProviderRegistry<ILlmProvider> {
  if (!_registry) {
    return initLlmRegistry();
  }
  return _registry;
}

// ============ 便捷查询函数 ============

/** 根据模型 ID 查找所属 Provider */
export function findProviderForModel(modelId: string): ILlmProvider | undefined {
  for (const node of getLlmRegistry().listEnabled()) {
    if (node.provider.hasModel(modelId)) {
      return node.provider;
    }
  }
  return undefined;
}

/** 根据模型 ID 查找所属 Provider 节点 */
export function findProviderNodeForModel(modelId: string) {
  for (const node of getLlmRegistry().listEnabled()) {
    if (node.provider.hasModel(modelId)) {
      return node;
    }
  }
  return undefined;
}

/** 获取所有支持工具调用的模型 */
export function getToolCapableModels(): Array<{ modelId: string; provider: ILlmProvider; node: any }> {
  const result: Array<{ modelId: string; provider: ILlmProvider; node: any }> = [];
  for (const node of getLlmRegistry().listAvailable()) {
    for (const modelId of node.provider.textModels) {
      const cfg = node.provider.getModelConfig(modelId);
      if (cfg?.supportsToolCall) {
        result.push({ modelId, provider: node.provider, node });
      }
    }
  }
  return result;
}

/** 获取默认模型 ID */
export function getDefaultModelId(): string {
  return (config.models as any).textGeneration?.defaultModel || 'MiniMax-M2.7';
}

/** 获取下一个支持工具调用的模型（用于故障切换） */
export function getNextToolCapableModel(currentModelId: string): string | null {
  const toolModels = getToolCapableModels().map(m => m.modelId);
  if (toolModels.length === 0) return null;

  const currentIdx = toolModels.indexOf(currentModelId);
  if (currentIdx === -1) return toolModels[0];
  return toolModels[(currentIdx + 1) % toolModels.length] || null;
}

/** 获取可用供应商状态摘要 */
export function getLlmStatus() {
  return getLlmRegistry().getStatus().map(s => ({
    ...s,
    modelCount: getLlmRegistry().get(s.name)?.provider.textModels.length || 0,
  }));
}

// ============ 定时健康检查 ============

let _healthCheckTimer: ReturnType<typeof setInterval> | null = null;
const HEALTH_CHECK_INTERVAL_MS = 30_000; // 30s

/** 启动定时健康检查，自动探测熔断供应商的恢复 */
export function startHealthCheck(): void {
  if (_healthCheckTimer) return; // 已启动

  _healthCheckTimer = setInterval(async () => {
    const registry = getLlmRegistry();

    // 检查熔断恢复
    const broken = registry.listBroken();
    for (const node of broken) {
      try {
        // 触发熔断器的 getState()，让 OPEN → HALF_OPEN 自动转换
        const state = node.breaker.getState();
        if (state === 'HALF_OPEN') {
          console.log(`[LLM HealthCheck] ${node.provider.displayName} 熔断冷却期已过，尝试探测...`);
          // 发送轻量请求探测恢复
          const healthy = await node.provider.healthCheck?.();
          if (healthy) {
            node.breaker.reset();
            console.log(`[LLM HealthCheck] ✅ ${node.provider.displayName} 熔断器已恢复`);
          } else {
            console.log(`[LLM HealthCheck] ❌ ${node.provider.displayName} 探测失败，继续熔断`);
          }
        }
      } catch {
        // 探测异常，保持熔断
      }
    }

    // 检查额度耗尽恢复
    const exhausted = registry.listExhausted();
    for (const node of exhausted) {
      try {
        const healthy = await node.provider.healthCheck?.();
        if (healthy) {
          registry.clearExhausted(node.provider.name);
          console.log(`[LLM HealthCheck] ✅ ${node.provider.displayName} 额度已恢复`);
        }
      } catch {
        // 探测失败，保持耗尽
      }
    }
  }, HEALTH_CHECK_INTERVAL_MS);

  console.log(`[LLM HealthCheck] 已启动，间隔 ${HEALTH_CHECK_INTERVAL_MS / 1000}s`);
}

/** 停止定时健康检查 */
export function stopHealthCheck(): void {
  if (_healthCheckTimer) {
    clearInterval(_healthCheckTimer);
    _healthCheckTimer = null;
    console.log('[LLM HealthCheck] 已停止');
  }
}
