/**
 * 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 { buildOpenAICompatibleMessages, buildOpenAICompatibleTools } from './shared/openai-format';
import { parseSSEStream, parseOpenAICompatibleResponse } from './shared/sse-stream';

export class DeepSeekAdapter extends BaseAdapter {
  override readonly provider: string = 'deepseek';
  readonly supportedModels = ['deepseek-v4-pro', 'deepseek-v4-flash'];
  readonly supportsToolCalling = true;
  readonly supportsThinking = true;

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

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

    const response = await fetch(`${this.config.baseURL}/chat/completions`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.config.apiKey}`,
        ...this.config.headers,
      },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(this.config.timeoutMs ?? 120_000),
    });

    if (!response.ok) {
      const errorBody = await response.text().catch(() => '');
      throw new Error(`DeepSeek API error: ${response.status} ${response.statusText} - ${errorBody}`);
    }

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

    return {
      meta: {
        requestId: request.meta.requestId,
        provider: this.provider,
        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 *chatStream(request: MetonaRequest): AsyncIterable<MetonaStreamEvent> {
    const body = this.toNativeRequest(request, true);

    const response = await fetch(`${this.config.baseURL}/chat/completions`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.config.apiKey}`,
        ...this.config.headers,
      },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(this.config.timeoutMs ?? 300_000),
    });

    if (!response.ok || !response.body) {
      throw new Error(`DeepSeek stream error: ${response.status}`);
    }

    yield* parseSSEStream(
      response.body,
      request.meta.requestId,
      request.meta.sessionId,
      request.meta.iteration,
    );
  }

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

  async listModels(): Promise<string[]> {
    try {
      const response = await fetch(`${this.config.baseURL}/models`, {
        headers: { Authorization: `Bearer ${this.config.apiKey}` },
        signal: AbortSignal.timeout(10_000),
      });
      if (!response.ok) return this.supportedModels;
      const data = await response.json() as { data?: Array<{ id: string }> };
      return data.data?.map((m) => m.id) ?? this.supportedModels;
    } catch {
      return this.supportedModels;
    }
  }

  // ===== 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', xhigh: 'max', 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;
  }
}
