"use strict";

/**
 * Puppeteer 浏览器单例（参考 zhenqianba DouyinBrowserManager / Playwright 实现）
 */

const fs = require("fs");
const path = require("path");
const os = require("os");

const {
  COMMON_BROWSER_ARGS,
  BROWSER_IGNORE_DEFAULT_ARGS,
  STEALTH_INIT_SCRIPT,
  DEFAULT_USER_AGENT,
} = require("./douyin_browser_constants.js");

function getRuntimeDataPath(...parts) {
  const base =
    process.env.AICLIENT_DATA_DIR ||
    path.join(os.homedir(), ".aiclient");
  return path.join(base, ...parts);
}

function getChromiumExecutablePath() {
  const fromEnv =
    process.env.AICLIENT_CHROMIUM_PATH ||
    process.env.PUPPETEER_EXECUTABLE_PATH;
  if (fromEnv && fs.existsSync(fromEnv)) return fromEnv;

  const candidates = [];
  if (process.platform === "win32") {
    const pf = process.env["ProgramFiles"] || "C:\\Program Files";
    const pfx86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
    const administrator_appdata="C:\\Users\\Administrator\\AppData\\Local";
    const local = process.env.LOCALAPPDATA || "";
    candidates.push(
      path.join(pf, "Google", "Chrome", "Application", "chrome.exe"),
      path.join(pfx86, "Google", "Chrome", "Application", "chrome.exe"),
      path.join(local, "Google", "Chrome", "Application", "chrome.exe"),
      path.join(pf, "Microsoft", "Edge", "Application", "msedge.exe"),
      path.join(pfx86, "Microsoft", "Edge", "Application", "msedge.exe"),
      path.join(pf, "Google", "Chrome", "Application", "chrome.exe"),
      path.join(administrator_appdata, "Google", "Chrome", "bin","chrome.exe"),
    );
  } else if (process.platform === "darwin") {
    candidates.push(
      "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
      "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
    );
  } else {
    candidates.push(
      "/usr/bin/google-chrome",
      "/usr/bin/chromium",
      "/usr/bin/chromium-browser"
    );
  }
  return candidates.find((p) => p && fs.existsSync(p));
}

function loadPuppeteer() {
  try {
    return require("puppeteer-core");
  } catch (_) {
    return require("puppeteer");
  }
}

function isHeadless() {
  if (process.env.AICLIENT_PUPPETEER_HEADLESS === "0") return false;
  if (process.env.AICLIENT_PUPPETEER_HEADLESS === "false") return false;
  return "new";
}

class DouyinBrowserManager {
  constructor() {
    this.browser = null;
    this.isInitializing = false;
    // 下载链路使用独立临时 profile，避免与其他流程/遗留进程抢占同一目录锁。
    this.userDataPath = getRuntimeDataPath(
      "temp",
      `douyin-pipeline-${process.pid}-${Date.now()}`
    );
  }

  static getInstance() {
    if (!DouyinBrowserManager.instance) {
      DouyinBrowserManager.instance = new DouyinBrowserManager();
    }
    return DouyinBrowserManager.instance;
  }

  cleanupBrowserLocks(userDataPath) {
    const lockFiles = [
      "SingletonLock",
      "SingletonSocket",
      "SingletonCookie",
      "Lock",
    ];
    for (const lockFile of lockFiles) {
      const lockPath = path.join(userDataPath, lockFile);
      try {
        if (fs.existsSync(lockPath)) {
          fs.unlinkSync(lockPath);
          console.log("已清理锁文件:", lockFile);
        }
      } catch (e) {
        console.log("清理锁文件失败（忽略）:", lockFile, e.message);
      }
    }
  }

  async launchBrowser(userDataPath) {
    const puppeteer = loadPuppeteer();
    if (!fs.existsSync(userDataPath)) {
      fs.mkdirSync(userDataPath, { recursive: true });
    }
    const executablePath = getChromiumExecutablePath();
    if (!executablePath) {
      throw new Error(
        "未找到 Chromium/Chrome。请安装 Chrome/Edge，或设置 AICLIENT_CHROMIUM_PATH，并在 src-tauri/resources/nodejs 执行 npm install"
      );
    }
    console.log("使用浏览器:", executablePath);
    this.browser = await puppeteer.launch({
      executablePath,
      headless: isHeadless(),
      userDataDir: userDataPath,
      args: COMMON_BROWSER_ARGS,
      ignoreDefaultArgs: BROWSER_IGNORE_DEFAULT_ARGS,
      defaultViewport: { width: 1920, height: 1080 },
    });
    console.log("浏览器实例初始化成功");
    this.browser.on("disconnected", () => {
      console.log("浏览器已断开连接");
      this.browser = null;
    });
  }

  async initBrowser() {
    if (this.isInitializing) {
      while (this.isInitializing) {
        await new Promise((r) => setTimeout(r, 500));
      }
      return;
    }
    if (this.browser && this.browser.isConnected()) {
      return;
    }
    this.isInitializing = true;
    try {
      const executablePath = getChromiumExecutablePath();
      if (executablePath && !fs.existsSync(executablePath)) {
        console.warn("Chromium 路径不存在，将使用 Puppeteer 自带浏览器:", executablePath);
      }
      try {
        console.log("[阶段1] 使用用户数据目录:", this.userDataPath);
        await this.launchBrowser(this.userDataPath);
        return;
      } catch (error1) {
        console.error("[阶段1] 启动失败:", error1.message);
        try {
          console.log("[阶段2] 清理锁文件后重试...");
          this.cleanupBrowserLocks(this.userDataPath);
          await this.launchBrowser(this.userDataPath);
          return;
        } catch (error2) {
          console.error("[阶段2] 启动失败:", error2.message);
          const tempDataPath = getRuntimeDataPath(
            "temp",
            `douyin-browser-${Date.now()}`
          );
          console.log("[阶段3] 使用临时目录:", tempDataPath);
          fs.mkdirSync(tempDataPath, { recursive: true });
          await this.launchBrowser(tempDataPath);
        }
      }
    } finally {
      this.isInitializing = false;
    }
  }

  async newPage() {
    if (!this.browser || !this.browser.isConnected()) {
      await this.initBrowser();
    }
    const page = await this.browser.newPage();
    await page.setUserAgent(DEFAULT_USER_AGENT);
    await page.evaluateOnNewDocument(STEALTH_INIT_SCRIPT);
    const pageTimeoutMs =
      Number(process.env.AICLIENT_DOUYIN_PAGE_TIMEOUT_MS) || 90_000;
    page.setDefaultNavigationTimeout(pageTimeoutMs);
    page.setDefaultTimeout(pageTimeoutMs);
    return page;
  }

  async close() {
    try {
      if (this.browser) {
        await this.browser.close();
        this.browser = null;
        console.log("浏览器管理器已关闭");
      }
      if (
        this.userDataPath &&
        this.userDataPath.includes(`${path.sep}temp${path.sep}douyin-pipeline-`)
      ) {
        try {
          fs.rmSync(this.userDataPath, { recursive: true, force: true });
        } catch {
          /* ignore temp dir cleanup errors */
        }
      }
    } catch (error) {
      console.error("关闭浏览器管理器失败:", error);
    }
  }

  isInitialized() {
    return this.browser != null && this.browser.isConnected();
  }

  getBrowserInfo() {
    return { browser: this.browser };
  }

  static async cleanup() {
    if (DouyinBrowserManager.instance) {
      await DouyinBrowserManager.instance.close();
      DouyinBrowserManager.instance = null;
    }
  }
}

module.exports = { DouyinBrowserManager, getRuntimeDataPath };
