// ══════════════════════════════════════════════════════════════
// nterm — Block context menu + Visualizer overlay
// ══════════════════════════════════════════════════════════════
// Depends on: state, diag, perf, escapeHtml

// ── Vendor hint resolution ──────────────────────────────────
function getVendorHint(tabId) {
  const session = state.sessions[tabId];
  if (!session) return null;

  // Priority 1: Manual override from Quick Connect dropdown
  if (session.vendorOverride) return session.vendorOverride;

  // Priority 2: Previously detected and cached
  if (session.detectedVendor) return session.detectedVendor;

  // Priority 3: Banner-based detection (most reliable for auto-detect)
  // The sniffer captures pre-prompt banner lines — these almost always
  // contain the OS identifier (JUNOS, EOS, NX-OS, IOS XR, etc.)
  const bext = diag.snifferBlocks[tabId];
  if (bext && bext.banner && bext.banner.length > 0) {
    const bannerText = bext.banner.join(' ').toLowerCase();
    const bannerVendor = _vendorFromText(bannerText);
    if (bannerVendor) {
      session.detectedVendor = bannerVendor;  // cache for subsequent blocks
      return bannerVendor;
    }
  }

  // Priority 4: Hostname heuristic (weak — hostnames rarely contain OS names)
  const pdet = diag.snifferPrompts[tabId];
  if (pdet && pdet.hostname) {
    const hostVendor = _vendorFromText(pdet.hostname.toLowerCase());
    if (hostVendor) {
      session.detectedVendor = hostVendor;
      return hostVendor;
    }
  }

  return null;
}

/**
 * Match vendor platform string from arbitrary text (banner, hostname, etc.)
 * Order matters: more specific patterns first to avoid false positives
 * (e.g. "IOS XR" before "IOS", "NX-OS" before "Nexus").
 */
function _vendorFromText(text) {
  // Juniper
  if (text.includes('junos'))                          return 'juniper_junos';
  // Arista
  if (text.includes('arista') || text.includes('eos version')) return 'arista_eos';
  // Cisco NX-OS (before generic IOS)
  if (text.includes('nx-os') || text.includes('nxos') ||
      text.includes('nexus'))                          return 'cisco_nxos';
  // Cisco IOS-XR
  if (text.includes('ios xr') || text.includes('iosxr')) return 'cisco_iosxr';
  // Cisco IOS-XE
  if (text.includes('ios-xe') || text.includes('iosxe')) return 'cisco_iosxe';
  // Cisco IOS (generic — must come after XR/XE/NX-OS)
  if (text.includes('cisco ios') || text.includes('ios software')) return 'cisco_ios';
  // Palo Alto
  if (text.includes('panos') || text.includes('palo alto')) return 'paloalto_panos';
  // Fortinet
  if (text.includes('fortios') || text.includes('fortigate')) return 'fortinet_fortios';
  // VyOS
  if (text.includes('vyos'))                           return 'vyos';
  // Linux
  if (text.includes('ubuntu') || text.includes('debian') ||
      text.includes('centos') || text.includes('red hat') ||
      text.includes('linux'))                          return 'linux';

  return null;
}

