// ============================================================================
//  douyin_pipeline.js
//
//  Pure-JS port of the original `VideoRewriteIntegration.processDouyinShareComplete`
//  pipeline.  All host capabilities (network, fs, ffmpeg, OSS, ASR, AI chat,
//  log, progress) come from the `__native` global injected by the Rust side
//  (see `js_runtime/native.rs`).  Nothing in this file should ever depend on
//  Node.js, Electron or browser globals.
//
//  由后端 `/api/v1/quickjs-scripts?name=douyin_pipeline.js` 下发；桌面端 QuickJS
//  eval 后调用 `globalThis.__quickjsMain(params)`。
//
//  `params` shape mirrors the original Electron IPC payload:
//      {
//        shareText:    string,
//        modelConfig:  { providerId, modelId, apiUrl, apiKey,
//                        asrMode: "online" | "local",
//                        aliyunApiKey, ossConfig, asrServerInfo },
//        rewriteConfig: object,
//        customPrompt:  string | null
//      }
// ============================================================================

const log = (level, msg, fields) => __native.log(level, msg, fields || null);
const progress = (s) => { try { __native.emitProgress(s); } catch (_) { /* ignore */ } };

// ---------------------------------------------------------------------------
//  DouyinContentParser  (pure-JS, no host calls)
// ---------------------------------------------------------------------------
const DouyinContentParser = {
  // Extract a sharable URL from a raw "share text".  Handles:
  //   - direct https://... links
  //   - "复制此链接，打开Dou音搜索，直接观看视频！https://v.douyin.com/xxxx/"
  //   - bare v.douyin.com short links
  parseContent(shareText) {
    if (!shareText || typeof shareText !== "string") {
      return { success: false, error: "分享文本为空" };
    }
    const urlRegex = /https?:\/\/[^\s\u4e00-\u9fa5]+/g;
    const matches = shareText.match(urlRegex);
    if (!matches || matches.length === 0) {
      return { success: false, error: "未在文本中识别到任何 URL" };
    }
    // Prefer douyin/iesdouyin domains, fallback to first match.
    const douyinUrl =
      matches.find((u) => /douyin\.com|iesdouyin\.com/i.test(u)) || matches[0];
    return { success: true, content: { videoUrl: douyinUrl, raw: shareText } };
  },

  // Whether `url` already points at an actual media file we can fetch directly.
  isDirectVideoUrl(url) {
    if (!url) return false;
    return /\.(mp4|mov|m4v|webm|mkv|flv|avi)(\?|$)/i.test(url);
  },

  // Derive a short title (<= 20 chars) from a long description.
  generateTitleFromDescription(desc) {
    if (!desc) return "改写视频";
    const firstLine = String(desc).split(/[\r\n]/)[0].trim();
    const cleaned = firstLine.replace(/[#@].*$/g, "").trim();
    return (cleaned || "改写视频").slice(0, 20);
  },
};

// ---------------------------------------------------------------------------
//  DouyinVideoDownloader
//      Resolves the final media URL (following 302 redirects on the share
//      domain) and saves the binary to a host-managed temp file.
// ---------------------------------------------------------------------------
class DouyinVideoDownloader {
  async downloadDirectVideo(url) {
    log("info", "downloader.directVideo", { url });
    const dest = __native.tempPath("video", "mp4");
    const res = await __native.httpRequest({
      method: "GET",
      url,
      headers: { "User-Agent": this._ua() },
      response_type: "file",
      dest_path: dest,
      timeout_ms: 120000,
    });
    if (!res || res.status >= 400) {
      return { success: false, error: `直链下载失败 status=${res && res.status}` };
    }
    return { success: true, videoPath: dest };
  }

  async downloadDouyinVideo(shareUrl) {
    log("info", "downloader.douyin", { shareUrl });
    // Step 1: follow redirect on v.douyin.com -> iesdouyin.com to get itemId
    let landing;
    try {
      landing = await __native.httpRequest({
        method: "GET",
        url: shareUrl,
        headers: { "User-Agent": this._ua() },
        response_type: "text",
        follow_redirects: true,
        timeout_ms: 30000,
      });
    } catch (e) {
      return { success: false, error: `解析跳转失败: ${e.message || e}` };
    }
    const finalUrl = (landing && landing.final_url) || shareUrl;
    const itemId =
      this._extract(/\/video\/(\d+)/, finalUrl) ||
      this._extract(/item_ids?=(\d+)/, finalUrl) ||
      this._extract(/modal_id=(\d+)/, finalUrl);
    if (!itemId) {
      return {
        success: false,
        error: "未能从分享链接中提取视频 itemId，可能是抖音改版",
      };
    }
    // Step 2: ask iesdouyin for the playable URL.
    const apiUrl =
      "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=" + itemId;
    let info;
    try {
      info = await __native.httpRequest({
        method: "GET",
        url: apiUrl,
        headers: { "User-Agent": this._ua() },
        response_type: "json",
        timeout_ms: 30000,
      });
    } catch (e) {
      return { success: false, error: `获取视频信息失败: ${e.message || e}` };
    }
    const item =
      info && info.body && info.body.item_list && info.body.item_list[0];
    const playAddr =
      item && item.video && item.video.play_addr && item.video.play_addr.url_list;
    if (!playAddr || !playAddr.length) {
      return { success: false, error: "iesdouyin 接口未返回播放地址" };
    }
    // Force HTTPS + replace `playwm` (watermarked) with `play` if present.
    const mediaUrl = playAddr[0]
      .replace(/^http:\/\//, "https://")
      .replace("/playwm/", "/play/");
    return this.downloadDirectVideo(mediaUrl);
  }

  _ua() {
    return (
      "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
      "AppleWebKit/537.36 (KHTML, like Gecko) " +
      "Chrome/124.0.0.0 Safari/537.36"
    );
  }
  _extract(re, s) {
    const m = String(s || "").match(re);
    return m ? m[1] : null;
  }
}

// ---------------------------------------------------------------------------
//  AI rewrite (OpenAI-compatible chat completions)
// ---------------------------------------------------------------------------
const VideoRewriteOptimizer = {
  buildRewritePrompt(content, rewriteConfig, customPrompt) {
    if (customPrompt && String(customPrompt).trim().length > 0) {
      return String(customPrompt).replace(/\{\{\s*content\s*\}\}/g, content);
    }
    const cfg = rewriteConfig || {};
    const style = cfg.style || "短视频口播风格";
    const tone = cfg.tone || "自然、口语化";
    const lengthHint = cfg.length ? `控制在 ${cfg.length} 字以内` : "保持与原文相近的长度";
    return [
      "你是一名专业的短视频文案改写助手。",
      `请基于下面这段视频转写文案，用【${style}】重新仿写一版。`,
      `要求：语气 ${tone}，${lengthHint}，保留原意但避免与原文雷同；`,
      "不要解释、不要加任何额外标题，直接输出仿写后的正文。",
      "",
      "【原文案】",
      content,
    ].join("\n");
  },
};

async function rewriteWithAI(content, modelConfig, rewriteConfig, customPrompt) {
  if (!modelConfig || !modelConfig.apiUrl || !modelConfig.apiKey) {
    log("error", "rewrite.missingConfig");
    return null;
  }
  const prompt = VideoRewriteOptimizer.buildRewritePrompt(
    content,
    rewriteConfig || {},
    customPrompt
  );
  const result = await __native.openaiChat({
    apiUrl: modelConfig.apiUrl,
    apiKey: modelConfig.apiKey,
    model: modelConfig.modelId,
    messages: [
      { role: "system", content: "你是专业的短视频文案改写助手。" },
      { role: "user", content: prompt },
    ],
    temperature: typeof modelConfig.temperature === "number" ? modelConfig.temperature : 0.8,
    max_tokens: modelConfig.maxTokens || 1024,
  });
  if (!result || !result.success) {
    log("error", "rewrite.failed", { error: result && result.error });
    return null;
  }
  return (result.content || "").trim();
}

// ---------------------------------------------------------------------------
//  VideoRewriteIntegration.processDouyinShareComplete
//      The 1:1 port of the Electron version's pipeline.
// ---------------------------------------------------------------------------
async function processDouyinShareComplete(shareText, modelConfig, rewriteConfig, customPrompt) {
  let videoPath = null;
  let audioPath = null;
  try {
    log("info", "pipeline.start", {
      inputLength: shareText && shareText.length,
      modelConfig: {
        providerId: modelConfig && modelConfig.providerId,
        modelId: modelConfig && modelConfig.modelId,
        asrMode: modelConfig && modelConfig.asrMode,
      },
    });

    // 1) parse share text -> video URL
    progress("正在解析视频URL...");
    const parseResult = DouyinContentParser.parseContent(shareText);
    if (!parseResult.success || !parseResult.content) {
      return { success: false, error: parseResult.error || "内容解析失败" };
    }
    const { videoUrl } = parseResult.content;
    log("info", "pipeline.urlExtracted", { videoUrl });

    // 2) download video
    progress("正在下载视频...");
    const downloader = new DouyinVideoDownloader();
    const downloadResult = DouyinContentParser.isDirectVideoUrl(videoUrl)
      ? await downloader.downloadDirectVideo(videoUrl)
      : await downloader.downloadDouyinVideo(videoUrl);
    if (!downloadResult.success || !downloadResult.videoPath) {
      return { success: false, error: downloadResult.error || "视频下载失败" };
    }
    videoPath = downloadResult.videoPath;
    log("info", "pipeline.videoDownloaded", { videoPath });

    // 3) extract audio (ffmpeg sidecar)
    progress("视频下载完成，正在提取音频...");
    try {
      audioPath = await __native.ffmpegExtractAudio(videoPath);
    } catch (e) {
      return {
        success: false,
        error: `音频提取失败: ${(e && e.message) || e}`,
      };
    }
    log("info", "pipeline.audioExtracted", { audioPath });

    // 4) ASR
    const asrMode = (modelConfig && modelConfig.asrMode) || "online";
    let recognizedText = "";
    if (asrMode === "online") {
      progress("音频提取完成，正在上传到OSS...");
      const ossConfig = modelConfig.ossConfig;
      if (!ossConfig || !ossConfig.bucket) {
        return { success: false, error: "未配置阿里云 OSS（bucket / region / key）" };
      }
      const upload = await __native.aliyunOssUpload(audioPath, ossConfig);
      if (!upload || !upload.success || !upload.url) {
        return {
          success: false,
          error: `音频上传失败: ${(upload && upload.error) || "unknown"}`,
        };
      }
      log("info", "pipeline.ossOk", { audioUrl: upload.url.slice(0, 100) + "..." });

      progress("音频上传完成，正在识别文案（在线模式）...");
      const asr = await __native.aliyunAsrFiletrans(upload.url, {
        apiKey: modelConfig.aliyunApiKey,
        model: "qwen3-asr-flash-filetrans",
      });
      if (!asr || !asr.success) {
        return {
          success: false,
          error: `语音识别失败（在线模式）: ${(asr && asr.error) || "unknown"}`,
        };
      }
      recognizedText = asr.text || "";
      log("info", "pipeline.asrOk", {
        method: "aliyun-online",
        len: recognizedText.length,
      });
    } else {
      progress("音频提取完成，正在识别文案（本地模式）...");
      const info = modelConfig.asrServerInfo;
      if (!info) {
        return {
          success: false,
          error: "未配置本地语音识别服务器，请在「设置」中配置",
        };
      }
      const asr = await __native.localAsr(audioPath, info);
      if (!asr || !asr.success) {
        return {
          success: false,
          error: `语音识别失败（本地模式）: ${(asr && asr.error) || "unknown"}`,
        };
      }
      recognizedText = asr.text || "";
      log("info", "pipeline.asrOk", {
        method: "local",
        server: info.name,
        len: recognizedText.length,
      });
    }

    // 5) rewrite
    if (!modelConfig || !modelConfig.apiUrl || !modelConfig.apiKey) {
      return { success: false, error: "未配置AI模型，请先在设置中配置" };
    }
    progress("文案识别完成，正在改写...");
    const rewrittenDescription = await rewriteWithAI(
      recognizedText,
      modelConfig,
      rewriteConfig,
      customPrompt
    );
    if (!rewrittenDescription) {
      return { success: false, error: "AI仿写失败，请检查模型配置和网络连接" };
    }
    const rewrittenTitle = DouyinContentParser.generateTitleFromDescription(rewrittenDescription);

    log("info", "pipeline.success", {
      originalLength: recognizedText.length,
      rewrittenLength: rewrittenDescription.length,
    });
    progress("完成！");

    return {
      success: true,
      original: {
        videoUrl,
        description: recognizedText,
        hashtags: [],
        title: "视频音频识别结果",
      },
      rewritten: {
        description: rewrittenDescription,
        title: rewrittenTitle,
        fullContent: rewrittenDescription,
      },
    };
  } catch (e) {
    log("error", "pipeline.error", { message: (e && e.message) || String(e) });
    return { success: false, error: (e && e.message) || String(e) };
  } finally {
    // 6) cleanup local temp files
    if (videoPath) {
      try { __native.deleteFile(videoPath); } catch (_) {}
    }
    if (audioPath) {
      try { __native.deleteFile(audioPath); } catch (_) {}
    }
  }
}

// ---------------------------------------------------------------------------
//  单一入口：Rust eval 本脚本后调用 globalThis.__quickjsMain(params)
// ---------------------------------------------------------------------------
globalThis.__quickjsMain = function (params) {
  const p = params || {};
  return processDouyinShareComplete(
    p.shareText,
    p.modelConfig,
    p.rewriteConfig,
    p.customPrompt || null
  );
};
