import fs from "node:fs/promises";
import path from "node:path";

const debugBase = "http://127.0.0.1:9222";
const appBase = "http://10.0.2.2:8080/mikrotik/admin";
const artifactsDir = path.resolve("mikrotik/admin/tests/artifacts/android");
const args = process.argv.slice(2);
const loginOnly = args[0] === "--login";
const currentOnly = args[0] === "--current" || loginOnly;
const clickMenu = args.includes("--click-menu");
const clickTraffic = args.includes("--click-traffic");
const auditOnly = currentOnly || args[0] === "--audit-only";
const pages = (currentOnly ? ["__current__"] : auditOnly ? args.slice(1) : args)
  .filter((page) => !page.startsWith("--click-"));
const requestedWidth = Number(process.env.QA_WIDTH || 0);
if (!pages.length) throw new Error("Pass one or more PHP page names.");
await fs.mkdir(artifactsDir, { recursive: true });

const targets = await fetch(`${debugBase}/json`).then((response) => response.json());
const pageInfo = targets.find((target) => target.type === "page");
if (!pageInfo) throw new Error("Android WebView target was not found.");
const socket = new WebSocket(pageInfo.webSocketDebuggerUrl);
await new Promise((resolve, reject) => {
  socket.addEventListener("open", resolve, { once: true });
  socket.addEventListener("error", reject, { once: true });
});

let commandId = 0;
const pending = new Map();
socket.addEventListener("message", (event) => {
  const message = JSON.parse(event.data);
  if (!message.id || !pending.has(message.id)) return;
  const item = pending.get(message.id);
  pending.delete(message.id);
  if (message.error) item.reject(new Error(message.error.message));
  else item.resolve(message.result);
});
function send(method, params = {}) {
  const id = ++commandId;
  socket.send(JSON.stringify({ id, method, params }));
  return new Promise((resolve, reject) => pending.set(id, { resolve, reject }));
}
const delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
async function evaluate(expression) {
  const result = await send("Runtime.evaluate", { expression, awaitPromise: true, returnByValue: true });
  if (result.exceptionDetails) throw new Error(result.exceptionDetails.text);
  return result.result.value;
}