// ── Block context menu ──────────────────────────────────────
const blockCtx = {
  _menu: null,
  _activeBlockId: null,
  _activeGutterMark: null,

  show(x, y, tabId, blockId) {
    const bext = diag.snifferBlocks[tabId];
    if (!bext) return;
    const block = bext.blocks.find(b => b.blockId === blockId);
    if (!block) return;

    this.hide();
    this._activeBlockId = blockId;

    const menu = document.createElement('div');
    menu.className = 'block-ctx-menu';

    const sizeStr = block.byteCount >= 1024
      ? (block.byteCount / 1024).toFixed(1) + 'KB'
      : block.byteCount + 'B';
    const ts = new Date(block.timestamp).toTimeString().slice(0, 8);

    menu.innerHTML = `
      <div class="ctx-header">
        <div class="ctx-cmd" title="${escapeHtml(block.command)}">${escapeHtml(block.command)}</div>
        <div class="ctx-meta">${block.lineCount} lines · ${sizeStr} · ${block.durationMs}ms · ${ts}</div>
      </div>
      <div class="ctx-item ctx-cyan" data-action="visualize">
        <span class="ctx-icon">◈</span>Visualize${block._parseCache ? `<span class="ctx-badge">${block._parseCache.provider || 'cached'}</span>` : ''}
      </div>
      <div class="ctx-item" data-action="copy">
        <span class="ctx-icon">⊡</span>Copy Block
      </div>
      <div class="ctx-item" data-action="copy-cmd">
        <span class="ctx-icon">⊡</span>Copy Command
      </div>
      <div class="ctx-sep"></div>
      <div class="ctx-item" data-action="export-json">
        <span class="ctx-icon">⟐</span>Export as JSON
      </div>
      <div class="ctx-item" data-action="export-csv">
        <span class="ctx-icon">⟐</span>Export as CSV
      </div>
      <div class="ctx-item" data-action="export-md">
        <span class="ctx-icon">⟐</span>Export as Markdown
      </div>
      <div class="ctx-sep"></div>
      <div class="ctx-item ctx-yellow" data-action="rerun">
        <span class="ctx-icon">↻</span>Re-run Command
      </div>
    `;

    document.body.appendChild(menu);
    const rect = menu.getBoundingClientRect();
    const finalX = (x + rect.width > window.innerWidth) ? window.innerWidth - rect.width - 8 : x;
    const finalY = (y + rect.height > window.innerHeight) ? window.innerHeight - rect.height - 8 : y;
    menu.style.left = finalX + 'px';
    menu.style.top = finalY + 'px';

    menu.addEventListener('click', (e) => {
      const item = e.target.closest('.ctx-item');
      if (!item || item.classList.contains('ctx-disabled')) return;
      const action = item.dataset.action;
      this._exec(action, tabId, block);
      this.hide();
    });

    menu.addEventListener('mousedown', (e) => e.stopPropagation());
    this._menu = menu;
  },

  hide() {
    if (this._menu) {
      this._menu.remove();
      this._menu = null;
    }
    if (this._activeGutterMark) {
      this._activeGutterMark.classList.remove('gutter-active');
      this._activeGutterMark = null;
    }
    this._activeBlockId = null;
  },

  _exec(action, tabId, block) {
    switch (action) {
      case 'visualize': {
        if (block._parseCache) {
          openVisualizer(tabId, block, block._parseCache);
          return;
        }

        this._toast('Parsing...');
        perf.start('parse_api');

        const vendor = getVendorHint(tabId);
        const payload = {
          command: block.command,
          output: block.output,
          vendor_hint: vendor,
        };

        fetch('/api/parse', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(payload),
        })
        .then(r => {
          if (!r.ok) throw new Error(`HTTP ${r.status}`);
          return r.json();
        })
        .then(result => {
          perf.end('parse_api');
          block._parseCache = result;
          openVisualizer(tabId, block, result);
        })
        .catch(err => {
          perf.end('parse_api');
          this._toast('Parse failed: ' + err.message, true);
        });
        break;
      }

      case 'copy':
        this._copyText(block.output, 'Block copied · ' + block.lineCount + ' lines');
        break;

      case 'copy-cmd':
        this._copyText(block.command, 'Command copied: ' + block.command);
        break;

      case 'export-json': {
        const obj = {
          command: block.command,
          output: block.outputLines,
          metadata: {
            lineCount: block.lineCount,
            byteCount: block.byteCount,
            durationMs: block.durationMs,
            timestamp: new Date(block.timestamp).toISOString(),
            interrupted: block.interrupted,
            prompt: block.prompt,
          },
        };
        this._download(JSON.stringify(obj, null, 2),
          this._filename(block.command, 'json'), 'application/json');
        this._toast('Exported JSON · ' + block.lineCount + ' lines');
        break;
      }

      case 'export-csv': {
        let csv = 'line_num,text\n';
        block.outputLines.forEach((line, i) => {
          csv += (i + 1) + ',"' + line.replace(/"/g, '""') + '"\n';
        });
        this._download(csv, this._filename(block.command, 'csv'), 'text/csv');
        this._toast('Exported CSV · ' + block.lineCount + ' lines');
        break;
      }

      case 'export-md': {
        let md = '## `' + block.command + '`\n\n';
        md += '| Field | Value |\n|---|---|\n';
        md += '| Lines | ' + block.lineCount + ' |\n';
        md += '| Size | ' + block.byteCount + 'B |\n';
        md += '| Duration | ' + block.durationMs + 'ms |\n';
        md += '| Timestamp | ' + new Date(block.timestamp).toISOString() + ' |\n\n';
        md += '```\n' + block.output + '\n```\n';
        this._download(md, this._filename(block.command, 'md'), 'text/markdown');
        this._toast('Exported Markdown · ' + block.lineCount + ' lines');
        break;
      }

      case 'rerun': {
        const session = state.sessions[tabId];
        if (session?.socket?.readyState === WebSocket.OPEN) {
          session.socket.send(JSON.stringify({ type: 'input', data: block.command + '\r' }));
          this._toast('Re-sent: ' + block.command);
        } else {
          this._toast('Session not connected', true);
        }
        break;
      }
    }
  },

  _filename(command, ext) {
    const slug = command.replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_|_$/g, '').substring(0, 40);
    const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
    return slug + '_' + ts + '.' + ext;
  },

  _download(content, filename, mimeType) {
    const blob = new Blob([content], { type: mimeType });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  },

  _toast(msg, isError) {
    const el = document.getElementById('ctx-toast');
    el.textContent = msg;
    el.style.color = isError ? 'var(--red)' : 'var(--cyan)';
    el.classList.add('show');
    clearTimeout(this._toastTimer);
    this._toastTimer = setTimeout(() => el.classList.remove('show'), 2200);
  },

  /** Clipboard write with execCommand fallback for QWebEngineView */
  _copyText(text, successMsg) {
    const fallback = () => {
      const ta = document.createElement('textarea');
      ta.value = text;
      ta.style.cssText = 'position:fixed;left:-9999px;top:-9999px;opacity:0;';
      document.body.appendChild(ta);
      ta.select();
      try {
        document.execCommand('copy');
        this._toast(successMsg);
      } catch (_) {
        this._toast('Copy failed — check permissions', true);
      }
      document.body.removeChild(ta);
    };
    try {
      if (navigator.clipboard?.writeText) {
        navigator.clipboard.writeText(text).then(
          () => this._toast(successMsg),
          () => fallback()
        );
      } else {
        fallback();
      }
    } catch (_) {
      fallback();
    }
  },
};

