#!/usr/bin/env node
/**
 * 精准诊断 — 定位 IoTStatusBar 和 ChatInput 在 DOM 中的确切位置
 */
import WebSocket from 'ws';

const CDP_PORT = parseInt(process.env.CDP_PORT || '9225');
const BASE = 'http://localhost:5199';
const API = 'http://localhost:8080/api/v1';

function makeCDPHelper(ws) {
  let msgId = 1;
  const pending = new Map();
  ws.on('message', (raw) => {
    try {
      const msg = JSON.parse(raw.toString());
      if (msg.id && pending.has(msg.id)) {
        pending.get(msg.id)(msg.result || msg);
        pending.delete(msg.id);
      }
    } catch {}
  });
  return (method, params = {}) => new Promise((resolve, reject) => {
    const id = msgId++;
    const timer = setTimeout(() => { pending.delete(id); reject(new Error(`CDP timeout: ${method}`)); }, 15000);
    ws.send(JSON.stringify({ id, method, params }));
    pending.set(id, (r) => { clearTimeout(timer); resolve(r); });
  });
}

async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }

async function evaluate(cdp, expression, label) {
  try {
    const r = await cdp('Runtime.evaluate', { returnByValue: true, expression });
    const val = r?.result?.value ?? r?.value;
    console.log(`\n=== ${label} ===`);
    console.log(typeof val === 'string' ? val : JSON.stringify(val, null, 2));
    return val;
  } catch (e) {
    console.error(`[${label}] ERROR:`, e.message);
    return null;
  }
}

