/**
 * 运八司机端小程序 — miniprogram-automator 测试执行器。
 *
 * 由 Python conftest.py 通过 subprocess 驱动，接收 JSON 指令，返回 JSON 结果。
 *
 * 用法:
 *   node runner.js --action smoke --project-path "E:/xxx/dist"
 *
 * 返回 JSON:
 *   { "ok": true/false, "message": "...", "screenshot": "path/to.png" }
 */

const automator = require("miniprogram-automator");
const path = require("path");
const fs = require("fs");
const { execSync } = require("child_process");

// ═══════════════════════════════════════════════════════════════
// 参数解析
// ═══════════════════════════════════════════════════════════════
const args = process.argv.slice(2);
function getArg(name) {
  const idx = args.indexOf(`--${name}`);
  return idx >= 0 ? args[idx + 1] : null;
}

const ACTION = getArg("action") || "smoke";
const PROJECT_PATH = getArg("project-path") || ".";
const DEV_TOOL_PATH =
  getArg("dev-tool-path") || "C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat";
const TIMEOUT = parseInt(getArg("timeout")) || 30000;

const SCREENSHOTS_DIR = path.resolve(__dirname, "../../../../output/screenshots/mini_program");
fs.mkdirSync(SCREENSHOTS_DIR, { recursive: true });

// ═══════════════════════════════════════════════════════════════
// 辅助
// ═══════════════════════════════════════════════════════════════
function ok(msg, screenshot) {
  console.log(JSON.stringify({ ok: true, message: msg, screenshot: screenshot || null }));
  process.exit(0);
}

function fail(msg, screenshot) {
  console.error(JSON.stringify({ ok: false, message: msg, screenshot: screenshot || null }));
  process.exit(1);
}

async function screenshot(miniProgram, name) {
  const file = path.join(SCREENSHOTS_DIR, `${name}_${Date.now()}.png`);
  await miniProgram.screenshot({ path: file });
  return file;
}

// ═══════════════════════════════════════════════════════════════
// Actions
// ═══════════════════════════════════════════════════════════════

async function actionSmoke() {
  let miniProgram;
  try {
    miniProgram = await automator.launch({
      projectPath: PROJECT_PATH,
      cliPath: DEV_TOOL_PATH,
    });

    // 等小程序启动
    await miniProgram.waitFor(5000);

    // 检查是否启动成功
    const currentPage = await miniProgram.currentPage();
    if (!currentPage) {
      return fail("小程序启动后未获取到页面", null);
    }

    // 验证首页元素
    const elements = await currentPage.$$("view");
    const ss = await screenshot(miniProgram, "smoke_home");

    return ok(`首页加载成功，检测到 ${elements.length} 个 view 元素`, ss);
  } catch (e) {
    return fail(`小程序启动失败: ${e.message}`, null);
  } finally {
    if (miniProgram) await miniProgram.close();
  }
}

async function actionNavigate(pagePath) {
  let miniProgram;
  try {
    miniProgram = await automator.launch({
      projectPath: PROJECT_PATH,
      cliPath: DEV_TOOL_PATH,
    });
    await miniProgram.waitFor(3000);

    const currentPage = await miniProgram.currentPage();
    await currentPage.callMethod("navigateTo", { url: pagePath });
    await miniProgram.waitFor(2000);

    const newPage = await miniProgram.currentPage();
    const ss = await screenshot(miniProgram, `nav_${pagePath.replace(/\//g, "_")}`);
    const currentPath = await newPage.path();

    return ok(`导航到 ${pagePath}，当前路径: ${currentPath}`, ss);
  } catch (e) {
    return fail(`页面导航失败 ${pagePath}: ${e.message}`, null);
  } finally {
    if (miniProgram) await miniProgram.close();
  }
}

async function actionElement(actionType, selector) {
  let miniProgram;
  try {
    miniProgram = await automator.launch({
      projectPath: PROJECT_PATH,
      cliPath: DEV_TOOL_PATH,
    });
    await miniProgram.waitFor(3000);

    const currentPage = await miniProgram.currentPage();

    if (actionType === "exists") {
      const el = await currentPage.$(selector);
      const exists = el !== null;
      const ss = exists ? null : await screenshot(miniProgram, `missing_${selector}`);
      return ok(exists ? `元素 "${selector}" 存在` : `元素 "${selector}" 不存在`, ss);
    }

    if (actionType === "tap") {
      const el = await currentPage.$(selector);
      if (!el) {
        return fail(`元素 "${selector}" 不存在，无法点击`, null);
      }
      await el.tap();
      await miniProgram.waitFor(1000);
      const ss = await screenshot(miniProgram, `tap_${selector}`);
      return ok(`点击 "${selector}" 成功`, ss);
    }

    return fail(`未知子操作: ${actionType}`, null);
  } catch (e) {
    return fail(`操作失败 ${actionType}/${selector}: ${e.message}`, null);
  } finally {
    if (miniProgram) await miniProgram.close();
  }
}

// ═══════════════════════════════════════════════════════════════
// 入口
// ═══════════════════════════════════════════════════════════════
(async () => {
  switch (ACTION) {
    case "smoke":
      return actionSmoke();
    case "navigate":
      return actionNavigate(getArg("page") || "/pages/index/index");
    case "element":
      return actionElement(getArg("element-action") || "exists", getArg("selector") || "view");
    default:
      return fail(`未知 action: ${ACTION}`, null);
  }
})();
