// ══════════════════════════════════════════════════════════════
// nterm — Session tree (left rail)
// ══════════════════════════════════════════════════════════════
// Depends on: state (app.js), createSession (session.js)
// Loaded last — after session.js
//
// Changes from v0.5:
// - All injected styles use CSS custom property tokens (--fs-*)
//   instead of hardcoded px values, so they scale with --ui-scale
// - Improved contrast: uses --text-muted instead of --text-dim
//   for session/folder names

const sessionTree = {
  _data: null,        // { folders, sessions }
  _container: null,   // DOM element
  _searchInput: null,
  _filter: '',

  // ── Initialize ────────────────────────────────────────────
  init() {
    this._injectStyles();
    this._buildUI();
    this.refresh();
  },

  // ── Fetch tree from backend ───────────────────────────────
  async refresh() {
    try {
      const resp = await fetch('/api/sessions/tree');
      if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
      this._data = await resp.json();

      if (!this._data.available) {
        this._renderEmpty('No session database found.', 'Use ntermqt to create sessions, or connect manually above.');
        return;
      }

      if (this._data.sessions.length === 0) {
        this._renderEmpty('No saved sessions.', 'Add sessions in ntermqt or use Quick Connect.');
        return;
      }

      this._render();
    } catch (err) {
      this._renderEmpty('Could not load sessions.', err.message);
    }
  },

  // ── Build the UI skeleton ─────────────────────────────────
  _buildUI() {
    const section = document.getElementById('session-tree-section');
    if (!section) return;

    section.innerHTML = '';

    // Header row with title and refresh button
    const header = document.createElement('div');
    header.className = 'st-header';
    header.innerHTML = `
      <h3>Sessions</h3>
      <button class="st-refresh" title="Refresh session tree">↻</button>
    `;
    header.querySelector('.st-refresh').addEventListener('click', () => this.refresh());
    section.appendChild(header);

    // Search box
    const search = document.createElement('input');
    search.type = 'text';
    search.className = 'st-search';
    search.placeholder = 'Filter sessions...';
    search.addEventListener('input', () => {
      this._filter = search.value.toLowerCase();
      this._render();
    });
    this._searchInput = search;
    section.appendChild(search);

    // Tree container
    const container = document.createElement('div');
    container.className = 'st-tree';
    this._container = container;
    section.appendChild(container);
  },

  // ── Render the tree ───────────────────────────────────────
  _render() {
    if (!this._container || !this._data) return;

    const { folders, sessions } = this._data;

    // Build lookup maps
    const folderMap = {};       // id → folder
    const childFolders = {};    // parent_id → [folder, ...]
    const folderSessions = {};  // folder_id → [session, ...]

    for (const f of folders) {
      folderMap[f.id] = f;
      const pid = f.parent_id === null ? 'root' : f.parent_id;
      if (!childFolders[pid]) childFolders[pid] = [];
      childFolders[pid].push(f);
    }

    for (const s of sessions) {
      const fid = s.folder_id === null ? 'root' : s.folder_id;
      if (!folderSessions[fid]) folderSessions[fid] = [];
      folderSessions[fid].push(s);
    }

    // Filter
    const filterActive = this._filter.length > 0;
    const matchingSessionIds = new Set();
    const matchingFolderIds = new Set();

    if (filterActive) {
      for (const s of sessions) {
        if (s.name.toLowerCase().includes(this._filter) ||
            s.hostname.toLowerCase().includes(this._filter) ||
            (s.description && s.description.toLowerCase().includes(this._filter))) {
          matchingSessionIds.add(s.id);
          // Walk up folder chain to ensure parents are visible
          let fid = s.folder_id;
          while (fid !== null && fid !== undefined) {
            matchingFolderIds.add(fid);
            fid = folderMap[fid] ? folderMap[fid].parent_id : null;
          }
        }
      }
    }

    // Recursive render
    const renderLevel = (parentId) => {
      const key = parentId === null ? 'root' : parentId;
      const subFolders = (childFolders[key] || []).sort((a, b) => a.position - b.position || a.name.localeCompare(b.name));
      const subSessions = (folderSessions[key] || []).sort((a, b) => a.position - b.position || a.name.localeCompare(b.name));
      let html = '';

      for (const folder of subFolders) {
        if (filterActive && !matchingFolderIds.has(folder.id)) continue;

        const children = renderLevel(folder.id);
        const expanded = filterActive ? true : (folder.expanded !== false);
        const expandClass = expanded ? 'st-expanded' : 'st-collapsed';
        const arrow = expanded ? '▾' : '▸';
        const count = (folderSessions[folder.id] || []).length;

        html += `<div class="st-folder ${expandClass}" data-folder-id="${folder.id}">
          <div class="st-folder-row" data-folder-id="${folder.id}">
            <span class="st-arrow">${arrow}</span>
            <span class="st-folder-icon">📁</span>
            <span class="st-folder-name">${escapeHtml(folder.name)}</span>
            <span class="st-count">${count}</span>
          </div>
          <div class="st-folder-children">${children}</div>
        </div>`;
      }

      for (const session of subSessions) {
        if (filterActive && !matchingSessionIds.has(session.id)) continue;

        const vendor = session.extras ? (session.extras.vendor || session.extras.DeviceType || '') : '';
        const vendorClass = this._vendorClass(vendor);
        const connectedClass = this._isConnected(session.hostname) ? 'st-connected' : '';
        const desc = session.description || session.hostname;

        html += `<div class="st-session ${connectedClass}" data-session-id="${session.id}" title="${escapeHtml(session.hostname)}:${session.port}${desc !== session.hostname ? '\n' + escapeHtml(desc) : ''}">
          <span class="st-session-dot ${vendorClass}">●</span>
          <span class="st-session-name">${escapeHtml(session.name)}</span>
        </div>`;
      }

      return html;
    };

    const treeHtml = renderLevel(null);

    if (filterActive && treeHtml.trim() === '') {
      this._container.innerHTML = '<div class="st-empty">No matches</div>';
    } else {
      this._container.innerHTML = treeHtml;
    }

    // Wire events
    this._container.querySelectorAll('.st-folder-row').forEach(el => {
      el.addEventListener('click', (e) => {
        const folder = el.closest('.st-folder');
        if (!folder) return;
        const isExpanded = folder.classList.contains('st-expanded');
        folder.classList.toggle('st-expanded', !isExpanded);
        folder.classList.toggle('st-collapsed', isExpanded);
        el.querySelector('.st-arrow').textContent = isExpanded ? '▸' : '▾';
      });
    });

    this._container.querySelectorAll('.st-session').forEach(el => {
      // Single click → populate Quick Connect form
      el.addEventListener('click', () => {
        const sessionId = parseInt(el.dataset.sessionId);
        this._selectSession(sessionId);
      });

      // Double click → populate and connect
      el.addEventListener('dblclick', () => {
        const sessionId = parseInt(el.dataset.sessionId);
        this._connectSession(sessionId);
      });

      // Right-click → context menu
      el.addEventListener('contextmenu', (e) => {
        e.preventDefault();
        const sessionId = parseInt(el.dataset.sessionId);
        this._showContextMenu(e, sessionId);
      });
    });
  },

  // ── Session actions ───────────────────────────────────────

  _selectSession(sessionId) {
    const session = this._findSession(sessionId);
    if (!session) return;

    // Highlight in tree
    this._container.querySelectorAll('.st-session').forEach(el =>
      el.classList.toggle('st-selected', parseInt(el.dataset.sessionId) === sessionId));

    // Populate Quick Connect form
    document.getElementById('f-host').value = session.hostname;
    document.getElementById('f-port').value = session.port;

    // Set vendor hint from extras if available
    const extras = session.extras || {};
    const vendorHint = this._mapVendorHint(extras.vendor || extras.DeviceType || '');
    if (vendorHint) {
      const vendorSelect = document.getElementById('f-vendor');
      for (let i = 0; i < vendorSelect.options.length; i++) {
        if (vendorSelect.options[i].value === vendorHint) {
          vendorSelect.selectedIndex = i;
          break;
        }
      }
    }

    updateStatus(`Selected: ${session.name} (${session.hostname})`);
  },

  _connectSession(sessionId) {
    const session = this._findSession(sessionId);
    if (!session) return;

    // Populate form first
    this._selectSession(sessionId);

    // Record the connection attempt on the backend
    fetch(`/api/sessions/${sessionId}/connect`, { method: 'POST' }).catch(() => {});

    // Check if we have enough info to connect
    const username = document.getElementById('f-username').value.trim();
    const password = document.getElementById('f-password').value;

    if (username && password) {
      // Auto-submit the connect form
      document.getElementById('connect-form').dispatchEvent(new Event('submit'));
    } else {
      updateStatus(`Fill in credentials for ${session.name}, then click Connect`);
      // Focus username if empty, otherwise password
      if (!username) {
        document.getElementById('f-username').focus();
      } else {
        document.getElementById('f-password').focus();
      }
    }
  },

  _findSession(sessionId) {
    if (!this._data) return null;
    return this._data.sessions.find(s => s.id === sessionId);
  },

  _isConnected(hostname) {
    return Object.values(state.sessions).some(s =>
      s.config && s.config.hostname === hostname && s.status === 'connected');
  },

  // ── Context Menu ─────────────────────────────────────────

  _activeMenu: null,
  _dismissHandlers: null,    // stored so we can removeEventListener

  _showContextMenu(event, sessionId) {
    // Kill any existing menu + its listeners first
    this._dismissContextMenu();

    const session = this._findSession(sessionId);
    if (!session) return;

    // Highlight the session
    this._container.querySelectorAll('.st-session').forEach(el =>
      el.classList.toggle('st-selected', parseInt(el.dataset.sessionId) === sessionId));

    const menu = document.createElement('div');
    menu.className = 'st-context-menu';
    menu.innerHTML = `

      <div class="st-ctx-item" data-action="telemetry">
        <span class="st-ctx-icon">📊</span> Open Telemetry
      </div>
      <div class="st-ctx-separator"></div>
      <div class="st-ctx-item" data-action="copy-host">
        <span class="st-ctx-icon">📋</span> Copy Hostname
      </div>
    `;

    menu.style.left = `${event.pageX}px`;
    menu.style.top = `${event.pageY}px`;
    document.body.appendChild(menu);

    // Keep menu in viewport
    requestAnimationFrame(() => {
      const rect = menu.getBoundingClientRect();
      if (rect.right > window.innerWidth) {
        menu.style.left = `${event.pageX - rect.width}px`;
      }
      if (rect.bottom > window.innerHeight) {
        menu.style.top = `${event.pageY - rect.height}px`;
      }
    });

    // Wire menu item actions
    menu.addEventListener('click', (e) => {
      const item = e.target.closest('.st-ctx-item');
      if (!item) return;
      const action = item.dataset.action;
      this._dismissContextMenu();

      if (action === 'connect') {
        this._connectSession(sessionId);
      } else if (action === 'telemetry') {
        this._requestTelemetry(sessionId);
      } else if (action === 'copy-host') {
        navigator.clipboard.writeText(session.hostname).then(() => {
          updateStatus(`Copied: ${session.hostname}`);
        });
      }
    });

    // ── Dismiss listeners (stored for clean removal) ────────
    // Use mousedown instead of click — fires before the click
    // that would propagate, and catches right-clicks too.
    const onMouseDown = (e) => {
      if (!menu.contains(e.target)) this._dismissContextMenu();
    };
    const onContextMenu = (e) => {
      if (!menu.contains(e.target)) this._dismissContextMenu();
    };
    const onKeyDown = (e) => {
      if (e.key === 'Escape') this._dismissContextMenu();
    };
    const onBlur = () => this._dismissContextMenu();

    // Delay attachment by one frame so the current right-click
    // event doesn't immediately trigger the mousedown handler
    requestAnimationFrame(() => {
      document.addEventListener('mousedown', onMouseDown, true);
      document.addEventListener('contextmenu', onContextMenu, true);
      document.addEventListener('keydown', onKeyDown);
      window.addEventListener('blur', onBlur);
    });

    // Store everything for cleanup
    this._activeMenu = menu;
    this._dismissHandlers = { onMouseDown, onContextMenu, onKeyDown, onBlur };
  },

  _dismissContextMenu() {
    if (this._dismissHandlers) {
      const { onMouseDown, onContextMenu, onKeyDown, onBlur } = this._dismissHandlers;
      document.removeEventListener('mousedown', onMouseDown, true);
      document.removeEventListener('contextmenu', onContextMenu, true);
      document.removeEventListener('keydown', onKeyDown);
      window.removeEventListener('blur', onBlur);
      this._dismissHandlers = null;
    }
    if (this._activeMenu) {
      this._activeMenu.remove();
      this._activeMenu = null;
    }
  },

  _requestTelemetry(sessionId) {
    const session = this._findSession(sessionId);
    if (!session) return;

    const extras = session.extras || {};
    const vendor = this._mapVendorHint(
      extras.vendor || extras.DeviceType || ''
    ) || 'arista_eos';

    const config = {
      hostname: session.hostname,
      port: session.port,
      display_name: session.name,
      vendor: vendor,
      credential_name: session.credential_name || null,
      session_id: session.id,
    };

    fetch('/api/telemetry/request', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(config),
    })
    .then(resp => {
      if (resp.ok) {
        updateStatus(
          `Telemetry requested for ${session.name} (${session.hostname})`
        );
        fetch(`/api/sessions/${sessionId}/connect`, { method: 'POST' })
          .catch(() => {});
      } else {
        updateStatus('Telemetry request failed', 'error');
      }
    })
    .catch(err => {
      updateStatus(`Telemetry request error: ${err.message}`, 'error');
    });
  },

  // ── Vendor mapping ────────────────────────────────────────

  _vendorClass(vendor) {
    if (!vendor) return '';
    const v = vendor.toLowerCase();
    if (v.includes('arista') || v === 'arista_eos') return 'st-vendor-arista';
    if (v.includes('cisco') || v.includes('ios') || v.includes('nxos')) return 'st-vendor-cisco';
    if (v.includes('juniper') || v.includes('junos')) return 'st-vendor-juniper';
    if (v.includes('palo') || v.includes('panos')) return 'st-vendor-palo';
    if (v.includes('linux')) return 'st-vendor-linux';
    return '';
  },

  _mapVendorHint(vendor) {
    if (!vendor) return '';
    const v = vendor.toLowerCase();
    if (v.includes('arista') || v === 'arista_eos') return 'arista_eos';
    if (v === 'cisco_ios' || v === 'ios') return 'cisco_ios';
    if (v.includes('nxos') || v.includes('nexus')) return 'cisco_nxos';
    if (v.includes('juniper') || v.includes('junos')) return 'juniper_junos';
    if (v.includes('palo') || v.includes('panos')) return 'paloalto_panos';
    if (v.includes('linux')) return 'linux';
    return '';
  },

  // ── Empty state ───────────────────────────────────────────

  _renderEmpty(title, detail) {
    if (!this._container) return;
    this._container.innerHTML = `<div class="st-empty">
      <div>${escapeHtml(title)}</div>
      ${detail ? `<div style="margin-top:4px">${escapeHtml(detail)}</div>` : ''}
    </div>`;
  },

  // ── Inject styles (uses CSS custom property tokens) ───────

  _injectStyles() {
    if (document.getElementById('session-tree-styles')) return;
    const style = document.createElement('style');
    style.id = 'session-tree-styles';
    style.textContent = `
      .st-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 0 0 4px 0;
      }
      .st-header h3 { margin: 0; }
      .st-refresh {
        background: none;
        border: 1px solid var(--border, #333);
        color: var(--text-dim, #666);
        cursor: pointer;
        font-size: var(--fs-md, 13px);
        padding: 2px 6px;
        border-radius: 3px;
      }
      .st-refresh:hover { color: var(--cyan, #00d4ff); border-color: var(--cyan, #00d4ff); }

      .st-search {
        width: 100%;
        padding: 5px 8px;
        margin-bottom: 6px;
        background: var(--bg-surface, #1a1a2e);
        border: 1px solid var(--border, #333);
        color: var(--text, #ccc);
        font-size: var(--fs-base, 12px);
        font-family: 'JetBrains Mono', 'Consolas', monospace;
        border-radius: 3px;
        outline: none;
        box-sizing: border-box;
      }
      .st-search:focus { border-color: var(--cyan, #00d4ff); }
      .st-search::placeholder { color: var(--text-dim, #555); }

      .st-tree {
        overflow-y: auto;
        font-size: var(--fs-base, 12px);
        line-height: 1.6;
      }

      .st-folder-row {
        display: flex;
        align-items: center;
        padding: 4px 4px;
        cursor: pointer;
        border-radius: 3px;
        gap: 4px;
        user-select: none;
      }
      .st-folder-row:hover { background: rgba(255,255,255,0.04); }
      .st-arrow {
        width: 14px;
        text-align: center;
        font-size: var(--fs-xs, 10px);
        color: var(--text-dim, #666);
      }
      .st-folder-icon { font-size: var(--fs-base, 12px); }
      .st-folder-name {
        flex: 1;
        color: var(--text, #ccc);
        font-weight: 600;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .st-count {
        color: var(--text-dim, #555);
        font-size: var(--fs-xs, 10px);
        margin-left: auto;
      }

      .st-folder-children { padding-left: 14px; }
      .st-collapsed > .st-folder-children { display: none; }

      .st-session {
        display: flex;
        align-items: center;
        padding: 4px 4px 4px 18px;
        cursor: pointer;
        border-radius: 3px;
        gap: 6px;
        user-select: none;
      }
      .st-session:hover { background: rgba(255,255,255,0.04); }
      .st-session.st-selected { background: rgba(0, 212, 255, 0.08); }

      .st-session-dot {
        font-size: var(--fs-xs, 10px);
        color: var(--text-dim, #555);
        flex-shrink: 0;
      }
      .st-session.st-connected .st-session-dot { color: var(--green, #00ff88); }
      .st-session-name {
        color: var(--text-muted, #7d92ab);
        font-size: var(--fs-base, 12px);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .st-session:hover .st-session-name { color: var(--text, #d4dce8); }
      .st-session.st-selected .st-session-name { color: var(--cyan, #00d4aa); }
      .st-session.st-connected .st-session-name { color: var(--text, #d4dce8); }

      /* Vendor dot colors */
      .st-vendor-arista { color: #4a90d9 !important; }
      .st-vendor-cisco  { color: #00bceb !important; }
      .st-vendor-juniper { color: #84b135 !important; }
      .st-vendor-palo   { color: #f04e23 !important; }
      .st-vendor-linux  { color: #fcc624 !important; }

      .st-empty {
        color: var(--text-dim, #555);
        font-size: var(--fs-sm, 11px);
        padding: 12px 4px;
        text-align: center;
      }

      /* Context menu */
      .st-context-menu {
        position: fixed;
        z-index: 9999;
        background: var(--bg-surface, #111820);
        border: 1px solid var(--border, #1e2a36);
        border-radius: 6px;
        padding: 4px 0;
        min-width: 180px;
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
        font-size: var(--fs-base, 12px);
        font-family: 'JetBrains Mono', 'Consolas', monospace;
      }
      .st-ctx-item {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 7px 14px;
        cursor: pointer;
        color: var(--text, #c8d6e5);
        white-space: nowrap;
      }
      .st-ctx-item:hover {
        background: rgba(45, 212, 191, 0.12);
        color: var(--cyan, #2dd4bf);
      }
      .st-ctx-icon {
        width: 16px;
        text-align: center;
        font-size: var(--fs-sm, 11px);
      }
      .st-ctx-separator {
        height: 1px;
        background: var(--border, #1e2a36);
        margin: 4px 8px;
      }
    `;
    document.head.appendChild(style);
  },
};

// Auto-init when DOM is ready
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', () => sessionTree.init());
} else {
  sessionTree.init();
}