// Global click → close context menu
document.addEventListener('mousedown', () => blockCtx.hide());

// ── Visualizer overlay ──────────────────────────────────────
function openVisualizer(tabId, block, result) {
  perf.start('render_visualizer');

  const existing = document.querySelector('.viz-overlay');
  if (existing) existing.remove();

  const overlay = document.createElement('div');
  overlay.className = 'viz-overlay';

  const confClass = 'conf-' + (result.confidence || 'none');
  const confLabel = (result.confidence || 'none').toUpperCase();

  let metaHtml = `<span class="viz-confidence ${confClass}">${confLabel}</span>`;
  if (result.provider) {
    metaHtml += `<span class="viz-meta-item">Provider: <span>${result.provider}</span></span>`;
  }
  if (result.template) {
    metaHtml += `<span class="viz-meta-item">Template: <span>${result.template}</span></span>`;
  }
  metaHtml += `<span class="viz-meta-item">Score: <span>${result.score}/100</span></span>`;
  metaHtml += `<span class="viz-meta-item">Records: <span>${result.record_count || 0}</span></span>`;
  metaHtml += `<span class="viz-meta-item">Time: <span>${result.chain_time_ms || result.parse_time_ms || 0}ms</span></span>`;
  if (result.short_circuited) {
    metaHtml += `<span class="viz-meta-item" style="color:var(--green)">⚡ short-circuited</span>`;
  }

  let bodyHtml = '';
  const records = result.records || [];
  const fields = result.fields || [];

  if (records.length > 0 && fields.length > 0) {
    let tableHtml = '<table class="viz-table"><thead><tr>';
    tableHtml += fields.map(f => `<th>${escapeHtml(f)}</th>`).join('');
    tableHtml += '</tr></thead><tbody>';
    for (const rec of records) {
      tableHtml += '<tr>';
      tableHtml += fields.map(f => {
        const val = rec[f];
        const display = (val === null || val === undefined || val === '') ? '—' : String(val);
        return `<td title="${escapeHtml(display)}">${escapeHtml(display)}</td>`;
      }).join('');
      tableHtml += '</tr>';
    }
    tableHtml += '</tbody></table>';
    bodyHtml = tableHtml;
  } else if (result.error) {
    bodyHtml = `<div class="viz-empty">
      <div style="color:var(--red);margin-bottom:6px">${escapeHtml(result.error)}</div>
      <div>Raw block text is still available via Copy Block and Export.</div>
    </div>`;
  } else {
    bodyHtml = `<div class="viz-empty">
      <div style="margin-bottom:6px">No template matched this output.</div>
      <div>Copy Block and Export are still available from the context menu.</div>
    </div>`;
  }

  let competingHtml = '';
  if (result.competing && Object.keys(result.competing).length > 0) {
    const entries = Object.entries(result.competing)
      .sort(([,a],[,b]) => (b.score || 0) - (a.score || 0));
    competingHtml = entries.map(([name, info]) => {
      if (info.error) return `<span style="color:var(--text-dim)">${name}: ${info.error}</span>`;
      return `<span style="color:var(--text-dim)">${name}: ${info.score}${info.template ? ' (' + info.template.split('/').pop() + ')' : ''}</span>`;
    }).join(' · ');
  }

  overlay.innerHTML = `
    <div class="viz-panel">
      <div class="viz-header">
        <span class="viz-cmd" title="${escapeHtml(block.command)}">${escapeHtml(block.command)}</span>
        <button class="viz-close" title="Close (Esc)">✕</button>
      </div>
      <div class="viz-meta">${metaHtml}</div>
      <div class="viz-body">${bodyHtml}</div>
      <div class="viz-footer">
        ${records.length > 0 ? `
          <button data-viz-action="copy-table">Copy Table</button>
          <button data-viz-action="export-json">Export JSON</button>
          <button data-viz-action="export-csv">Export CSV</button>
        ` : ''}
        <span class="viz-spacer"></span>
        <span class="viz-stats">${competingHtml}</span>
      </div>
    </div>
  `;

  document.body.appendChild(overlay);

  const closeViz = () => overlay.remove();

  overlay.querySelector('.viz-close').addEventListener('click', closeViz);
  overlay.addEventListener('click', (e) => {
    if (e.target === overlay) closeViz();
  });

  const escHandler = (e) => {
    if (e.key === 'Escape') {
      closeViz();
      document.removeEventListener('keydown', escHandler);
    }
  };
  document.addEventListener('keydown', escHandler);

  // Export buttons
  overlay.addEventListener('click', (e) => {
    const btn = e.target.closest('[data-viz-action]');
    if (!btn) return;
    const vizAction = btn.dataset.vizAction;

    if (vizAction === 'copy-table') {
      if (fields.length === 0 || records.length === 0) return;

      // ── Rich HTML table for Word / Outlook / Google Docs ──
      let html = '<table style="border-collapse:collapse;font-family:Calibri,Arial,sans-serif;font-size:11pt;">';
      html += '<thead><tr>';
      for (const f of fields) {
        html += '<th style="border:1px solid #999;padding:4px 8px;background:#e8e8e8;font-weight:bold;text-align:left;">'
          + escapeHtml(f) + '</th>';
      }
      html += '</tr></thead><tbody>';
      for (let i = 0; i < records.length; i++) {
        const bg = i % 2 === 0 ? '#ffffff' : '#f5f5f5';
        html += '<tr>';
        for (const f of fields) {
          const v = records[i][f];
          const s = (v === null || v === undefined || v === '') ? '\u2014' : String(v);
          html += '<td style="border:1px solid #ccc;padding:3px 8px;background:' + bg + ';">'
            + escapeHtml(s) + '</td>';
        }
        html += '</tr>';
      }
      html += '</tbody></table>';

      // ── Plain text pipe table fallback ──
      const widths = fields.map(f =>
        Math.max(f.length, ...records.map(r => {
          const v = r[f];
          return (v === null || v === undefined || v === '') ? 1 : String(v).length;
        }))
      );
      let plain = '| ' + fields.map((f, i) => f.padEnd(widths[i])).join(' | ') + ' |\n';
      plain += '| ' + widths.map(w => '-'.repeat(w)).join(' | ') + ' |\n';
      for (const rec of records) {
        plain += '| ' + fields.map((f, i) => {
          const v = rec[f];
          const s = (v === null || v === undefined || v === '') ? '\u2014' : String(v);
          return s.padEnd(widths[i]);
        }).join(' | ') + ' |\n';
      }

      // ── Write both MIME types to clipboard ──
      // QWebEngineView may not support ClipboardItem / navigator.clipboard,
      // so we chain: ClipboardItem → writeText → execCommand('copy')
      const _execCopyFallback = (text) => {
        const ta = document.createElement('textarea');
        ta.value = text;
        ta.style.cssText = 'position:fixed;left:-9999px;top:-9999px;opacity:0;';
        document.body.appendChild(ta);
        ta.select();
        try {
          document.execCommand('copy');
          blockCtx._toast('Table copied (plain text) \u00b7 ' + records.length + ' records');
        } catch (_) {
          blockCtx._toast('Copy failed \u2014 check permissions', true);
        }
        document.body.removeChild(ta);
      };

      try {
        if (typeof ClipboardItem !== 'undefined' && navigator.clipboard?.write) {
          const htmlBlob = new Blob([html], { type: 'text/html' });
          const textBlob = new Blob([plain], { type: 'text/plain' });
          navigator.clipboard.write([
            new ClipboardItem({ 'text/html': htmlBlob, 'text/plain': textBlob })
          ]).then(
            () => blockCtx._toast('Table copied \u00b7 ' + records.length + ' records'),
            () => {
              // ClipboardItem failed, try writeText
              if (navigator.clipboard?.writeText) {
                navigator.clipboard.writeText(plain).then(
                  () => blockCtx._toast('Table copied (plain text) \u00b7 ' + records.length + ' records'),
                  () => _execCopyFallback(plain)
                );
              } else {
                _execCopyFallback(plain);
              }
            }
          );
        } else if (navigator.clipboard?.writeText) {
          navigator.clipboard.writeText(plain).then(
            () => blockCtx._toast('Table copied (plain text) \u00b7 ' + records.length + ' records'),
            () => _execCopyFallback(plain)
          );
        } else {
          _execCopyFallback(plain);
        }
      } catch (e) {
        _execCopyFallback(plain);
      }
      return;
    }

    if (vizAction === 'export-json') {
      const obj = {
        command: block.command,
        provider: result.provider,
        template: result.template,
        score: result.score,
        confidence: result.confidence,
        records: records,
        fields: fields,
        record_count: result.record_count,
      };
      blockCtx._download(JSON.stringify(obj, null, 2),
        blockCtx._filename(block.command + '_parsed', 'json'), 'application/json');
      blockCtx._toast('Exported parsed JSON · ' + records.length + ' records');
    }

    if (vizAction === 'export-csv') {
      if (fields.length === 0 || records.length === 0) return;
      let csv = fields.map(f => '"' + f.replace(/"/g, '""') + '"').join(',') + '\n';
      for (const rec of records) {
        csv += fields.map(f => {
          const v = rec[f];
          const s = (v === null || v === undefined) ? '' : String(v);
          return '"' + s.replace(/"/g, '""') + '"';
        }).join(',') + '\n';
      }
      blockCtx._download(csv,
        blockCtx._filename(block.command + '_parsed', 'csv'), 'text/csv');
      blockCtx._toast('Exported parsed CSV · ' + records.length + ' records');
    }
  });

  perf.end('render_visualizer');
}