// ============================================================
// Spec Panel — Tree / JSON / Tokens / Edge-case / Interaction
// ============================================================

const { useState: useStateSP } = React;

window.SpecPanel = function SpecPanel({ variant, scenario }) {
  const [tab, setTab] = useStateSP('spec');
  if (!variant) return null;

  return (
    <aside className="spec">
      <div className="spec__tabs">
        <button className={`spec__tab ${tab === 'spec' ? 'is-active' : ''}`} onClick={() => setTab('spec')}>規格</button>
        <button className={`spec__tab ${tab === 'tree' ? 'is-active' : ''}`} onClick={() => setTab('tree')}>Component Tree</button>
        <button className={`spec__tab ${tab === 'json' ? 'is-active' : ''}`} onClick={() => setTab('json')}>Flex JSON</button>
      </div>
      <div className="spec__body">
        {tab === 'spec' && <SpecView variant={variant} scenario={scenario} />}
        {tab === 'tree' && <TreeView node={variant.node} />}
        {tab === 'json' && <JsonView variant={variant} />}
      </div>
    </aside>
  );
};

// --------------------- Spec view ---------------------

function SpecView({ variant, scenario }) {
  const i = variant.interaction || [];
  const e = variant.edge || [];
  const t = variant.tokens || [];
  return (
    <>
      {variant.note && (
        <div className="spec__note" dangerouslySetInnerHTML={{ __html: variant.note }} />
      )}

      <div className="spec__section">
        <h4>整體 layout</h4>
        <dl className="spec__edge">
          <dt>Container</dt>
          <dd>
            {variant.node?.type === 'carousel'
              ? <>carousel · <span className="tok">{variant.node.contents.length}</span> bubble</>
              : <>single bubble</>}
          </dd>
          <dt>Bubble size</dt>
          <dd><span className="tok">{getBubbleSize(variant.node)}</span></dd>
          {variant.altText && (<><dt>alt_text</dt><dd>{variant.altText}</dd></>)}
        </dl>
      </div>

      {t.length > 0 && (
        <div className="spec__section">
          <h4>Token 標註</h4>
          <dl className="spec__edge">
            {t.map((row, idx) => (
              <React.Fragment key={idx}>
                <dt dangerouslySetInnerHTML={{ __html: row[0] }} />
                <dd dangerouslySetInnerHTML={{ __html: row[1] }} />
              </React.Fragment>
            ))}
          </dl>
        </div>
      )}

      {i.length > 0 && (
        <div className="spec__section">
          <h4>互動 spec</h4>
          <dl className="spec__edge">
            {i.map((row, idx) => (
              <React.Fragment key={idx}>
                <dt dangerouslySetInnerHTML={{ __html: row[0] }} />
                <dd dangerouslySetInnerHTML={{ __html: row[1] }} />
              </React.Fragment>
            ))}
          </dl>
        </div>
      )}

      {e.length > 0 && (
        <div className="spec__section">
          <h4>邊界處理</h4>
          <dl className="spec__edge">
            {e.map((row, idx) => (
              <React.Fragment key={idx}>
                <dt dangerouslySetInnerHTML={{ __html: row[0] }} />
                <dd dangerouslySetInnerHTML={{ __html: row[1] }} />
              </React.Fragment>
            ))}
          </dl>
        </div>
      )}
    </>
  );
}

function getBubbleSize(node) {
  if (!node) return '—';
  if (node.type === 'carousel') return node.contents[0]?.size || 'mega';
  return node.size || 'mega';
}

// --------------------- Tree view ---------------------