await send("Page.enable");
await send("Runtime.enable");
if (loginOnly) {
  const username = JSON.stringify(process.env.QA_USER || "");
  const password = JSON.stringify(process.env.QA_PASSWORD || "");
  await evaluate(`(() => {
    const user=document.querySelector('input[name="username"],input[name="user"],#username');
    const pass=document.querySelector('input[type="password"]');
    if(!user||!pass) throw new Error('Login fields were not found');
    user.value=${username}; pass.value=${password};
    for(const el of [user,pass]) for(const type of ['input','change']) el.dispatchEvent(new Event(type,{bubbles:true}));
    (pass.form||document.querySelector('form'))?.requestSubmit();
    return true;
  })()`);
  await delay(2200);
}
if (requestedWidth > 0) {
  await send("Emulation.setDeviceMetricsOverride", {
    width: requestedWidth,
    height: 800,
    deviceScaleFactor: 1,
    mobile: true,
    screenWidth: requestedWidth,
    screenHeight: 800,
  });
}
const report = {};
for (const page of pages) {
  const url = `${appBase}/${page}${page.includes("?") ? "&" : "?"}ua_android=1`;
  if (!currentOnly) {
    await send("Page.navigate", { url });
    await delay(auditOnly ? 800 : 2400);
  }
  if (clickMenu) {
    await evaluate(`document.querySelector('[data-ad-open-menu]')?.click()`);
    await delay(250);
  }
  if (clickTraffic) {
    await evaluate(`document.querySelector('[data-ad-traffic-toggle]')?.click()`);
    await delay(250);
  }
  const audit = await evaluate(`(() => {
    const html = document.documentElement;
    const controls = [...document.querySelectorAll('button,a,input,select,textarea,summary')].filter(el => {
      const r=el.getBoundingClientRect(); return r.width>0 && r.height>0;
    });
    const tooSmall = controls.map(el => {
      const r=el.getBoundingClientRect();
      return {tag:el.tagName,text:(el.innerText||el.getAttribute('aria-label')||el.name||'').trim().slice(0,45),w:Math.round(r.width),h:Math.round(r.height)};
    }).filter(x => x.h < 38).slice(0,12);
    return {
      title: document.title,
      href: location.href,
      bodyClass: document.body.className,
      pageCss: [...document.styleSheets].map(s => s.href || '').filter(x => x.includes('android_')),
      viewport: [html.clientWidth, html.clientHeight],
      scroll: [html.scrollWidth, html.scrollHeight],
      horizontalOverflow: html.scrollWidth > html.clientWidth + 2,
      fatal: /Fatal error|Warning:|Parse error/i.test(document.body.innerText),
      androidRoot: !!document.querySelector('[class*="android-"]'),
      tooSmall,
      trafficDetails: (() => {
        const button=document.querySelector('[data-ad-traffic-toggle]');
        const details=document.querySelector('[data-ad-traffic-details]');
        return button && details ? {
          expanded:button.getAttribute('aria-expanded'),
          hidden:details.hidden,
          sources:details.querySelectorAll('.ad-traffic-source-card').length,
          count:document.querySelector('[data-ad-traffic-count]')?.textContent?.trim() || '',
          rect:(() => { const r=details.getBoundingClientRect(); return {x:Math.round(r.x),y:Math.round(r.y),w:Math.round(r.width),h:Math.round(r.height)}; })()
        } : null;
      })(),
      detailLinks: [...document.querySelectorAll('a[href]')]
        .map(el => el.getAttribute('href') || '')
        .filter(href => /(?:agents_report_view|pay_invoice|print_invoice|purchases_view|sales_view)\.php\?/.test(href))
        .slice(0,12),
      touchProbe: {
        menu: [...document.elementsFromPoint(Math.max(0, html.clientWidth - 45), 62)].map(el => ({
          tag: el.tagName,
          id: el.id,
          cls: el.className,
          pointer: getComputedStyle(el).pointerEvents,
          position: getComputedStyle(el).position,
          z: getComputedStyle(el).zIndex,
        })).slice(0,8),
        scrollables: [...document.querySelectorAll('*')].filter(el => {
          const s=getComputedStyle(el); return el.scrollHeight > el.clientHeight + 4 && /(auto|scroll)/.test(s.overflowY);
        }).map(el => ({tag:el.tagName,id:el.id,cls:el.className,client:el.clientHeight,scroll:el.scrollHeight,top:el.scrollTop,pointer:getComputedStyle(el).pointerEvents})).slice(0,12),
        blockers: [...document.querySelectorAll('*')].filter(el => {
          const r=el.getBoundingClientRect(),s=getComputedStyle(el);
          return r.width >= html.clientWidth*.9 && r.height >= html.clientHeight*.9 && s.pointerEvents !== 'none' && ['fixed','absolute'].includes(s.position);
        }).map(el => ({tag:el.tagName,id:el.id,cls:el.className,position:getComputedStyle(el).position,z:getComputedStyle(el).zIndex,opacity:getComputedStyle(el).opacity})).slice(0,12),
        layoutChain: ['html','body','#uaShellRoot','#uaShellMain','#uaWorkspaceHost','[data-android-dashboard]','.ad-main'].map(selector => {
          const el=document.querySelector(selector); if(!el) return {selector,missing:true};
          const s=getComputedStyle(el),r=el.getBoundingClientRect();
          return {selector,h:Math.round(r.height),client:el.clientHeight,scroll:el.scrollHeight,overflowY:s.overflowY,position:s.position,display:s.display,minHeight:s.minHeight,height:s.height};
        }),
        sidebar: (() => {
          const el=document.querySelector('.ua-sb3');
          const button=document.querySelector('[data-ad-open-menu]');
          const r=button?.getBoundingClientRect();
          return el ? {present:true,display:getComputedStyle(el).display,h:Math.round(el.getBoundingClientRect().height),toggleType:typeof window.uaAndroidToggleMenu,buttonRect:r?{x:Math.round(r.x),y:Math.round(r.y),w:Math.round(r.width),h:Math.round(r.height)}:null} : {present:false};
        })(),
      },
      text: document.body.innerText.trim().slice(0,160)
    };
  })()`);
  const name = page.replace(/[^a-z0-9_-]+/gi, "-").replace(/-+$/, "");
  const outputPath = path.join(artifactsDir, `${name}.png`);
  if (!auditOnly) {
    const screenshot = await send("Page.captureScreenshot", { format: "png", captureBeyondViewport: false });
    await fs.writeFile(outputPath, Buffer.from(screenshot.data, "base64"));
  }
  report[page] = { ...audit, screenshot: auditOnly ? null : outputPath };
}
if (requestedWidth > 0) await send("Emulation.clearDeviceMetricsOverride");
socket.close();
console.log(JSON.stringify(report, null, 2));
