/**
 * MiMo (Xiaomi) Provider Adapter
 *
 * 基于 OpenAI 兼容 API。支持 Tool Calling、Thinking 模式、流式输出。
 * 模型: mimo-v2.5-pro（1M 上下文 / 131072 max_tokens）/ mimo-v2.5（1M 上下文 / 32768 max_tokens）
 *
 * 独立继承 BaseAdapter，通过 shared/openai-format 和 shared/sse-stream 复用
 * OpenAI 兼容格式构建和 SSE 流式解析逻辑。不与其他 Provider Adapter 耦合。
 *
 * 与 DeepSeek 适配器的关键差异：
 * - 使用 max_completion_tokens（非 max_tokens）
 * - thinking 参数结构与 DeepSeek 一致（thinking.type: "enabled"/"disabled"）
 * - 不提供 /models 端点（listModels 回退到本地元数据）
 * - 不提供 /user/balance 端点
 * - tool_choice 仅支持 "auto"
 *
 * @see apis/mimo-api-docs-20260715.html
 */

import { BaseAdapter } from './base-adapter';
import type { MetonaRequest, MetonaResponse, MetonaStreamEvent } from '../types';
import { MetonaFinishReason } from '../types';
import type { MetonaModelInfo } from '../types/metona-adapter';
import { buildOpenAICompatibleMessages, buildOpenAICompatibleTools } from './shared/openai-format';
import { parseSSEStream, parseOpenAICompatibleResponse } from './shared/sse-stream';

export class MimoAdapter extends BaseAdapter {
  override readonly providerId: string = 'mimo';
  readonly supportedModels = ['mimo-v2.5-pro', 'mimo-v2.5'];
  readonly supportsToolCalling = true;
  readonly supportsThinking = true;

  // MiMo 模型元信息
  // mimo-v2.5-pro: 1M 上下文（与 DeepSeek 一致）/ 131072 max_tokens；mimo-v2.5: 1M 上下文 / 32768 max_tokens
  private static readonly MODEL_INFO: Record<string, MetonaModelInfo> = {
    'mimo-v2.5-pro': {
      id: 'mimo-v2.5-pro',
      name: 'MiMo V2.5 Pro',
      contextWindow: 1_000_000,
      maxOutputTokens: 131_072,
      supportsToolCalling: true,
      supportsThinking: true,
      description: '小米 MiMo 旗舰模型，支持深度思考与工具调用',
    },
    'mimo-v2.5': {
      id: 'mimo-v2.5',
      name: 'MiMo V2.5',
      contextWindow: 1_000_000,
      maxOutputTokens: 32_768,
      supportsToolCalling: true,
      supportsThinking: true,
      description: '小米 MiMo 标准模型，低延迟推理',
    },
  };

  // ===== POST /chat/completions (非流式) =====

