// kb-query frontend -- module 5 phase 4 (docs/kb/modules/05-faza4-plan.md §7). Vanilla JS, no
// build step (plan §2 decision 4): fetch()s /search, renders results grouped by envelope_id.
//
// Threshold rule (plan §7, task spec): dist < 0.45 green, 0.45-0.55 yellow (still rendered with
// a visual warning), > 0.55 never rendered as an individual result. If a query ends up with zero
// renderable chunks (every hit > 0.55, or no hits at all), the whole group list is replaced by
// one message instead -- "brak odpowiedzi w KB", carrying the best (lowest) dist seen so the
// user can tell a near-miss from nothing at all.
(function () {
  'use strict';

  const GREEN_MAX = 0.45;
  const YELLOW_MAX = 0.55;

  function buildSearchUrl(query, mode) {
    const params = new URLSearchParams({ q: query, mode: mode });
    return '/search?' + params.toString();
  }

  function distClass(dist) {
    if (dist < GREEN_MAX) return 'dist-green';
    if (dist <= YELLOW_MAX) return 'dist-yellow';
    return 'dist-red';
  }

  // Flat /search results -> per-envelope groups, chunks sorted best-first within each group.
  // Insertion order follows first appearance, which already tracks best-group-first since the
  // API's own result list is globally dist-sorted (kb_retrieval.cascade_retrieve/flat_retrieve).
  function groupResults(results) {
    const groups = [];
    const byId = new Map();
    for (const r of results) {
      let group = byId.get(r.envelope_id);
      if (!group) {
        group = {
          envelopeId: r.envelope_id,
          source: r.source,
          summary: r.summary,
          summaryTags: r.summary_tags || [],
          link: r.link,
          mailUiUrl: r.mail_ui_url,
          subject: r.subject,
          from: r.from,
          date: r.date,
          chunks: [],
        };
        byId.set(r.envelope_id, group);
        groups.push(group);
      }
      group.chunks.push(r);
    }
    for (const group of groups) {
      group.chunks.sort((a, b) => a.dist - b.dist);
    }
    return groups;
  }

  function copyToClipboard(text) {
    if (navigator.clipboard && window.isSecureContext) {
      return navigator.clipboard.writeText(text);
    }
    // Fallback for plain-http LAN access (no secure context -> Clipboard API unavailable).
    const el = document.createElement('textarea');
    el.value = text;
    el.style.position = 'fixed';
    el.style.opacity = '0';
    document.body.appendChild(el);
    el.select();
    try {
      document.execCommand('copy');
    } finally {
      document.body.removeChild(el);
    }
    return Promise.resolve();
  }

  function renderSolStatus(el, status) {
    el.textContent = 'SOLARIA: ' + (status === 'up' ? 'online' : 'offline (fallback embed)');
    el.className = 'sol-status ' + (status === 'up' ? 'sol-up' : 'sol-down');
  }

  function renderChunk(chunk) {
    const details = document.createElement('details');
    details.className = 'chunk ' + distClass(chunk.dist);
    const summaryEl = document.createElement('summary');
    summaryEl.textContent = 'Fragment ' + chunk.chunk_index + ' (dist ' + chunk.dist.toFixed(4) + ')';
    const body = document.createElement('pre');
    body.className = 'chunk-text';
    body.textContent = chunk.text;
    details.appendChild(summaryEl);
    details.appendChild(body);
    return details;
  }

  function renderGroup(group) {
    const article = document.createElement('article');
    article.className = 'result-group';

    const header = document.createElement('h3');
    header.textContent = group.summary || group.envelopeId;
    article.appendChild(header);

    if (group.summaryTags.length) {
      const tags = document.createElement('div');
      tags.className = 'tags';
      tags.textContent = group.summaryTags.join(', ');
      article.appendChild(tags);
    }

    const meta = document.createElement('div');
    meta.className = 'source-meta';
    if (group.source === 'paperless' && group.link) {
      const a = document.createElement('a');
      a.href = group.link;
      a.target = '_blank';
      a.rel = 'noopener';
      a.textContent = 'Otwórz w Paperless';
      meta.appendChild(a);
    } else if (group.source === 'gmail') {
      const info = document.createElement('span');
      info.textContent = [group.subject, group.from, group.date].filter(Boolean).join(' — ');
      meta.appendChild(info);

      const btn = document.createElement('button');
      btn.type = 'button';
      btn.textContent = 'Kopiuj Message-ID';
      btn.addEventListener('click', function () {
        copyToClipboard(group.envelopeId).then(function () {
          btn.textContent = 'Skopiowano!';
          setTimeout(function () {
            btn.textContent = 'Kopiuj Message-ID';
          }, 1500);
        });
      });
      meta.appendChild(btn);
    }
    if (meta.childNodes.length) {
      article.appendChild(meta);
    }

    for (const chunk of group.chunks) {
      article.appendChild(renderChunk(chunk));
    }
    return article;
  }

  function render(container, data) {
    container.innerHTML = '';

    let bestDist = null;
    for (const r of data.results) {
      if (bestDist === null || r.dist < bestDist) bestDist = r.dist;
    }

    const groups = groupResults(data.results);
    let anyVisible = false;
    for (const group of groups) {
      group.chunks = group.chunks.filter(function (c) {
        return c.dist <= YELLOW_MAX;
      });
      if (group.chunks.length) anyVisible = true;
    }

    if (!anyVisible) {
      const msg = document.createElement('p');
      msg.className = 'no-answer';
      msg.textContent = bestDist === null
        ? 'Brak odpowiedzi w KB dla tego zapytania.'
        : 'Brak odpowiedzi w KB dla tego zapytania (najlepszy dist: ' + bestDist.toFixed(4) + ').';
      container.appendChild(msg);
      return;
    }

    for (const group of groups) {
      if (group.chunks.length) {
        container.appendChild(renderGroup(group));
      }
    }
  }

  function init() {
    const form = document.getElementById('search-form');
    const queryInput = document.getElementById('query');
    const modeToggle = document.getElementById('mode-flat');
    const results = document.getElementById('results');
    const errorBox = document.getElementById('error');
    const solStatusEl = document.getElementById('sol-status');

    async function refreshSolStatus() {
      try {
        const resp = await fetch('/healthz');
        const data = await resp.json();
        renderSolStatus(solStatusEl, data.sol_status);
      } catch (err) {
        renderSolStatus(solStatusEl, 'down');
      }
    }

    form.addEventListener('submit', async function (event) {
      event.preventDefault();
      const query = queryInput.value.trim();
      if (!query) return;
      const mode = modeToggle.checked ? 'flat' : 'cascade';

      errorBox.hidden = true;
      results.innerHTML = '<p class="loading">Szukam…</p>';

      try {
        const resp = await fetch(buildSearchUrl(query, mode));
        if (!resp.ok) {
          const body = await resp.json().catch(function () {
            return {};
          });
          throw new Error(body.detail || ('Błąd wyszukiwania (' + resp.status + ')'));
        }
        const data = await resp.json();
        render(results, data);
        renderSolStatus(solStatusEl, data.sol_status);
      } catch (err) {
        results.innerHTML = '';
        errorBox.textContent = 'Wyszukiwanie chwilowo niedostępne: ' + err.message;
        errorBox.hidden = false;
      }
    });

    refreshSolStatus();
  }

  if (typeof module !== 'undefined' && module.exports) {
    module.exports = { buildSearchUrl, groupResults, distClass };
  } else {
    document.addEventListener('DOMContentLoaded', init);
  }
})();
