// dashboard/js/render-pinned.js — Pinned context notes rendering and management

function renderPinned(entries) {
  entries = (entries || []).filter(function(e) { return !isDeleted('pin:' + e.title); });
  const el = document.getElementById('pinned-content');
  if (!el) return;
  if (entries.length === 0) {
    el.innerHTML = '<p class="empty">No pinned notes. Pin important context that all agents should see.</p>';
    return;
  }
  // Store entries for click-to-view (full content available in status response)
  window._pinnedEntries = entries;
  // eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml()/renderMd() (fields: title, content)
  el.innerHTML = entries.map((e, i) =>
    '<div class="pinned-card" data-file="pinned:' + escHtml(e.title) + '" style="padding:8px 12px;margin-bottom:6px;background:var(--surface2);border-left:3px solid ' +
      (e.level === 'critical' ? 'var(--red)' : e.level === 'warning' ? 'var(--yellow)' : 'var(--blue)') +
      ';border-radius:4px;cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openPinnedView(' + i + ')">' +
      '<div style="display:flex;justify-content:space-between;align-items:center">' +
        '<strong style="font-size:var(--text-md)">' + escHtml(e.title) + '</strong>' +
        '<button class="btn-destructive" data-pin-title="' + escHtml(e.title) + '" onclick="event.stopPropagation();removePinnedNote(this.dataset.pinTitle)">Unpin</button>' +
      '</div>' +
      '<div style="font-size:var(--text-sm);color:var(--muted);margin-top:4px">' + renderMd(e.content.slice(0, 200)) + (e.content.length > 200 ? '...' : '') + '</div>' +
    '</div>'
  ).join('');
}

function openPinNoteModal() {
  document.getElementById('modal-title').textContent = 'Pin a Note';
  // The shared #modal-body is reused across every modal, and other openers
  // (e.g. the pinned-note view modal, inbox edit/steer mode) leave inline
  // font-family / white-space / padding / border styles on it. Without this
  // reset the create form inherits whichever modal opened last, distorting
  // its padding and font. Clear those inline styles so the dialog always
  // renders against the default .modal-body CSS — mirror of openPinnedEdit.
  const modalBody = document.getElementById('modal-body');
  modalBody.style.fontFamily = '';
  modalBody.style.whiteSpace = '';
  modalBody.style.padding = '';
  modalBody.style.border = '';
  modalBody.style.borderRadius = '';
  const qa = document.getElementById('modal-qa'); if (qa) qa.style.display = 'none';
  modalBody.innerHTML =
    '<div style="display:flex;flex-direction:column;gap:12px">' +
      '<label style="color:var(--text);font-size:var(--text-md)">Title<input id="pin-title" style="display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text)" placeholder="e.g. API freeze until Friday"></label>' +
      '<label style="color:var(--text);font-size:var(--text-md)">Content<textarea id="pin-content" rows="4" style="display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);resize:vertical;font-family:inherit" placeholder="Context all agents should see..."></textarea></label>' +
      '<label style="color:var(--text);font-size:var(--text-md)">Level<select id="pin-level" style="display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text)"><option value="info">Info</option><option value="warning">Warning</option><option value="critical">Critical</option></select></label>' +
      '<div style="display:flex;justify-content:flex-end;gap:8px"><button onclick="closeModal()" class="pr-pager-btn">Cancel</button><button onclick="submitPinnedNote(event)" class="btn-primary-lg">Pin</button></div>' +
    '</div>';
  document.getElementById('modal').classList.add('open');
}

