import { Injectable } from "@nestjs/common";
import {
  AiChannelExecutor,
  AiChatInput,
  AiChatResult,
  AiResolvedRouteCandidate,
  AiRouteFailureError
} from "../ai.types";

@Injectable()
export class OpenAiCompatibleProvider implements AiChannelExecutor {
  async execute(candidate: AiResolvedRouteCandidate, input: AiChatInput): Promise<AiChatResult> {
    if (!candidate.endpoint) {
      throw new AiRouteFailureError(
        candidate.channel,
        candidate.providerName,
        "MISSING_ENDPOINT",
        "缺少 AI 服务地址配置"
      );
    }

    if (!candidate.apiKey) {
      throw new AiRouteFailureError(
        candidate.channel,
        candidate.providerName,
        "MISSING_API_KEY",
        "缺少 AI 服务密钥配置"
      );
    }

    if (!candidate.model) {
      throw new AiRouteFailureError(
        candidate.channel,
        candidate.providerName,
        "MISSING_MODEL",
        "缺少 AI 模型配置"
      );
    }

    const requestUrl = this.buildRequestUrl(candidate.endpoint);

    let response: Response;
    try {
      response = await fetch(requestUrl, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${candidate.apiKey}`
        },
        body: JSON.stringify({
          model: candidate.model,
          messages: [
            {
              role: "user",
              content: input.message
            }
          ],
          stream: false
        }),
        signal: AbortSignal.timeout(30000)
      });
    } catch (error) {
      throw new AiRouteFailureError(
        candidate.channel,
        candidate.providerName,
        "UPSTREAM_UNREACHABLE",
        this.toErrorMessage(error, "AI 服务请求失败")
      );
    }

    let payload: unknown;
    try {
      payload = await response.json();
    } catch (error) {
      throw new AiRouteFailureError(
        candidate.channel,
        candidate.providerName,
        "INVALID_RESPONSE",
        this.toErrorMessage(error, "AI 服务返回了无法解析的数据")
      );
    }

    if (!response.ok) {
      throw new AiRouteFailureError(
        candidate.channel,
        candidate.providerName,
        `UPSTREAM_HTTP_${response.status}`,
        this.extractErrorMessage(payload, `AI 服务调用失败，状态码 ${response.status}`)
      );
    }

    const content = this.extractAssistantText(payload);
    if (!content.trim()) {
      throw new AiRouteFailureError(
        candidate.channel,
        candidate.providerName,
        "EMPTY_RESPONSE",
        "AI 服务没有返回有效内容"
      );
    }

    return {
      channel: candidate.channel,
      providerName: candidate.providerName,
      model: this.extractModel(payload) ?? candidate.model,
      content,
      sessionId: input.sessionId,
      usage: this.extractUsage(payload),
      raw: payload
    };
  }

  private buildRequestUrl(endpoint: string): string {
    const normalizedEndpoint = endpoint.replace(/\/+$/, "");
    if (normalizedEndpoint.endsWith("/chat/completions")) {
      return normalizedEndpoint;
    }
    if (normalizedEndpoint.endsWith("/v1")) {
      return `${normalizedEndpoint}/chat/completions`;
    }
    return `${normalizedEndpoint}/v1/chat/completions`;
  }

  private extractAssistantText(payload: unknown): string {
    const chatCompletionText = this.extractChatCompletionText(payload);
    if (chatCompletionText) {
      return chatCompletionText;
    }

    const responsesText = this.extractResponsesApiText(payload);
    if (responsesText) {
      return responsesText;
    }

    return "";
  }

  private extractChatCompletionText(payload: unknown): string {
    if (!this.isRecord(payload)) {
      return "";
    }

    const choices = payload["choices"];
    if (!Array.isArray(choices) || choices.length === 0) {
      return "";
    }

    const firstChoice = choices[0];
    if (!this.isRecord(firstChoice)) {
      return "";
    }

    const message = firstChoice["message"];
    if (this.isRecord(message)) {
      const messageContent = this.extractMessageContent(message["content"]);
      if (messageContent) {
        return messageContent;
      }
    }

    if (typeof firstChoice["text"] === "string") {
      return firstChoice["text"];
    }

    return "";
  }

  private extractResponsesApiText(payload: unknown): string {
    if (!this.isRecord(payload)) {
      return "";
    }

    if (typeof payload["output_text"] === "string") {
      return payload["output_text"];
    }

    const output = payload["output"];
    if (!Array.isArray(output)) {
      return "";
    }

    return output
      .map((item) => {
        if (!this.isRecord(item)) {
          return "";
        }

        if (typeof item["text"] === "string") {
          return item["text"];
        }

        return this.extractMessageContent(item["content"]);
      })
      .filter((item) => item.length > 0)
      .join("\n")
      .trim();
  }

  private extractMessageContent(content: unknown): string {
    if (typeof content === "string") {
      return content;
    }

    if (!Array.isArray(content)) {
      return "";
    }

    return content
      .map((item) => this.extractContentPartText(item))
      .filter((item) => item.length > 0)
      .join("\n")
      .trim();
  }

  private extractContentPartText(item: unknown): string {
    if (!this.isRecord(item)) {
      return "";
    }

    if (typeof item["text"] === "string") {
      return item["text"];
    }

    if (this.isRecord(item["text"]) && typeof item["text"]["value"] === "string") {
      return item["text"]["value"];
    }

    if (typeof item["content"] === "string") {
      return item["content"];
    }

    if (this.isRecord(item["content"]) && typeof item["content"]["text"] === "string") {
      return item["content"]["text"];
    }

    return "";
  }

  private extractModel(payload: unknown): string | null {
    if (!this.isRecord(payload) || typeof payload["model"] !== "string") {
      return null;
    }

    return payload["model"];
  }

  private extractUsage(payload: unknown): AiChatResult["usage"] {
    if (!this.isRecord(payload)) {
      return null;
    }

    const usage = payload["usage"];
    if (!this.isRecord(usage)) {
      return null;
    }

    const promptTokens = this.readNumber(usage["prompt_tokens"]);
    const completionTokens = this.readNumber(usage["completion_tokens"]);
    const totalTokens = this.readNumber(usage["total_tokens"]);

    if (promptTokens === null && completionTokens === null && totalTokens === null) {
      return null;
    }

    return {
      promptTokens: promptTokens ?? 0,
      completionTokens: completionTokens ?? 0,
      totalTokens: totalTokens ?? (promptTokens ?? 0) + (completionTokens ?? 0)
    };
  }

  private extractErrorMessage(payload: unknown, fallback: string): string {
    if (!this.isRecord(payload)) {
      return fallback;
    }

    const error = payload["error"];
    if (!this.isRecord(error) || typeof error["message"] !== "string") {
      return fallback;
    }

    return error["message"];
  }

  private isRecord(value: unknown): value is Record<string, unknown> {
    return typeof value === "object" && value !== null;
  }

  private toErrorMessage(error: unknown, fallback: string): string {
    if (error instanceof Error && error.message) {
      return error.message;
    }

    return fallback;
  }

  private readNumber(value: unknown): number | null {
    return typeof value === "number" && Number.isFinite(value) ? value : null;
  }
}
