from __future__ import annotations

import json
import os
import shutil
import subprocess
import time
from pathlib import Path

import yaml


NPX_EXECUTABLE = shutil.which("npx.cmd") or shutil.which("npx") or "npx"
PLAYWRIGHT_CMD = [NPX_EXECUTABLE, "--yes", "--package", "@playwright/cli", "playwright-cli"]
SESSION_NAME = "vip-report-monthly-sales"
TARGET_URL = "https://compass.vip.com/frontend/index.html#/rejectionAnalysis/index"


def run_cmd(args: list[str], *, cwd: Path, timeout: int = 300) -> subprocess.CompletedProcess[str]:
    env = os.environ.copy()
    npm_cache = cwd / ".npm-cache"
    npm_cache.mkdir(parents=True, exist_ok=True)
    env["NPM_CONFIG_CACHE"] = str(npm_cache)
    return subprocess.run(args, cwd=str(cwd), text=True, encoding="utf-8", errors="replace", capture_output=True, timeout=timeout, check=True, env=env)


def main() -> None:
    cfg = yaml.safe_load(Path("config.yaml").read_text(encoding="utf-8"))
    workdir = Path(cfg["paths"]["workdir"]).resolve()
    js_path = workdir / f"tmp-debug-rejection-dom-{time.time_ns()}.js"
    js_path.write_text(
        """async function(page) {
  const sleep = (ms) => page.waitForTimeout(ms);
  await page.goto('""" + TARGET_URL + """?t=' + Date.now(), { waitUntil: 'domcontentloaded', timeout: 120000 });
  await sleep(9000);
  const out = await page.evaluate(() => {
    const visible = (el) => {
      if (!el) return false;
      const s = getComputedStyle(el);
      const r = el.getBoundingClientRect();
      return s.display !== 'none' && s.visibility !== 'hidden' && r.width > 1 && r.height > 1;
    };
    const compact = (s) => String(s || '').replace(/\\s+/g, ' ').trim().slice(0, 260);
    const nodeInfo = (el) => {
      let cur = el;
      const chain = [];
      for (let i = 0; cur && i < 6; i += 1, cur = cur.parentElement) {
        chain.push({ tag: cur.tagName, cls: cur.className, text: compact(cur.textContent) });
      }
      const inputs = Array.from(el.querySelectorAll('input')).map((x) => ({ placeholder: x.getAttribute('placeholder'), value: x.value, cls: x.className }));
      const r = el.getBoundingClientRect();
      return { cls: el.className, text: compact(el.textContent), inputs, rect: { x: r.x, y: r.y, w: r.width, h: r.height }, chain };
    };
    const editors = Array.from(document.querySelectorAll('.el-date-editor,.el-date-editor--daterange,.el-date-editor--monthrange,.el-date-editor--month')).filter(visible).map(nodeInfo);
    const forms = Array.from(document.querySelectorAll('.el-form-item,.search-item,.filter-item')).filter(visible).map(nodeInfo);
    const buttons = Array.from(document.querySelectorAll('button,span')).filter((el) => visible(el) && compact(el.textContent).includes('查询')).map(nodeInfo);
    const vueChain = [];
    const firstEditor = Array.from(document.querySelectorAll('.el-date-editor--daterange')).filter(visible)[0];
    let cur = firstEditor;
    for (let i = 0; cur && i < 8; i += 1, cur = cur.parentElement) {
      const vm = cur.__vue__;
      vueChain.push({
        tag: cur.tagName,
        cls: cur.className,
        text: compact(cur.textContent),
        hasVue: !!vm,
        vueKeys: vm ? Object.keys(vm).filter((k) => !k.startsWith('_')).slice(0, 80) : [],
        dataKeys: vm && vm.$data ? Object.keys(vm.$data).slice(0, 80) : [],
        props: vm && vm.$props ? Object.keys(vm.$props).slice(0, 80) : []
      });
    }
    const parentChain = [];
    const selector = Array.from(document.querySelectorAll('.gd-date-selector.date-selector')).filter((el) => visible(el) && !el.classList.contains('compare'))[0];
    let vm = selector && selector.__vue__;
    for (let i = 0; vm && i < 10; i += 1, vm = vm.$parent) {
      const keys = Object.keys(vm).filter((k) => !k.startsWith('_'));
      parentChain.push({
        name: vm.$options && vm.$options.name,
        tag: vm.$el && vm.$el.tagName,
        cls: vm.$el && vm.$el.className,
        text: vm.$el ? compact(vm.$el.textContent) : '',
        keys: keys.slice(0, 160),
        interesting: keys.filter((k) => /query|search|fetch|get|load|change|date|init|refresh|data/i.test(k)).slice(0, 120),
        methodHints: Object.fromEntries(keys.filter((k) => /changeDateHandler|handlerConditionChange|getConfigs|getParams|handleUpdateOpts/.test(k)).map((k) => [k, String(vm[k]).slice(0, 500)])),
        data: vm.$data ? Object.fromEntries(Object.entries(vm.$data).filter(([k]) => /date|time|query|search|param|condition|filter/i.test(k)).slice(0, 40)) : {}
      });
    }
    return { editors, forms, buttons, vueChain, parentChain, bodyHead: compact(document.body.innerText) };
  });
  return out;
}
""",
        encoding="utf-8",
    )
    try:
        out = run_cmd(PLAYWRIGHT_CMD + ["--session", SESSION_NAME, "run-code", "--filename", str(js_path)], cwd=workdir, timeout=420).stdout
    finally:
        js_path.unlink(missing_ok=True)
    for line in out.splitlines():
        s = line.strip()
        if s.startswith("{") and s.endswith("}"):
            print(json.dumps(json.loads(s), ensure_ascii=False, indent=2))
            return
    print(out)


if __name__ == "__main__":
    main()
