#!/usr/bin/env node
/**
 * 布局诊断 v3 — 正确使用 CDP Target.createTarget + Page target
 */
import { writeFileSync, mkdirSync } from 'fs';
import WebSocket from 'ws';

const BASE = 'http://localhost:5199';
const API = 'http://localhost:8080/api/v1';
const CDP_PORT = parseInt(process.env.CDP_PORT || '9225');
const OUT = '/home/aska/Code/Cyrene/debug/logs/chromium';

mkdirSync(OUT, { recursive: true });

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) => {
    const id = msgId++;
    ws.send(JSON.stringify({ id, method, params }));
    pending.set(id, resolve);
  });
}

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

async function main() {
  // Step 1: Login
  console.log('[1] Logging in...');
  const loginRes = await fetch(`${API}/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'admin', password: 'admin123' }),
  });
  const loginData = await loginRes.json();
  const token = loginData?.token;
  const userId = loginData?.user_id || 'admin';
  console.log('   userId:', userId, 'token:', token?.slice(0, 20) + '...');
  if (!token) throw new Error('Login failed');

  // Step 2: Connect to browser CDP, create target
  console.log('[2] Getting browser WS URL...');
  const ver = await (await fetch(`http://127.0.0.1:${CDP_PORT}/json/version`)).json();
  const browserWsUrl = ver.webSocketDebuggerUrl;
  console.log('   Browser WS:', browserWsUrl.slice(0, 80));

  // Connect to browser
  const bw = new WebSocket(browserWsUrl);
  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);

  // Create a new page target
  console.log('[3] Creating page target...');
  const targetResult = await bcdp('Target.createTarget', { url: 'about:blank', width: 1440, height: 900 });
  const targetId = targetResult?.targetId;
  console.log('   TargetId:', targetId);

  // Get page list to find our target's WS URL
  const pages = await (await fetch(`http://127.0.0.1:${CDP_PORT}/json`)).json();
  const ourPage = pages.find(p => p.id === targetId || p.url === 'about:blank');
  const pageWsUrl = ourPage?.webSocketDebuggerUrl;
  console.log('   Page WS:', pageWsUrl?.slice(0, 80));

  if (!pageWsUrl) throw new Error('Could not find page WS URL');

  // Close browser connection, connect to page
  bw.close();

  // Step 4: Connect to page CDP
  console.log('[4] Connecting to page target...');
  const pw = new WebSocket(pageWsUrl);
  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('DOM.enable');

  // Step 5: Navigate to set origin, inject auth, reload
  console.log('[5] Setting auth...');
  await cdp('Page.navigate', { url: BASE });
  await sleep(3000);

  // Inject localStorage
  const injectResult = await cdp('Runtime.evaluate', {
    expression: `localStorage.setItem('token', ${JSON.stringify(token)}); localStorage.setItem('user_id', '${userId}'); 'injected';`
  });
  console.log('   Inject result:', JSON.stringify(injectResult).slice(0, 200));

  // Reload to pick up auth
  console.log('[6] Reloading with auth...');
  await cdp('Page.navigate', { url: BASE });
  await sleep(4000);

  // Check if on chat page  
  const checkResult = await cdp('Runtime.evaluate', {
    returnByValue: true,
    expression: `JSON.stringify({ title: document.title, hasTextarea: !!document.querySelector('textarea'), bodyText: document.body?.innerText?.slice(0, 200) })`
  });
  console.log('   Page state:', checkResult?.result?.value || 'N/A');

  // Step 7: Screenshot
  console.log('[7] Taking screenshot...');
  const ss = await cdp('Page.captureScreenshot', { format: 'png', clip: { x: 0, y: 0, width: 1440, height: 900, scale: 1 } });
  if (ss?.data) {
    writeFileSync(`${OUT}/screenshot_layout.png`, Buffer.from(ss.data, 'base64'));
    console.log('   ✅ Saved', ss.data.length, 'chars base64');
  } else {
    console.log('   ⚠️ No screenshot data');
  }

  // Step 8: Extract layout diagnostics
  console.log('[8] Extracting layout info...');
  const diagResult = await cdp('Runtime.evaluate', {
    returnByValue: true,
    expression: `
      (() => {
        function info(el) {
          if (!el) return null;
          const s = getComputedStyle(el);
          const r = el.getBoundingClientRect();
          return {
            tag: el.tagName,
            cls: (el.className && typeof el.className === 'string') ? el.className.slice(0,300) : '',
            rect: { t: Math.round(r.top), l: Math.round(r.left), b: Math.round(r.bottom), r: Math.round(r.right), w: Math.round(r.width), h: Math.round(r.height) },
            display: s.display, visibility: s.visibility, opacity: s.opacity,
            overflow: s.overflow, overflowY: s.overflowY, position: s.position,
            zIndex: s.zIndex, flexShrink: s.flexShrink, flexGrow: s.flexGrow,
            clientH: el.clientHeight, scrollH: el.scrollHeight,
            offsetParent: el.offsetParent ? el.offsetParent.tagName : null,
          };
        }
        const R = {};
        R.viewport = { w: innerWidth, h: innerHeight };
        R.root = info(document.getElementById('root'));
        R.hScreen = info(document.querySelector('.h-screen'));
        R.main = info(document.querySelector('main'));

        const m = document.querySelector('main');
        if (m && m.firstElementChild) {
          R.appWrapper = info(m.firstElementChild);
          R.appWrapperChildren = Array.from(m.firstElementChild.children).map((c,i) => ({
            idx: i,
            cls: (c.className && typeof c.className === 'string') ? c.className.slice(0,200) : '',
            ...info(c),
          }));
        }

        R.chatBackground = info(document.querySelector('.chat-background'));
        const cb = document.querySelector('.chat-background');
        if (cb) {
          R.chatBgChildren = Array.from(cb.children).map((c,i) => ({
            idx: i,
            cls: (c.className && typeof c.className === 'string') ? c.className.slice(0,200) : '',
            ...info(c),
          }));
        }

        R.textarea = info(document.querySelector('textarea'));

        R.shrink0 = Array.from(document.querySelectorAll('.flex-shrink-0')).map(el => ({
          text: (el.textContent||'').slice(0,60),
          ...info(el),
        }));

        R.borderT = Array.from(document.querySelectorAll('.border-t')).map(el => ({
          text: (el.textContent||'').slice(0,100),
          ...info(el),
        }));

        return JSON.stringify(R);
      })()
    `
  });

  const diagValue = diagResult?.result?.value || diagResult?.value || '{}';
  const result = typeof diagValue === 'string' ? JSON.parse(diagValue) : diagValue;
  console.log(JSON.stringify(result, null, 2));
  writeFileSync(`${OUT}/diagnostics.json`, JSON.stringify(result, null, 2));

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

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