#!/usr/bin/env node
/**
 * 最终诊断 — 检查 JS 控制台错误 + 终端渲染确认
 */
import { writeFileSync } from 'fs';
import WebSocket from 'ws';

const CDP_PORT = 9225;
const BASE = 'http://localhost:5199';
const API = 'http://localhost:8080/api/v1';
const OUT = '/home/aska/Code/Cyrene/debug/logs/chromium';

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 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');

  // Use existing page
  const pages = await (await fetch(`http://127.0.0.1:${CDP_PORT}/json`)).json();
  const ourPage = pages.find(p => p.url === BASE || p.url === `${BASE}/`);
  if (!ourPage) throw new Error('No page found');

  // Connect CDP
  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');
  await cdp('Log.enable');

  // Collect console messages
  const consoleMessages = [];
  pw.on('message', (raw) => {
    try {
      const msg = JSON.parse(raw.toString());
      if (msg.method === 'Runtime.consoleAPICalled') {
        consoleMessages.push({
          type: msg.params.type,
          text: msg.params.args?.map(a => a.value ?? a.description ?? '').join(' ').slice(0, 300),
        });
      }
      if (msg.method === 'Log.entryAdded') {
        consoleMessages.push({
          type: 'log',
          text: msg.params.entry?.text?.slice(0, 300) || '',
        });
      }
    } catch {}
  });

  // Inject auth, reload
  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);

  // Get JS errors
  await cdp('Runtime.evaluate', {
    expression: `window.__errors = []; window.onerror = function(m,s,l,c,e) { window.__errors.push({msg:m,source:s,line:l,col:c,error:String(e)}); }; 'listening';`
  });

  // Screenshot
  console.log('[1] Taking screenshot...');
  const ss = await cdp('Page.captureScreenshot', { format: 'png' });
  if (ss?.data) {
    writeFileSync(`${OUT}/screenshot_layout.png`, Buffer.from(ss.data, 'base64'));
    console.log('   ✅ Saved', ss.data.length, 'bytes base64');
  }

  // Get console errors
  const jsErrors = await cdp('Runtime.evaluate', {
    returnByValue: true,
    expression: `JSON.stringify(window.__errors || [])`
  });
  console.log('\n[2] JS Errors:', jsErrors?.result?.value || 'none');

  // Console messages
  console.log('\n[3] Console messages collected:', consoleMessages.length);
  for (const m of consoleMessages) {
    console.log(`   [${m.type}] ${m.text}`);
  }

  // Check computed styles for rendering issues
  console.log('\n[4] Final rendering check...');
  const finalCheck = await cdp('Runtime.evaluate', {
    returnByValue: true,
    expression: `
      JSON.stringify({
        chatInput: (() => {
          const ta = document.querySelector('textarea');
          if (!ta) return 'NO_TEXTAREA';
          const r = ta.getBoundingClientRect();
          // Walk up to find the ChatInput wrapper
          let parent = ta.parentElement;
          for (let i = 0; i < 10 && parent; i++) {
            if (parent.className?.includes?.('border-t')) break;
            parent = parent.parentElement;
          }
          const wrapper = parent;
          return {
            textareaRect: { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height), w: Math.round(r.width) },
            textareaVisible: r.top < innerHeight && r.bottom > 0 && r.width > 0,
            wrapperRect: wrapper ? (() => { const wr = wrapper.getBoundingClientRect(); return { t: Math.round(wr.top), b: Math.round(wr.bottom), h: Math.round(wr.height) }; })() : null,
            wrapperBg: wrapper ? getComputedStyle(wrapper).backgroundColor : 'N/A',
            wrapperDisplay: wrapper ? getComputedStyle(wrapper).display : 'N/A',
          };
        })(),
        iotStatusBar: (() => {
          const cb = document.querySelector('.chat-background');
          if (!cb) return 'NO_CHAT_BG';
          const kids = Array.from(cb.children);
          const iotKid = kids[kids.length - 1]; // Last child of chat-background
          if (!iotKid) return 'NO_IOT_KID';
          const r = iotKid.getBoundingClientRect();
          return {
            rect: { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height) },
            visible: r.top < innerHeight && r.bottom > 0 && r.height > 0,
            cls: iotKid.className?.slice?.(0, 200) || '',
            cs: { display: getComputedStyle(iotKid).display, bg: getComputedStyle(iotKid).backgroundColor },
            txt: (iotKid.textContent || '').slice(0, 100),
          };
        })(),
        overlays: (() => {
          const all = Array.from(document.querySelectorAll('*'));
          return all.filter(el => {
            const cs = getComputedStyle(el);
            return (cs.position === 'fixed' || cs.position === 'absolute') && 
                   parseInt(cs.zIndex) > 0 &&
                   el.clientHeight > 100;
          }).map(el => {
            const r = el.getBoundingClientRect();
            return {
              tag: el.tagName,
              cls: (el.className?.slice?.(0, 150) || ''),
              rect: { t: Math.round(r.top), b: Math.round(r.bottom), l: Math.round(r.left), r: Math.round(r.right), h: Math.round(r.height) },
              zIndex: getComputedStyle(el).zIndex,
              position: getComputedStyle(el).position,
            };
          });
        })(),
      })
    `
  });
  console.log(finalCheck?.result?.value || 'ERROR');

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

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