/**
 * Toonflow AI供应商模板
 * @version 2.0
 */

// ============================================================
// 类型定义
// ============================================================

type VideoMode =
  | "singleImage" //单图参考
  | "startEndRequired" //首尾帧（两张都得有）
  | "endFrameOptional" //首尾帧（尾帧可选）
  | "startFrameOptional" //首尾帧（首帧可选）
  | "text" //文本
  | (
      | `videoReference:${number}`
      | `imageReference:${number}`
      | `audioReference:${number}`
    )[]; //多参考（数字代表限制数量）

interface TextModel {
  name: string;
  modelName: string;
  type: "text";
  think: boolean;
}

interface ImageModel {
  name: string;
  modelName: string;
  type: "image";
  mode: ("text" | "singleImage" | "multiReference")[];
  associationSkills?: string;
}

interface VideoModel {
  name: string;
  modelName: string;
  type: "video";
  mode: VideoMode[];
  associationSkills?: string;
  audio: "optional" | false | true;
  durationResolutionMap: { duration: number[]; resolution: string[] }[];
}

interface TTSModel {
  name: string;
  modelName: string;
  type: "tts";
  voices: { title: string; voice: string }[];
}

interface VendorConfig {
  id: string; //唯一ID，作为文件名存储用户磁盘上，禁止符号
  version: string; //版本号，格式为x.y，需遵守语义化版本控制
  name: string; //供应商名称
  author: string; //作者
  description?: string; //描述，支持Markdown格式
  icon?: string; //图标，仅支持Base64格式，建议尺寸为128x128像素
  inputs: {
    key: string;
    label: string;
    type: "text" | "password" | "url";
    required: boolean;
    placeholder?: string;
  }[];
  inputValues: Record<string, string>;
  models: (TextModel | ImageModel | VideoModel | TTSModel)[];
}

type ReferenceList =
  | { type: "image"; sourceType: "base64"; base64: string }
  | { type: "audio"; sourceType: "base64"; base64: string }
  | { type: "video"; sourceType: "base64"; base64: string };

interface ImageConfig {
  prompt: string;
  referenceList?: Extract<ReferenceList, { type: "image" }>[];
  size: "1K" | "2K" | "4K";
  aspectRatio: `${number}:${number}`;
}

interface VideoConfig {
  duration: number;
  resolution: string;
  aspectRatio: "16:9" | "9:16";
  prompt: string;
  referenceList?: ReferenceList[];
  audio?: boolean;
  mode: VideoMode[];
}

interface TTSConfig {
  text: string;
  voice: string;
  speechRate: number;
  pitchRate: number;
  volume: number;
  referenceList?: Extract<ReferenceList, { type: "audio" }>[];
}

interface PollResult {
  completed: boolean;
  data?: string;
  error?: string;
}

// ============================================================
// 全局声明
// ============================================================

declare const axios: any; // HTTP请求库
declare const logger: (msg: string) => void; // 日志函数
declare const jsonwebtoken: any; // JWT处理库
declare const zipImage: (base64: string, size: number) => Promise<string>; // 图片压缩函数，返回有头base64字符串
declare const zipImageResolution: (
  base64: string,
  w: number,
  h: number,
) => Promise<string>; // 图片分辨率调整函数，返回有头base64字符串
declare const mergeImages: (
  base64Arr: string[],
  maxSize?: string,
) => Promise<string>; // 图片合成函数，返回有头base64字符串
declare const urlToBase64: (url: string) => Promise<string>; // URL转Base64函数，返回有头base64字符串
declare const pollTask: (
  fn: () => Promise<PollResult>,
  interval?: number,
  timeout?: number,
) => Promise<PollResult>; // 轮询函数，fn为异步函数，interval为轮询间隔，timeout为超时时间，返回fn的结果
declare const createOpenAI: any;
declare const createDeepSeek: any;
declare const createZhipu: any;
declare const createQwen: any;
declare const createAnthropic: any;
declare const createOpenAICompatible: any;
declare const createXai: any;
declare const createMinimax: any;
declare const createGoogleGenerativeAI: any;
declare const exports: {
  vendor: VendorConfig;
  textRequest: (m: TextModel, t: boolean, tl: 0 | 1 | 2 | 3) => any; //文本模型
  imageRequest: (c: ImageConfig, m: ImageModel) => Promise<string>; //图片模型，返回有头base64字符串
  videoRequest: (c: VideoConfig, m: VideoModel) => Promise<string>; //视频模型，返回有头base64字符串
  ttsRequest: (c: TTSConfig, m: TTSModel) => Promise<string>; //（暂未开放）语音模型，返回有头base64字符串
  checkForUpdates?: () => Promise<{
    hasUpdate: boolean;
    latestVersion: string;
    notice: string;
  }>; //检查更新函数，返回是否有更新和最新版本号和更公告（支持Markdown格式）
  updateVendor?: () => Promise<string>; //更新函数，返回最新的代码文本
};