  async send(request: MetonaRequest): Promise<MetonaResponse> {
    const body = this.toNativeRequest(request, false);

    // #24 修复: 使用 fetchWithTimeout 替代 getFetchSignal + fetch，确保 timer 清理
    const response = await this.fetchWithTimeout(`${this.config.baseURL}/chat/completions`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.config.apiKey}`,
        ...this.config.headers,
      },
      body: JSON.stringify(body),
    }, this.config.timeoutMs ?? 120_000);

    if (!response.ok) {
      await this.throwHttpError(response, 'MiMo API error');
    }

    const data = await response.json() as Record<string, unknown>;
    const parsed = parseOpenAICompatibleResponse(data, request.meta.requestId, this.providerId, this.config.defaultModel);

    return {
      meta: {
        requestId: request.meta.requestId,
        provider: this.providerId,
        model: (data.model as string) ?? this.config.defaultModel,
        latencyMs: 0,
        timestamp: Date.now(),
      },
      content: parsed.content,
      reasoningContent: parsed.reasoningContent,
      toolCalls: parsed.toolCalls,
      usage: parsed.usage,
      finishReason: parsed.finishReason as MetonaFinishReason,
    };
  }

  // ===== POST /chat/completions (流式) =====

  async *sendStream(request: MetonaRequest): AsyncIterable<MetonaStreamEvent> {
    const body = this.toNativeRequest(request, true);

    // #24 修复: 使用 fetchWithTimeout 替代 getFetchSignal + fetch，确保 timer 清理
    const response = await this.fetchWithTimeout(`${this.config.baseURL}/chat/completions`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.config.apiKey}`,
        ...this.config.headers,
      },
      body: JSON.stringify(body),
    }, this.config.timeoutMs ?? 300_000);

    if (!response.ok || !response.body) {
      await this.throwHttpError(response, 'MiMo stream error');
    }

    yield* parseSSEStream(
      // 非空断言：上方 if 已确保 response.body 不为 null
      response.body!,
      request.meta.requestId,
      request.meta.sessionId,
      request.meta.iteration,
    );
  }

  // ===== 模型列表 =====

  /**
   * MiMo 官方未提供 /models 端点，直接返回本地元数据。
   */
  override async listModels(): Promise<MetonaModelInfo[]> {
    return this.supportedModels.map((id) => MimoAdapter.MODEL_INFO[id] ?? { id });
  }

  /**
   * 获取上下文窗口大小
   *
   * v0.3.1: 优先使用配置注入的 contextWindow，回退到 MODEL_INFO 默认值。
   * MiMo OpenAI 兼容 API 不支持 context_window 参数，此值仅用于
   * Engine 压缩判断和前端 UI 显示。
   */
  override getContextWindow(): number {
    if (typeof this.config.contextWindow === 'number' && this.config.contextWindow > 0) {
      return this.config.contextWindow;
    }
    const modelInfo = MimoAdapter.MODEL_INFO[this.config.defaultModel];
    return modelInfo?.contextWindow ?? 1_000_000;
  }

  // ========== 私有方法 ==========

  /**
   * 构建 MiMo 原生请求体
   *
   * MiMo 特有参数：
   * - 多模态图片：user 消息的 images[] → OpenAI content 数组 [{type:"text"}, {type:"image_url"}]
   *   支持 HTTPS URL 或 base64 Data URI
   * - thinking: { type: "enabled" / "disabled" } — 与 DeepSeek 一致
   * - max_completion_tokens — 非 max_tokens（MiMo 使用新字段名）
   * - stream_options: { include_usage: true } — 流式返回 usage
   * - tool_choice: "auto" — MiMo 仅支持 auto
   *
   * 思考模式下 temperature/top_p 会被 API 强制覆盖，因此不传这两个参数。
   */
  private toNativeRequest(request: MetonaRequest, stream: boolean): Record<string, unknown> {
    const messages = buildOpenAICompatibleMessages(request);
    const tools = buildOpenAICompatibleTools(request.tools);

    // === MiMo 多模态：将 images 转为 OpenAI content 数组 ===
    // buildOpenAICompatibleMessages 不处理图片（各 Provider 自行处理）
    // MiMo 是 OpenAI 兼容 API，多模态格式与 Agnes AI 一致
    const nonSystemMsgs = request.messages.filter((m) => m.role !== 'system');
    let imageCount = 0;
    for (let i = 0; i < messages.length; i++) {
      // messages[0] 是 system，非 system 消息从 messages[1] 开始
      if (i === 0) continue;
      const origMsg = nonSystemMsgs[i - 1];
      if (!origMsg?.images?.length) continue;

      imageCount += origMsg.images.length;

      const contentParts: Array<Record<string, unknown>> = [];
      if (origMsg.content) {
        contentParts.push({ type: 'text', text: origMsg.content });
      }
      for (const img of origMsg.images) {
        contentParts.push({
          type: 'image_url',
          image_url: { url: img.url },
        });
      }
      messages[i].content = contentParts;
    }

    const body: Record<string, unknown> = {
      model: this.config.defaultModel,
      messages,
      // MiMo 使用 max_completion_tokens（非 max_tokens）
      // #41 修复: thinking 模式下 max_completion_tokens 未配置时使用兜底默认值（32768）
      // thinking 占用 token 配额，未配置时 API 默认值可能过小导致输出被截断
      // thinkingEnabled !== false 包含 true 和 undefined（MiMo 默认 enabled）两种情况
      // 审查修复: 兜底值从 63488 改为 32768，避免超出标准版上限导致 API 400
      max_completion_tokens: request.params.maxTokens
        ?? (request.params.thinkingEnabled !== false ? 32768 : undefined),
      stream,
    };

    if (stream) {
      body.stream_options = { include_usage: true };
    }

    if (tools) {
      body.tools = tools;
      // MiMo 仅支持 tool_choice: "auto"
      body.tool_choice = 'auto';
    }

    // Thinking 模式（与 DeepSeek 参数结构一致）
    // MiMo API 默认 thinking.type = "enabled"，必须显式发送 disabled 才能关闭
    if (request.params.thinkingEnabled === false) {
      // 显式禁用思考：传 disabled + temperature/top_p（非思考模式下这两个参数有效）
      body.thinking = { type: 'disabled' };
      body.temperature = request.params.temperature;
      body.top_p = request.params.topP;
    } else {
      // 启用思考（包括 undefined，因为 MiMo 默认 enabled）
      // 思考模式下 temperature/top_p 被 API 强制覆盖为 1.0/0.95，不传
      body.thinking = { type: 'enabled' };
    }

    // 停止序列
    if (request.params.stopSequences?.length) {
      body.stop = request.params.stopSequences;
    }

    return body;
  }
}