function TreeView({ node }) {
  if (!node) return null;
  if (node.type === 'carousel') {
    return (
      <pre className="tree-line" style={{ margin: 0 }}>
        <span dangerouslySetInnerHTML={{
          __html: `<span class="t-type">carousel</span> <span class="t-comment">// ${node.contents.length} bubble</span>`
        }} />
        {node.contents.map((b, i) => (
          <span key={i}>
            {`\n`}
            <span dangerouslySetInnerHTML={{ __html: `<span class="t-comment">  ├─ bubble[${b.size || 'mega'}]</span>` }} />
            {`\n`}
            <span dangerouslySetInnerHTML={{ __html: renderTree(b, 2).join('\n') }} />
          </span>
        ))}
      </pre>
    );
  }
  return <pre className="tree-line" style={{ margin: 0 }} dangerouslySetInnerHTML={{ __html: renderTree(node, 0).join('\n') }} />;
}

function renderTree(node, depth, key) {
  if (!node) return [];
  const ind = '  '.repeat(depth);
  const lines = [];

  if (node.type === 'bubble') {
    lines.push(`${ind}<span class="t-type">bubble</span><span class="t-key">[${node.size || 'mega'}]</span>`);
    if (node.header) {
      lines.push(`${ind}  <span class="t-key">header</span>`);
      lines.push(...renderTree(node.header, depth + 2));
    }
    if (node.hero) {
      lines.push(`${ind}  <span class="t-key">hero</span>`);
      lines.push(...renderTree(node.hero, depth + 2));
    }
    if (node.body) {
      lines.push(`${ind}  <span class="t-key">body</span>`);
      lines.push(...renderTree(node.body, depth + 2));
    }
    if (node.footer) {
      lines.push(`${ind}  <span class="t-key">footer</span>`);
      lines.push(...renderTree(node.footer, depth + 2));
    }
    return lines.map((l) => l);
  }

  let label = `<span class="t-type">${node.type}</span>`;
  const attrs = [];
  if (node.layout) attrs.push(`layout=<span class="t-val">${node.layout}</span>`);
  if (node.size && node.type !== 'bubble') attrs.push(`size=<span class="t-val">${node.size}</span>`);
  if (node.weight) attrs.push(`weight=<span class="t-val">${node.weight}</span>`);
  if (node.color) attrs.push(`color=<span class="t-val">${node.color}</span>`);
  if (node.align) attrs.push(`align=<span class="t-val">${node.align}</span>`);
  if (node.wrap) attrs.push(`wrap`);
  if (node.maxLines) attrs.push(`maxLines=<span class="t-val">${node.maxLines}</span>`);
  if (node.spacing) attrs.push(`spacing=<span class="t-val">${node.spacing}</span>`);
  if (node.margin) attrs.push(`margin=<span class="t-val">${node.margin}</span>`);
  if (node.paddingAll) attrs.push(`pad=<span class="t-val">${node.paddingAll}</span>`);
  if (node.backgroundColor) attrs.push(`bg=<span class="t-val">${node.backgroundColor}</span>`);
  if (node.cornerRadius) attrs.push(`radius=<span class="t-val">${node.cornerRadius}</span>`);
  if (node.style) attrs.push(`style=<span class="t-val">${node.style}</span>`);
  if (node.flex != null) attrs.push(`flex=<span class="t-val">${node.flex}</span>`);

  if (node.action) attrs.push(`<span class="t-action">action=${node.action.type}</span>`);

  let body = '';
  if (node.type === 'text') body = ` "<span class="t-val">${truncate(node.text || '', 32)}</span>"`;
  if (node.type === 'button' && node.action) body = ` <span class="t-val">"${truncate(node.action.label || '', 18)}"</span>`;

  lines.push(`${ind}${label}${attrs.length ? '[' + attrs.join(', ') + ']' : ''}${body}`);

  if (node.contents) {
    node.contents.forEach((c) => lines.push(...renderTree(c, depth + 1)));
  }
  return lines;
}

function truncate(s, n) {
  if (!s) return '';
  return s.length > n ? s.slice(0, n) + '…' : s;
}

// --------------------- JSON view ---------------------

