/**
 * DeepSeek Provider Adapter
 *
 * 基于 OpenAI 兼容 API。支持 Tool Calling、Thinking 模式、流式输出。
 * 模型: deepseek-v4-flash / deepseek-v4-pro（1M 上下文，384K 最大输出）
 *
 * 独立继承 BaseAdapter，通过 shared/openai-format 和 shared/sse-stream 复用
 * OpenAI 兼容格式构建和 SSE 流式解析逻辑。不与其他 Provider Adapter 耦合。
 *
 * @see apis/deepseek-api-docs-20260518.html
 */

import { BaseAdapter } from './base-adapter';
import type { MetonaRequest, MetonaResponse, MetonaStreamEvent } from '../types';
import { MetonaFinishReason, MetonaErrorCode } 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 DeepSeekAdapter extends BaseAdapter {
  // H-2 修复: provider → providerId（规范要求）
  override readonly providerId: string = 'deepseek';
  readonly supportedModels = ['deepseek-v4-pro', 'deepseek-v4-flash'];
  readonly supportsToolCalling = true;
  readonly supportsThinking = true;

  // H-2 修复: DeepSeek 模型元信息（1M 上下文，384K 最大输出）
  private static readonly MODEL_INFO: Record<string, MetonaModelInfo> = {
    'deepseek-v4-pro': {
      id: 'deepseek-v4-pro',
      name: 'DeepSeek V4 Pro',
      contextWindow: 1_000_000,
      maxOutputTokens: 384_000,
      supportsToolCalling: true,
      supportsThinking: true,
      description: 'DeepSeek 旗舰模型，1M 上下文，支持深度推理与工具调用',
    },
    'deepseek-v4-flash': {
      id: 'deepseek-v4-flash',
      name: 'DeepSeek V4 Flash',
      contextWindow: 1_000_000,
      maxOutputTokens: 384_000,
      supportsToolCalling: true,
      supportsThinking: true,
      description: 'DeepSeek 快速版，1M 上下文，低延迟推理',
    },
  };

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

  // H-2 修复: chat → send（规范要求）
  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, 'DeepSeek 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 (流式) =====

  // H-2 修复: chatStream → sendStream（规范要求）
  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, 'DeepSeek stream error');
    }

    yield* parseSSEStream(
      // 非空断言：上方 if 已确保 response.body 不为 null
      // TypeScript 无法通过 await Promise<never> 正确收窄，需显式断言
      response.body!,
      request.meta.requestId,
      request.meta.sessionId,
      request.meta.iteration,
    );
  }

  // ===== GET /models =====

  /**
   * H-2 修复: 返回 MetonaModelInfo[]（规范要求）
   *
   * 优先尝试从 API 获取实时模型列表，并合并本地 MODEL_INFO 元数据。
   * API 不可用时回退到 supportedModels。
   */
  async listModels(): Promise<MetonaModelInfo[]> {
    try {
      const response = await fetch(`${this.config.baseURL}/models`, {
        headers: { Authorization: `Bearer ${this.config.apiKey}` },
        signal: AbortSignal.timeout(10_000),
      });
      if (response.ok) {
        const data = await response.json() as { data?: Array<{ id: string }> };
        if (data.data?.length) {
          // 合并 API 返回的模型 ID 与本地元数据
          return data.data.map((m) => DeepSeekAdapter.MODEL_INFO[m.id] ?? { id: m.id });
        }
      }
    } catch {
      // API 不可用时降级
    }
    // 回退到 supportedModels（带本地元数据）
    return this.supportedModels.map((id) => DeepSeekAdapter.MODEL_INFO[id] ?? { id });
  }

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

  // ===== GET /user/balance =====

  async getBalance(): Promise<{
    currency: string;
    totalBalance: string;
    grantedBalance: string;
    toppedUpBalance: string;
  } | null> {
    try {
      const response = await fetch(`${this.config.baseURL}/user/balance`, {
        headers: { Authorization: `Bearer ${this.config.apiKey}` },
        signal: AbortSignal.timeout(10_000),
      });
      if (!response.ok) return null;
      const data = await response.json() as {
        currency?: string; total_balance?: string;
        granted_balance?: string; topped_up_balance?: string;
      };
      return {
        currency: data.currency ?? 'CNY',
        totalBalance: data.total_balance ?? '0',
        grantedBalance: data.granted_balance ?? '0',
        toppedUpBalance: data.topped_up_balance ?? '0',
      };
    } catch {
      return null;
    }
  }

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

  /**
   * 构建 DeepSeek 原生请求体
   *
   * DeepSeek 特有参数：
   * - thinking: { type: "enabled" } — 启用思考模式
   * - reasoning_effort — 思考强度映射
   * - stream_options: { include_usage: true } — 流式返回 usage
   */
  private toNativeRequest(request: MetonaRequest, stream: boolean): Record<string, unknown> {
    const messages = buildOpenAICompatibleMessages(request);
    const tools = buildOpenAICompatibleTools(request.tools);

    const body: Record<string, unknown> = {
      model: this.config.defaultModel,
      messages,
      temperature: request.params.temperature,
      max_tokens: request.params.maxTokens,
      stream,
    };

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

    if (tools) {
      body.tools = tools;
    }

    // Thinking 模式
    // API 默认 thinking.type = "enabled"，必须显式发送 disabled 才能关闭
    if (request.params.thinkingEnabled === false) {
      body.thinking = { type: 'disabled' };
    } else if (request.params.thinkingEnabled) {
      body.thinking = { type: 'enabled' };
      const effortMap: Record<string, string> = {
        low: 'high', medium: 'high', high: 'high', max: 'max',
      };
      // DeepSeek API 仅支持 high / max 两档，low/medium 映射为 high
      body.reasoning_effort = effortMap[request.params.thinkingEffort ?? 'high'] ?? 'high';
    }

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

    return body;
  }
}