// ============================================================
// 供应商配置
// ============================================================

const vendor: VendorConfig = {
  id: "grsai",
  version: "2.1",
  author: "Toonflow",
  name: "Grsai",
  description:
    "Grsai AI平台适配，支持文生图、图生图、文生视频、Gemini兼容文本模型 \n [前往中转平台](https://tf.grsai.ai/zh)",
  inputs: [
    { key: "apiKey", label: "API密钥", type: "password", required: true },
    {
      key: "baseUrl",
      label: "请求地址",
      type: "url",
      required: true,
      placeholder: "示例：https://grsai.dakka.com.cn",
    },
  ],
  inputValues: { apiKey: "", baseUrl: "https://grsai.dakka.com.cn" },
  models: [
    {
      name: "GPT Image 2",
      modelName: "gpt-image-2",
      type: "image",
      mode: ["text", "singleImage", "multiReference"],
    },
    {
      name: "Nano Banana Fast",
      modelName: "nano-banana-fast",
      type: "image",
      mode: ["text", "singleImage", "multiReference"],
    },
    {
      name: "Nano Banana 2",
      modelName: "nano-banana-2",
      type: "image",
      mode: ["text", "singleImage", "multiReference"],
    },
    {
      name: "Nano Banana Pro",
      modelName: "nano-banana-pro",
      type: "image",
      mode: ["text", "singleImage", "multiReference"],
    },
  ],
};

// ============================================================
// 辅助工具
// ============================================================

const getHeaders = () => {
  const apiKey = vendor.inputValues.apiKey.replace(/^Bearer\s+/i, "");
  return {
    "Content-Type": "application/json",
    Authorization: `Bearer ${apiKey}`,
  };
};

// ============================================================
// 适配器函数
// ============================================================

const textRequest = (
  model: TextModel,
  think: boolean,
  thinkLevel: 0 | 1 | 2 | 3,
) => {
  if (!vendor.inputValues.apiKey) throw new Error("缺少API Key");
  const apiKey = vendor.inputValues.apiKey.replace(/^Bearer\s+/i, "");
  return createGoogleGenerativeAI({
    baseURL: `${vendor.inputValues.baseUrl}/v1beta`,
    apiKey,
  }).chat(model.modelName);
};

const imageRequest = async (
  config: ImageConfig,
  model: ImageModel,
): Promise<string> => {
  if (!vendor.inputValues.apiKey) throw new Error("缺少API Key");
  const baseUrl = vendor.inputValues.baseUrl;
  const headers = getHeaders();

  // 构造请求参数
  const requestBody: any = {
    model: model.modelName,
    prompt: config.prompt,
    aspectRatio: config.aspectRatio,
    webHook: "-1",
    shutProgress: true,
  };

  // 补充模型专属参数
  if (model.modelName.startsWith("nano-banana")) {
    requestBody.imageSize = config.size;
  } else {
    requestBody.size = config.aspectRatio;
    requestBody.variants = 1;
  }

  // 处理参考图
  if (config.referenceList && config.referenceList.length > 0) {
    requestBody.urls = config.referenceList.map((img) => img.base64);
  }

  // 选择接口路径
  const apiPath = model.modelName.startsWith("nano-banana")
    ? "/v1/draw/nano-banana"
    : "/v1/draw/completions";

  logger(`开始提交图片生成任务，模型：${model.modelName}`);
  const submitResp = await axios.post(`${baseUrl}${apiPath}`, requestBody, {
    headers,
  });
  if (submitResp.data.code !== 0)
    throw new Error(`任务提交失败：${submitResp.data.msg}`);

  const taskId = submitResp.data.data.id;
  logger(`图片任务提交成功，任务ID：${taskId}`);

  // 轮询结果
  const pollResult = await pollTask(
    async () => {
      const resp = await axios.post(
        `${baseUrl}/v1/draw/result`,
        { id: taskId },
        { headers },
      );
      if (resp.data.code !== 0)
        return { completed: true, error: resp.data.msg };

      const taskData = resp.data.data;
      if (taskData.status === "failed")
        return {
          completed: true,
          error: taskData.failure_reason || taskData.error,
        };
      if (taskData.status === "succeeded") {
        const imgUrl = taskData.results?.[0]?.url || taskData.url;
        return { completed: true, data: imgUrl };
      }
      logger(`图片任务生成中，进度：${taskData.progress}%`);
      return { completed: false };
    },
    3000,
    600000,
  );

  if (pollResult.error) throw new Error(pollResult.error);
  logger(`图片生成完成，开始转换Base64`);
  return await urlToBase64(pollResult.data!);
};