function JsonView({ variant }) {
  const [copied, setCopied] = useStateSP(false);
  // Default to 'simulator' so paste-into-Flex-Simulator works without trimming the wrapper.
  // 'sdk' = full Messaging API Message object for linebot-sdk consumers.
  const [mode, setMode] = useStateSP('simulator');

  const payload = mode === 'sdk'
    ? { type: 'flex', altText: variant.altText || '', contents: variant.node }
    : variant.node;
  const payloadStr = JSON.stringify(payload, null, 2);

  const copy = () => {
    navigator.clipboard.writeText(payloadStr);
    setCopied(true);
    setTimeout(() => setCopied(false), 1200);
  };

  const modeSwitch = (
    <div className="json-mode-switch" style={{ display: 'inline-flex', gap: 0, marginRight: 8 }}>
      <button
        className={`btn-ghost ${mode === 'simulator' ? 'is-active' : ''}`}
        onClick={() => setMode('simulator')}
        title="Flex Simulator 用 — 只輸出 bubble / carousel"
      >Simulator</button>
      <button
        className={`btn-ghost ${mode === 'sdk' ? 'is-active' : ''}`}
        onClick={() => setMode('sdk')}
        title="Messaging API payload — 含 type:flex + altText 包裝、可直接餵 linebot-sdk"
      >SDK payload</button>
    </div>
  );

  const modeHint = mode === 'sdk'
    ? <>完整 <strong>Messaging API</strong> Message 物件 — 可直接貼入 <code>FlexContainer.from_dict()</code>。<br/>⚠ 此格式 <strong>不能</strong>貼進 LINE Flex Simulator（Simulator 只吃 bubble / carousel）。</>
    : <>Flex container 本體 — 可直接貼進 <a href="https://developers.line.biz/flex-simulator/" target="_blank" rel="noreferrer">LINE Flex Simulator</a> 預覽。<br/>若要餵 <code>linebot-sdk</code>，切換到 <strong>SDK payload</strong> 取得 <code>altText</code> 包裝版本。</>;

  // If variant.diff is set, show diff-style summary
  if (variant.diff && variant.diff.length > 0) {
    return (
      <>
        <div className="spec__note">
          <strong>Diff 模式</strong> — 此狀態與 happy path 主要差異如下。完整 JSON 請參考 happy path tab 後手動套用。
        </div>
        <div className="spec__section">
          <h4>與 happy path 差異</h4>
          {variant.diff.map((d, i) => (
            <div key={i} style={{ marginBottom: 8, fontFamily: 'inherit' }}>
              <span style={{ color: d[0] === '+' ? '#8fdabf' : d[0] === '-' ? '#fa8e8e' : '#ffc072', fontWeight: 700 }}>{d[0]}</span>
              {' '}
              <span dangerouslySetInnerHTML={{ __html: d[1] }} />
            </div>
          ))}
        </div>
        <div className="spec__section">
          <h4>本狀態節點預覽</h4>
          <pre className="json-block">{colorizeJson(JSON.stringify(variant.node, null, 2))}</pre>
        </div>
      </>
    );
  }

  return (
    <>
      <div className="code-actions">
        {modeSwitch}
        <button className="btn-ghost" onClick={copy}>{copied ? '已複製 ✓' : 'Copy JSON'}</button>
      </div>
      <div className="spec__note">{modeHint}</div>
      <pre className="json-block">{colorizeJson(payloadStr)}</pre>
    </>
  );
}

function colorizeJson(s) {
  // Basic syntax highlight via spans
  const safe = s.replace(/&/g, '&amp;').replace(/</g, '&lt;');
  const out = safe
    .replace(/"([^"\\]|\\.)*"(\s*:)?/g, (m, _a, isKey) => isKey
      ? `<span class="j-key">${m.slice(0, -1)}</span>:`
      : `<span class="j-str">${m}</span>`)
    .replace(/\b(true|false)\b/g, '<span class="j-bool">$1</span>')
    .replace(/\bnull\b/g, '<span class="j-null">null</span>')
    .replace(/(?<=[\s:,\[])(-?\d+(\.\d+)?)/g, '<span class="j-num">$1</span>');
  return <span dangerouslySetInnerHTML={{ __html: out }} />;
}