async function submitPinnedNote(e) {
  var btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = 'Pinning...'; }
  const title = document.getElementById('pin-title').value;
  const content = document.getElementById('pin-content').value;
  const level = document.getElementById('pin-level').value;
  if (!title || !content) { if (btn) { btn.disabled = false; btn.textContent = 'Pin'; } alert('Title and content required'); return; }
  try { closeModal(); } catch { /* may not be open */ }

  // Optimistic render: append the new entry to the pinned list and re-render immediately
  // so it appears without waiting for the POST round-trip or the next status refresh (closes #1295).
  // Snapshot prevEntries so we can revert on failure.
  const prevEntries = Array.isArray(window._pinnedEntries) ? window._pinnedEntries.slice() : [];
  const newEntry = { title, content, level: level || 'info' };
  const nextEntries = prevEntries.concat([newEntry]);
  window._pinnedEntries = nextEntries;
  try { renderPinned(nextEntries); } catch { /* DOM may be missing — non-fatal */ }
  showToast('cmd-toast', 'Note pinned', true);

  try {
    const res = await fetch('/api/pinned', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title, content, level }) });
    if (res.ok) {
      // Reconcile with server state (captures the normalised entry shape from parsePinnedEntries).
      refresh();
    } else {
      // Revert optimistic update and surface the error.
      window._pinnedEntries = prevEntries;
      try { renderPinned(prevEntries); } catch { /* ignore */ }
      const d = await res.json().catch(() => ({}));
      showToast('cmd-toast', 'Pin failed: ' + (d.error || 'unknown'), false);
      openPinNoteModal();
    }
  } catch (err) {
    // Network failure — revert optimistic update.
    window._pinnedEntries = prevEntries;
    try { renderPinned(prevEntries); } catch { /* ignore */ }
    showToast('cmd-toast', 'Error: ' + err.message, false);
    openPinNoteModal();
  }
}