const videoRequest = async (
  config: VideoConfig,
  model: VideoModel,
): Promise<string> => {
  if (!vendor.inputValues.apiKey) throw new Error("缺少API Key");
  const baseUrl = vendor.inputValues.baseUrl;
  const headers = getHeaders();

  // 构造请求参数
  const requestBody: any = {
    model: model.modelName,
    prompt: config.prompt,
    aspectRatio: config.aspectRatio,
    webHook: "-1",
    shutProgress: true,
  };

  // 处理参考资源
  if (config.referenceList && config.referenceList.length > 0) {
    const imageRefs = config.referenceList.filter(
      (item) => item.type === "image",
    ) as Extract<ReferenceList, { type: "image" }>[];
    if (config.mode.includes("endFrameOptional") && imageRefs.length >= 1) {
      requestBody.firstFrameUrl = imageRefs[0].base64;
      if (imageRefs.length >= 2) requestBody.lastFrameUrl = imageRefs[1].base64;
    } else if (
      config.mode.some(
        (m) => Array.isArray(m) && m.includes("imageReference:3"),
      )
    ) {
      requestBody.urls = imageRefs.map((img) => img.base64);
    }
  }

  logger(`开始提交视频生成任务，模型：${model.modelName}`);
  const submitResp = await axios.post(`${baseUrl}/v1/video/veo`, requestBody, {
    headers,
  });
  if (submitResp.data.code !== 0)
    throw new Error(`任务提交失败：${submitResp.data.msg}`);

  const taskId = submitResp.data.data.id;
  logger(`视频任务提交成功，任务ID：${taskId}`);

  // 轮询结果
  const pollResult = await pollTask(
    async () => {
      const resp = await axios.post(
        `${baseUrl}/v1/draw/result`,
        { id: taskId },
        { headers },
      );
      if (resp.data.code !== 0)
        return { completed: true, error: resp.data.msg };

      const taskData = resp.data.data;
      if (taskData.status === "failed")
        return {
          completed: true,
          error: taskData.failure_reason || taskData.error,
        };
      if (taskData.status === "succeeded") {
        return { completed: true, data: taskData.url };
      }
      logger(`视频任务生成中，进度：${taskData.progress}%`);
      return { completed: false };
    },
    5000,
    1800000,
  );

  if (pollResult.error) throw new Error(pollResult.error);
  logger(`视频生成完成，开始转换Base64`);
  return await urlToBase64(pollResult.data!);
};

const ttsRequest = async (
  config: TTSConfig,
  model: TTSModel,
): Promise<string> => {
  return "";
};

const checkForUpdates = async (): Promise<{
  hasUpdate: boolean;
  latestVersion: string;
  notice: string;
}> => {
  return {
    hasUpdate: false,
    latestVersion: "1.0",
    notice: "## 新版本更新公告",
  };
};

const updateVendor = async (): Promise<string> => {
  return "";
};

// ============================================================
// 导出
// ============================================================

exports.vendor = vendor;
exports.textRequest = textRequest;
exports.imageRequest = imageRequest;
exports.videoRequest = videoRequest;
exports.ttsRequest = ttsRequest;
exports.checkForUpdates = checkForUpdates;
exports.updateVendor = updateVendor;

// 这行代码用于确保当前文件被识别为模块，避免全局变量冲突
export {};