async function main() {
  // Login
  const loginRes = await fetch(`${API}/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'admin', password: 'admin123' }),
  });
  const { token, user_id: userId } = await loginRes.json();
  if (!token) throw new Error('Login failed');

  // Connect to existing page or create new one
  let pages = await (await fetch(`http://127.0.0.1:${CDP_PORT}/json`)).json();
  console.log('Pages:', pages.map(p => ({ id: p.id?.slice(0, 20), url: p.url?.slice(0, 60) })));

  // Prefer existing localhost page, or navigate a page to localhost
  let ourPage = pages.find(p => p.url === BASE || p.url === `${BASE}/`);
  let needNavigate = false;

  if (!ourPage) {
    // Create new target
    const ver = await (await fetch(`http://127.0.0.1:${CDP_PORT}/json/version`)).json();
    const bw = new WebSocket(ver.webSocketDebuggerUrl);
    await new Promise((resolve, reject) => {
      bw.once('open', resolve);
      bw.once('error', reject);
      setTimeout(() => reject(new Error('browser WS timeout')), 5000);
    });
    const bcdp = makeCDPHelper(bw);
    await bcdp('Target.createTarget', { url: 'about:blank', width: 1440, height: 900 });
    bw.close();
    await sleep(1000);
    pages = await (await fetch(`http://127.0.0.1:${CDP_PORT}/json`)).json();
    ourPage = pages.find(p => p.url === 'about:blank');
    needNavigate = true;
  }

  if (!ourPage) throw new Error('No page found');
  console.log('Using page:', { id: ourPage.id?.slice(0, 20), url: ourPage.url });

  // Connect to page
  const pw = new WebSocket(ourPage.webSocketDebuggerUrl);
  await new Promise((resolve, reject) => {
    pw.once('open', resolve);
    pw.once('error', reject);
    setTimeout(() => reject(new Error('page WS timeout')), 5000);
  });
  const cdp = makeCDPHelper(pw);
  await cdp('Page.enable');
  await cdp('Runtime.enable');

  // Navigate, inject auth, reload
  await cdp('Page.navigate', { url: BASE });
  await sleep(3000);
  await cdp('Runtime.evaluate', { expression: `localStorage.setItem('token', ${JSON.stringify(token)}); localStorage.setItem('user_id', '${userId}');` });
  await cdp('Page.navigate', { url: BASE });
  await sleep(5000);

  // === DIAGNOSIS 1: Main element's full DOM structure ===
  await evaluate(cdp, `
    (() => {
      const main = document.querySelector('main');
      if (!main) return 'NO_MAIN';
      
      function describe(el, maxDepth) {
        if (!el || maxDepth <= 0) return null;
        const r = el.getBoundingClientRect();
        const cs = getComputedStyle(el);
        const txt = (el.textContent || '').replace(/\\s+/g, ' ').slice(0, 80);
        return {
          tag: el.tagName,
          cls: (el.className?.slice?.(0, 200) || ''),
          id: el.id || '',
          rect: { t: Math.round(r.top), b: Math.round(r.bottom), l: Math.round(r.left), r: Math.round(r.right), w: Math.round(r.width), h: Math.round(r.height) },
          cs: { display: cs.display, overflow: cs.overflow, height: cs.height, flexShrink: cs.flexShrink, flexGrow: cs.flexGrow },
          clientH: el.clientHeight, scrollH: el.scrollHeight,
          txt,
          kids: maxDepth > 1 ? Array.from(el.children).map(c => describe(c, maxDepth - 1)) : undefined,
        };
      }
      
      return describe(main, 5);
    })()
  `, 'MainDOMTree');

  // === DIAGNOSIS 2: Find ChatContainer (.chat-background) and its children ===
  await evaluate(cdp, `
    (() => {
      const cb = document.querySelector('.chat-background');
      if (!cb) return 'NO_CHAT_BACKGROUND';
      const r = cb.getBoundingClientRect();
      const cs = getComputedStyle(cb);
      
      const kids = Array.from(cb.children).map((c, i) => {
        const cr = c.getBoundingClientRect();
        const ccs = getComputedStyle(c);
        return {
          idx: i,
          tag: c.tagName,
          cls: (c.className?.slice?.(0, 200) || ''),
          rect: { t: Math.round(cr.top), b: Math.round(cr.bottom), h: Math.round(cr.height) },
          cs: { display: ccs.display, flexShrink: ccs.flexShrink, flexGrow: ccs.flexGrow, height: ccs.height },
          clientH: c.clientHeight,
          txt: (c.textContent || '').replace(/\\s+/g, ' ').slice(0, 100),
        };
      });
      
      return {
        chatBgRect: { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height) },
        chatBgCS: { height: cs.height, overflow: cs.overflow, display: cs.display },
        chatBgClientH: cb.clientHeight,
        children: kids,
      };
    })()
  `, 'ChatContainerChildren');

  // === DIAGNOSIS 3: Check if IoTStatusBar is rendered at all ===
  await evaluate(cdp, `
    (() => {
      // Search for all elements that might be the IoTStatusBar
      const results = [];
      
      // Strategy 1: Find div with "IoT" text that is a direct child of .chat-background
      const cb = document.querySelector('.chat-background');
      if (cb) {
        const iotInChatBg = Array.from(cb.querySelectorAll('*')).filter(el => 
          el.textContent?.includes('IoT') && el.children.length === 0
        );
        results.push({
          strategy: 'IoT_leaf_in_chatBg',
          count: iotInChatBg.length,
          elements: iotInChatBg.slice(0, 3).map(el => {
            const r = el.getBoundingClientRect();
            return {
              tag: el.tagName,
              txt: (el.textContent || '').slice(0, 80),
              rect: { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height) },
            };
          }),
        });
      }
      
      // Strategy 2: Find .border-t elements inside .chat-background 
      if (cb) {
        const borderTInCb = Array.from(cb.querySelectorAll('.border-t'));
        results.push({
          strategy: 'borderT_in_chatBg',
          count: borderTInCb.length,
          elements: borderTInCb.map(el => {
            const r = el.getBoundingClientRect();
            let parentCls = '';
            let p = el.parentElement;
            for (let i = 0; i < 3 && p; i++) {
              parentCls += (p.className?.slice?.(0, 100) || '') + ' | ';
              p = p.parentElement;
            }
            return {
              rect: { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height) },
              txt: (el.textContent || '').replace(/\\s+/g, ' ').slice(0, 100),
              parentChain: parentCls,
            };
          }),
        });
      }
      
      // Strategy 3: Find all flex-shrink-0 inside .chat-background
      if (cb) {
        const shrinkInCb = Array.from(cb.querySelectorAll('.flex-shrink-0'));
        results.push({
          strategy: 'flexShrink0_in_chatBg',
          count: shrinkInCb.length,
          elements: shrinkInCb.map(el => {
            const r = el.getBoundingClientRect();
            return {
              rect: { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height) },
              cls: (el.className?.slice?.(0, 200) || ''),
              txt: (el.textContent || '').replace(/\\s+/g, ' ').slice(0, 100),
              display: getComputedStyle(el).display,
            };
          }),
        });
      }
      
      return results;
    })()
  `, 'IoTStatusBarSearch');

  // === DIAGNOSIS 4: Get complete rect for every child of the App wrapper ===
  await evaluate(cdp, `
    (() => {
      const main = document.querySelector('main');
      if (!main?.firstElementChild) return 'NO_MAIN_CHILD';
      const appWrapper = main.firstElementChild;
      return {
        appWrapperCls: appWrapper.className?.slice?.(0, 200),
        appWrapperRect: (() => { const r = appWrapper.getBoundingClientRect(); return { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height) }; })(),
        children: Array.from(appWrapper.children).map((c, i) => {
          const r = c.getBoundingClientRect();
          const cs = getComputedStyle(c);
          return {
            idx: i,
            cls: (c.className?.slice?.(0, 200) || ''),
            rect: { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height) },
            cs: { display: cs.display, flexGrow: cs.flexGrow, flexShrink: cs.flexShrink, height: cs.height, overflow: cs.overflow },
            clientH: c.clientHeight,
            childCount: c.children.length,
            firstChildCls: c.firstElementChild ? (c.firstElementChild.className?.slice?.(0, 150) || '') : '',
          };
        }),
      };
    })()
  `, 'AppWrapperChildren');

  pw.close();
  console.log('\n✅ Done.');
}

main().catch(err => { console.error('Fatal:', err); process.exit(1); });
