(() => {
  'use strict';

  const byId = id => document.getElementById(id);
  const toast = msg => window.toast?.(msg);
  const appendLog = (text, tone = 'neutral') => window.appendLog?.(text, tone);
  const apiCall = async (path, method = 'GET', body = null) => {
    if (typeof window.api !== 'function') return { ok: false, error: 'api unavailable' };
    return await window.api(path, method, body);
  };

  const RECORDING_CONTRACT = `NODARAMA — RECORDING CONTRACT

PURPOSE
- Structure valid Recording Sheets for Verbatim.
- Use the live recorder actions exactly as named.

RECORDING SHEET SHAPE
Use a plain JSON object with:
- Task
- Action
- FilePath
- Payload
- Comment

REVISION ACTIONS
- replacestring: Payload requires target and replacement.
- replacefunction: Payload requires start and replacement. The start value must be the unique top-level function declaration line.
- replaceblock: Payload requires start, end, and replacement. Start and end must be unique boundary lines.
- insertbefore: Payload requires find and content.
- insertafter: Payload requires find and content.

WRITE ACTIONS
- newfile: Payload is full file contents as a string. Use only when the file does not already exist.
- rewritefile: Payload is full replacement file contents as a string. Use only when smaller revision actions are not sufficient.

AUTHORING RULES
- Read the target file first when possible.
- Prefer the smallest safe action.
- Prefer replacestring, replacefunction, replaceblock, insertbefore, and insertafter.
- Use newfile only when needed.
- Treat rewritefile as the exception.

RUNTIME ROUTES
- Read: /read/file, /read/tree, /status
- Execute: /record
- Chat and EQ: /chat, /chat/pending, /chat/accept, /eq/reports, /eq/activity
- Logs: /logs/system, /logs/ai, /logs/code

BOTTOM LINE
Author against the live system, not a generic contract.
Return Recording Sheet JSON only when the user asks for a sheet.`;

  const normalizePath = value => String(value || '').trim().replace(/\\/g, '/');

  function activeProjectMeta() {
    const pd = window.SNP?.getProjectData?.() || {};
    return {
      name: String(pd.name || byId('proj-name')?.value || 'Universal').trim(),
      location: String(pd.location || byId('proj-location')?.value || '').trim(),
      summary: String(pd.summary || byId('proj-summary')?.value || '').trim(),
      notes: String(pd.notes || byId('proj-notes')?.value || '').trim()
    };
  }

  function activeProjectRoot() {
    const meta = activeProjectMeta();
    if (!meta.name || meta.name.toLowerCase() === 'universal') return '';
    return normalizePath(meta.location || `./projects/${meta.name}`);
  }

  function treeToText(node, depth = 0, maxDepth = 4) {
    if (!node) return '';
    const pad = '  '.repeat(depth);
    const label = `${pad}${node.type === 'dir' ? '▸ ' : '• '}${node.name || node.path || 'root'}`;
    if (depth >= maxDepth || !Array.isArray(node.children) || !node.children.length) return label;
    return [label, ...node.children.map(child => treeToText(child, depth + 1, maxDepth))].filter(Boolean).join('\n');
  }

  async function writeClipboard(text, label) {
    const value = String(text || '').trim();
    if (!value) {
      toast('Nothing to copy');
      return false;
    }
    try {
      await navigator.clipboard.writeText(value);
      toast(`${label} copied`);
      appendLog(`⧉ copied ${label.toLowerCase()}`, 'pass');
      closeMenu();
      return true;
    } catch {
      toast('Copy failed');
      appendLog(`✗ copy failed: ${label}`, 'fail');
      return false;
    }
  }

  async function copyRecordingContract() {
    try {
      const response = await fetch('/assets/docs/VerbatimRecordingContract.txt', { cache: 'no-store' });
      const text = response.ok ? await response.text() : '';
      if (text.trim()) return writeClipboard(text, 'Recording Contract');
    } catch {}
    return writeClipboard(RECORDING_CONTRACT, 'Recording Contract');
  }

  async function copyProjectTree() {
    const meta = activeProjectMeta();
    if (!meta.name || meta.name.toLowerCase() === 'universal') {
      return writeClipboard('PROJECT TREE\nSelect a project first. Universal does not copy a system root or all project trees for normal users.', 'Project Tree');
    }
    const root = activeProjectRoot();
    const data = await apiCall('/read/tree', 'POST', { root });
    if (!data?.ok || !data.root) return writeClipboard(`PROJECT TREE\nRoot: ${root}\n\nTree unavailable.`, 'Project Tree');
    return writeClipboard([
      'PROJECT TREE',
      `Project: ${meta.name || 'Universal'}`,
      `Root: ${root}`,
      '',
      treeToText(data.root)
    ].join('\n'), 'Project Tree');
  }

  function collectIntentSectionsFromDom() {
    const sections = [];
    document.querySelectorAll('.project-list-section').forEach(sectionEl => {
      const title = sectionEl.querySelector('.project-list-title-input')?.value?.trim() || '';
      const flags = [...sectionEl.querySelectorAll('.project-list-flag')].map(el => ({
        text: String(el.textContent || '').trim().toLowerCase(),
        checked: Boolean(el.querySelector('input')?.checked)
      }));
      const isIntent = flags.some(flag => flag.text.includes('intent') && flag.checked);
      if (!isIntent) return;
      const items = [...sectionEl.querySelectorAll('.field-input, .field-textarea')]
        .map(el => String(el.value || '').trim())
        .filter(Boolean);
      sections.push({ title, items });
    });
    return sections;
  }

  async function copyProjectOverviewIntent() {
    const meta = activeProjectMeta();
    const intentSections = collectIntentSectionsFromDom();
    return writeClipboard([
      'PROJECT OVERVIEW + INTENT',
      '',
      `Name: ${meta.name}`,
      `Location: ${meta.location}`,
      '',
      'Summary:',
      meta.summary || '(none)',
      '',
      'Notes:',
      meta.notes || '(none)',
      '',
      'Intent Repo Lists:',
      intentSections.length ? JSON.stringify(intentSections, null, 2) : '(none marked in current view)'
    ].join('\n'), 'Project Overview + Intent');
  }

  async function fetchLogLines(kind) {
    const endpoints = {
      system: '/logs/system',
      ai: '/logs/ai-chat',
      diagnostic: '/logs/ai-diagnostic',
      code: '/logs/code'
    };
    const data = await apiCall(endpoints[kind]);
    return data?.ok && Array.isArray(data.lines) ? data.lines : [];
  }

  async function copyRecentHistory() {
    const [system, ai, diagnostic, code] = await Promise.all([
      fetchLogLines('system'),
      fetchLogLines('ai'),
      fetchLogLines('diagnostic'),
      fetchLogLines('code')
    ]);
    const items = [
      ...system.slice(-4).map(line => `[System] ${line}`),
      ...ai.slice(-3).map(line => `[AI Chat] ${line}`),
      ...diagnostic.slice(-2).map(line => `[AI Diagnostic] ${line}`),
      ...code.slice(-4).map(line => `[Code] ${line}`)
    ].slice(-12);
    return writeClipboard(['RECENT HISTORY', 'What have we been doing?', '', ...items].join('\n'), 'Recent History');
  }

  function lineTime(line) {
    const match = String(line || '').match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})/);
    if (!match) return 0;
    const dt = new Date(`${match[1]}T${match[2]}`);
    return Number.isFinite(dt.getTime()) ? dt.getTime() : 0;
  }

  function isRunStart(line) {
    return /execute_start|record\/check|playback\/approve|inject →|sent \d+ manifest|\[Code\]/i.test(String(line || ''));
  }

  function isRunEnd(line) {
    return /execute_done|execute_error|run complete|recording complete|inject failed|\[Code\]/i.test(String(line || ''));
  }

  async function copyLastRun() {
    const [system, ai, diagnostic, code] = await Promise.all([
      fetchLogLines('system'),
      fetchLogLines('ai'),
      fetchLogLines('diagnostic'),
      fetchLogLines('code')
    ]);
    const all = [
      ...system.map(line => ({ source: 'System', line, time: lineTime(line) })),
      ...ai.map(line => ({ source: 'AI Chat', line, time: lineTime(line) })),
      ...diagnostic.map(line => ({ source: 'AI Diagnostic', line, time: lineTime(line) })),
      ...code.map(line => ({ source: 'Code', line, time: lineTime(line) }))
    ].filter(item => item.line).sort((a, b) => a.time - b.time);

    if (!all.length) return writeClipboard('LAST RUN\nNo run history found.', 'Last Run');

    const latestRunIndex = (() => {
      for (let i = all.length - 1; i >= 0; i--) if (isRunStart(all[i].line) || isRunEnd(all[i].line)) return i;
      return all.length - 1;
    })();

    const anchor = all[latestRunIndex];
    const anchorTime = anchor.time || Date.now();
    let start = latestRunIndex;
    let end = latestRunIndex;

    for (let i = latestRunIndex; i >= 0; i--) {
      const t = all[i].time || anchorTime;
      if (anchorTime - t > 3 * 60 * 1000) break;
      start = i;
      if (isRunStart(all[i].line)) break;
    }

    for (let i = latestRunIndex; i < all.length; i++) {
      const t = all[i].time || anchorTime;
      if (t - anchorTime > 3 * 60 * 1000) break;
      end = i;
      if (i > latestRunIndex && isRunEnd(all[i].line)) break;
    }

    const bundle = all.slice(start, end + 1).map(item => `[${item.source}] ${item.line}`);
    return writeClipboard(['LAST RUN', 'What just happened?', '', ...bundle].join('\n'), 'Last Run');
  }

  function activeTabName() {
    return document.querySelector('.tab-btn.active')?.dataset?.tab || '';
  }

  function selectedTextFromEditor() {
    const collapsed = [...document.querySelectorAll('#editor-collapsed-view .editor-collapsed-item.selected')]
      .map(el => el.textContent.trim())
      .filter(Boolean);
    if (collapsed.length) return ['EDITOR SECTION SELECTION', '', ...collapsed].join('\n');

    const textarea = byId('editor-textarea');
    if (!textarea) return '';
    const selected = textarea.value.slice(textarea.selectionStart || 0, textarea.selectionEnd || 0).trim();
    if (selected) return selected;
    return textarea.value.trim();
  }

  function selectedTextFromDom(selector, heading) {
    const selected = [...document.querySelectorAll(selector)]
      .filter(el => el.offsetParent !== null || el.classList.contains('selected') || el.classList.contains('context-selected') || el.classList.contains('selected-project-unit'))
      .map(el => {
        const label = el.getAttribute?.('aria-label') || el.dataset?.contextLabel || el.placeholder || el.name || el.id || '';
        const value = 'value' in el ? String(el.value || '').trim() : String(el.innerText || el.textContent || '').trim();
        return [label, value].filter(Boolean).join(label && value ? ': ' : '').trim();
      })
      .filter(Boolean);
    if (!selected.length) return '';
    return [heading, '', ...selected].join('\n\n---\n\n');
  }

  function activeHistoryPanel() {
    return document.querySelector('#panel-history .hpanel.active');
  }

  function selectedIndexIn(items) {
    const idx = items.findIndex(el => el.classList.contains('selected'));
    return idx >= 0 ? idx : items.length - 1;
  }

  function itemTimestamp(el) {
    return lineTime(String(el?.innerText || el?.textContent || '').trim());
  }

  function timestampClusterFromSelection(items) {
    if (!items.length) return [];
    const anchorIndex = selectedIndexIn(items);
    const anchorTime = itemTimestamp(items[anchorIndex]);
    if (!anchorTime) return [items[anchorIndex]];

    let start = anchorIndex;
    for (let i = anchorIndex - 1; i >= 0; i--) {
      const currentTime = itemTimestamp(items[i + 1]) || anchorTime;
      const previousTime = itemTimestamp(items[i]) || currentTime;
      if (currentTime - previousTime > 3 * 60 * 1000) break;
      start = i;
    }

    let end = anchorIndex;
    for (let i = anchorIndex + 1; i < items.length; i++) {
      const previousTime = itemTimestamp(items[i - 1]) || anchorTime;
      const currentTime = itemTimestamp(items[i]) || previousTime;
      if (currentTime - previousTime > 3 * 60 * 1000) break;
      end = i;
    }

    return items.slice(start, end + 1);
  }

  function highlightOnly(items, selected) {
    items.forEach(el => el.classList.remove('selected', 'context-selected'));
    selected.forEach(el => el.classList.add('selected', 'context-selected'));
    selected[0]?.scrollIntoView?.({ block: 'nearest', behavior: 'smooth' });
  }

  function copyActiveHistorySelection() {
    const panel = activeHistoryPanel();
    if (!panel) return '';

    if (panel.id === 'hpanel-tape') {
      return selectedTextFromDom('#hpanel-tape .tape-item.selected', 'HISTORY SELECTION');
    }

    const items = [...panel.querySelectorAll('.log-stream-line')].filter(el => el.offsetParent !== null);
    const cluster = timestampClusterFromSelection(items);
    if (!cluster.length) return '';
    highlightOnly(items, cluster);

    const selected = cluster
      .map(el => String(el.innerText || el.textContent || '').trim())
      .filter(Boolean);
    return selected.length ? ['HISTORY SELECTION', '', ...selected].join('\n\n---\n\n') : '';
  }

  async function copySelection() {
    const tab = activeTabName();
    let text = '';

    if (tab === 'editor') {
      text = selectedTextFromEditor();
      return writeClipboard(text, 'Selection');
    }

    if (tab === 'dubs') {
      text = selectedTextFromDom('#dubs-list .dub-item.selected', 'DUBS SELECTION');
      if (text) return writeClipboard(text, 'Selection');
    }

    if (tab === 'history') {
      text = copyActiveHistorySelection();
      if (text) return writeClipboard(text, 'Selection');
    }

    text = selectedTextFromDom('#system-log .log-line.selected', 'SYSTEM RUN SELECTION')
      || selectedTextFromDom('.context-selected, .selected-project-unit, .selected', 'CURRENT SELECTION')
      || String(window.getSelection?.() || '').trim();

    return writeClipboard(text, 'Selection');
  }

  function ensureStyle() {
    if (byId('nv-context-copy-style')) return;
    const style = document.createElement('style');
    style.id = 'nv-context-copy-style';
    style.textContent = `
      .nv-context-copy-menu{position:fixed;z-index:13000;min-width:248px;background:rgba(35,35,51,.96);border:1px solid rgba(105,86,52,.7);box-shadow:0 10px 28px rgba(0,0,0,.42);padding:4px}
      .nv-context-copy-menu[hidden]{display:none}
      .nv-context-copy-item{display:block;width:100%;text-align:left;padding:9px 12px;border:0;border-bottom:1px solid rgba(86,86,108,.52);background:transparent;color:#c8cad4;font:400 13px/1.2 'Courier New','Courier',monospace;cursor:pointer}
      .nv-context-copy-item:last-child{border-bottom:0}
      .nv-context-copy-item:hover{background:rgba(255,255,255,.04);color:#f0c040}
      .context-selected,.selected-project-unit,.log-stream-line.selected,.tape-item.selected,.dub-item.selected,#system-log .log-line.selected{outline:1px solid rgba(240,192,64,.95)!important;box-shadow:0 0 0 2px rgba(240,192,64,.18)!important;background:rgba(240,192,64,.10)!important;color:#f5e6b8!important}
    `;
    document.head.appendChild(style);
  }

  function ensureMenu() {
    ensureStyle();
    let menu = byId('nv-context-copy-menu');
    if (menu) return menu;
    menu = document.createElement('div');
    menu.id = 'nv-context-copy-menu';
    menu.className = 'nv-context-copy-menu';
    menu.hidden = true;
    menu.innerHTML = [
      '<button class="nv-context-copy-item" data-copy-kind="contract">Copy Recording Contract</button>',
      '<button class="nv-context-copy-item" data-copy-kind="tree">Copy Project Tree</button>',
      '<button class="nv-context-copy-item" data-copy-kind="overview">Copy Project Overview + Intent</button>',
      '<button class="nv-context-copy-item" data-copy-kind="selection">Copy Selection</button>',
      '<button class="nv-context-copy-item" data-copy-kind="last-run">Copy Last Run</button>',
      '<button class="nv-context-copy-item" data-copy-kind="history">Copy Recent History</button>'
    ].join('');
    menu.addEventListener('click', event => {
      const btn = event.target.closest('[data-copy-kind]');
      if (!btn) return;
      runCopy(btn.dataset.copyKind);
    });
    document.body.appendChild(menu);
    return menu;
  }

  function closeMenu() {
    const menu = byId('nv-context-copy-menu');
    if (menu) menu.hidden = true;
  }

  function openMenu(button) {
    const menu = ensureMenu();
    const rect = button.getBoundingClientRect();
    menu.hidden = false;
    menu.style.left = `${Math.max(8, Math.min(rect.left, window.innerWidth - 270))}px`;
    menu.style.top = `${Math.min(rect.bottom + 6, window.innerHeight - 220)}px`;
  }

  function runCopy(kind) {
    if (kind === 'contract') return copyRecordingContract();
    if (kind === 'tree') return copyProjectTree();
    if (kind === 'overview') return copyProjectOverviewIntent();
    if (kind === 'selection') return copySelection();
    if (kind === 'last-run') return copyLastRun();
    if (kind === 'history') return copyRecentHistory();
    return false;
  }

  function boot() {
    const btn = byId('btn-pause');
    if (!btn) return;
    btn.title = 'COPY CONTEXT — copy contract, project tree, overview, last run, or recent history.';
    btn.setAttribute('aria-label', 'Copy context');
    btn.addEventListener('click', event => {
      event.preventDefault();
      event.stopImmediatePropagation();
      openMenu(btn);
    }, true);
    document.addEventListener('click', event => {
      const menu = byId('nv-context-copy-menu');
      if (!menu || menu.hidden) return;
      if (menu.contains(event.target) || event.target.closest('#btn-pause')) return;
      closeMenu();
    });
    document.addEventListener('keydown', event => {
      if (event.key === 'Escape') closeMenu();
    });
  }

  window.NVContextCopy = { copyRecordingContract, copyProjectTree, copyProjectOverviewIntent, copySelection, copyLastRun, copyRecentHistory, openMenu, closeMenu };

  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
  else boot();
})();