async function removePinnedNote(title) {
  // Capture the trigger before awaiting so confirmDialog can restore focus
  // to the actual Unpin button (window.event is cleared by the time the
  // dialog resolves).
  const btn = (window.event)?.target;
  const ok = await confirmDialog({
    title: 'Unpin context?',
    message: 'This will remove "' + title + '" from your pinned context. You can re-pin it anytime.',
    confirmLabel: 'Unpin',
    cancelLabel: 'Cancel',
    danger: false,
  });
  if (!ok) return;
  showToast('cmd-toast', 'Note unpinned', true);
  markDeleted('pin:' + title);
  if (btn) { const card = btn.closest('.pinned-card') || btn.parentElement?.parentElement; if (card) card.remove(); }
  try {
    const res = await fetch('/api/pinned/remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title }) });
    if (!res.ok) { clearDeleted('pin:' + title); showToast('cmd-toast', 'Unpin failed', false); refresh(); }
  } catch (e) { clearDeleted('pin:' + title); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
}

function openPinnedView(idx) {
  const entry = (window._pinnedEntries || [])[idx];
  if (!entry) return;
  document.getElementById('modal-title').textContent = entry.title;
  // Content and Edit button share one top-aligned flex row: rendered text fills
  // the left (flex:1, starting at the top), the Edit button sits top-right — so
  // both read as anchored to the top of the dialog body.
  // eslint-disable-next-line no-unsanitized/property -- reason: renderMd() escapes all user-controlled fields; the wrapper + Edit button are string literals with a numeric idx
  document.getElementById('modal-body').innerHTML =
    '<div style="display:flex;align-items:flex-start;gap:12px">' +
      '<div style="flex:1;min-width:0">' + renderMd(entry.content) + '</div>' +
      '<button class="pr-pager-btn" style="flex:none" onclick="openPinnedEdit(' + idx + ')">Edit</button>' +
    '</div>';
  document.getElementById('modal-body').style.fontFamily = "'Segoe UI', system-ui, sans-serif";
  document.getElementById('modal-body').style.whiteSpace = 'normal';
  document.getElementById('modal').classList.add('open');
  _modalDocContext = { title: entry.title, content: entry.content, selection: '' };
  _modalFilePath = 'pinned.md';
  showModalQa();
}

// Edit an existing pinned note. Opened from the "Edit" button in the view
// modal. The form is DOM-built (not innerHTML) so pre-filled user content
// can't inject markup, and the document-Q&A panel is hidden while editing.
function openPinnedEdit(idx) {
  const entry = (window._pinnedEntries || [])[idx];
  if (!entry) return;
  const originalTitle = entry.title;
  document.getElementById('modal-title').textContent = 'Edit Pinned Note';
  const body = document.getElementById('modal-body');
  // The view modal sets a serif font + normal whitespace for rendered markdown;
  // reset so the form inputs use the default UI font.
  body.style.fontFamily = '';
  body.style.whiteSpace = '';
  // Hide the doc-Q&A panel the view modal opened — irrelevant while editing.
  const qa = document.getElementById('modal-qa'); if (qa) qa.style.display = 'none';

  const inputStyle = 'display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text)';
  function field(labelText) {
    const label = document.createElement('label');
    label.style.cssText = 'color:var(--text);font-size:var(--text-md)';
    label.appendChild(document.createTextNode(labelText));
    return label;
  }

  const wrap = document.createElement('div');
  wrap.style.cssText = 'display:flex;flex-direction:column;gap:12px';

  const titleLabel = field('Title');
  const titleInput = document.createElement('input');
  titleInput.id = 'pin-edit-title';
  titleInput.style.cssText = inputStyle;
  titleInput.value = entry.title || '';
  titleLabel.appendChild(titleInput);

  const contentLabel = field('Content');
  const contentInput = document.createElement('textarea');
  contentInput.id = 'pin-edit-content';
  contentInput.rows = 6;
  contentInput.style.cssText = inputStyle + ';resize:vertical;font-family:inherit';
  contentInput.value = entry.content || '';
  contentLabel.appendChild(contentInput);

  const levelLabel = field('Level');
  const levelSelect = document.createElement('select');
  levelSelect.id = 'pin-edit-level';
  levelSelect.style.cssText = inputStyle;
  [['info', 'Info'], ['warning', 'Warning'], ['critical', 'Critical']].forEach(function(o) {
    const opt = document.createElement('option');
    opt.value = o[0];
    opt.textContent = o[1];
    if ((entry.level || 'info') === o[0]) opt.selected = true;
    levelSelect.appendChild(opt);
  });
  levelLabel.appendChild(levelSelect);

  const actions = document.createElement('div');
  actions.style.cssText = 'display:flex;justify-content:flex-end;gap:8px';
  const cancelBtn = document.createElement('button');
  cancelBtn.className = 'pr-pager-btn';
  cancelBtn.textContent = 'Cancel';
  cancelBtn.onclick = function() { openPinnedView(idx); };
  const saveBtn = document.createElement('button');
  saveBtn.className = 'btn-primary-lg';
  saveBtn.textContent = 'Save';
  saveBtn.onclick = function(ev) { submitPinnedEdit(originalTitle, ev); };
  actions.appendChild(cancelBtn);
  actions.appendChild(saveBtn);

  wrap.appendChild(titleLabel);
  wrap.appendChild(contentLabel);
  wrap.appendChild(levelLabel);
  wrap.appendChild(actions);
  body.replaceChildren(wrap);
  document.getElementById('modal').classList.add('open');
}

async function submitPinnedEdit(originalTitle, e) {
  const btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; }
  const title = (document.getElementById('pin-edit-title').value || '').trim();
  const content = document.getElementById('pin-edit-content').value;
  const level = document.getElementById('pin-edit-level').value;
  if (!title || !content) { if (btn) { btn.disabled = false; btn.textContent = 'Save'; } alert('Title and content required'); return; }
  try { closeModal(); } catch { /* may not be open */ }

  // Optimistic update: swap the edited entry in window._pinnedEntries (keyed on
  // the original title) and re-render immediately. Snapshot to revert on failure.
  const prevEntries = Array.isArray(window._pinnedEntries) ? window._pinnedEntries.slice() : [];
  const nextEntries = prevEntries.map(en => en.title === originalTitle ? { title, content, level: level || 'info' } : en);
  window._pinnedEntries = nextEntries;
  try { renderPinned(nextEntries); } catch { /* DOM may be missing — non-fatal */ }
  showToast('cmd-toast', 'Note updated', true);

  try {
    const res = await fetch('/api/pinned/update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ originalTitle, title, content, level }) });
    if (res.ok) {
      refresh();
    } else {
      window._pinnedEntries = prevEntries;
      try { renderPinned(prevEntries); } catch { /* ignore */ }
      const d = await res.json().catch(() => ({}));
      showToast('cmd-toast', 'Update failed: ' + (d.error || 'unknown'), false);
    }
  } catch (err) {
    window._pinnedEntries = prevEntries;
    try { renderPinned(prevEntries); } catch { /* ignore */ }
    showToast('cmd-toast', 'Error: ' + err.message, false);
  }
}

window.MinionsPinned = { renderPinned, openPinNoteModal, submitPinnedNote, removePinnedNote, openPinnedView, openPinnedEdit, submitPinnedEdit };
