/* global React, ReactDOM, Sidebar, TopBar, Panel, SynthesisHero, UserMessage, Icon, I18N, Emblem */
// ============================================================
//  AI Lawyer — App (LIVE, povezan na backend API)
// ============================================================

/* ---------- minimalni markdown -> HTML ---------- */
function mdToHtml(md) {
  if (!md) return "";
  let h = md;
  // escape
  h = h.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  // code blocks
  h = h.replace(/```([\s\S]*?)```/g, (m, c) => "<pre><code>" + c.trim() + "</code></pre>");
  // headings
  h = h.replace(/^###### (.*)$/gm, "<h6>$1</h6>")
       .replace(/^##### (.*)$/gm, "<h5>$1</h5>")
       .replace(/^#### (.*)$/gm, "<h4>$1</h4>")
       .replace(/^### (.*)$/gm, "<h3>$1</h3>")
       .replace(/^## (.*)$/gm, "<h2>$1</h2>")
       .replace(/^# (.*)$/gm, "<h1>$1</h1>");
  // bold / italic
  h = h.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
  h = h.replace(/(^|[^*])\*([^*]+)\*/g, "$1<i>$2</i>");
  // inline code
  h = h.replace(/`([^`]+)`/g, "<code>$1</code>");
  // links [text](url)
  h = h.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
  // bare URLs
  h = h.replace(/(^|[\s(])(https?:\/\/[^\s<)]+)/g, '$1<a href="$2" target="_blank" rel="noopener">$2</a>');
  // tables (preprosto: vrstice z | )
  h = h.replace(/(^\|.*\|$\n?)+/gm, (block) => {
    const rows = block.trim().split("\n").filter(r => r.trim());
    let out = "<table>";
    rows.forEach((r, idx) => {
      const cells = r.split("|").slice(1, -1).map(c => c.trim());
      if (cells.every(c => /^[-:\s]+$/.test(c))) return; // separator
      const tag = idx === 0 ? "th" : "td";
      out += "<tr>" + cells.map(c => `<${tag}>${c}</${tag}>`).join("") + "</tr>";
    });
    return out + "</table>";
  });
  // unordered lists
  h = h.replace(/(^[-*] .*$\n?)+/gm, (block) => {
    const items = block.trim().split("\n").map(l => l.replace(/^[-*]\s+/, ""));
    return "<ul>" + items.map(i => `<li>${i}</li>`).join("") + "</ul>";
  });
  // ordered lists
  h = h.replace(/(^\d+\. .*$\n?)+/gm, (block) => {
    const items = block.trim().split("\n").map(l => l.replace(/^\d+\.\s+/, ""));
    return "<ol>" + items.map(i => `<li>${i}</li>`).join("") + "</ol>";
  });
  // paragraphs (dvojni newline)
  h = h.split(/\n\n+/).map(p => {
    if (/^\s*<(h\d|ul|ol|pre|table|blockquote)/.test(p)) return p;
    return "<p>" + p.replace(/\n/g, "<br>") + "</p>";
  }).join("\n");
  return h;
}
window.mdToHtml = mdToHtml;

/* ---------- API helper ---------- */
const API = {
  async me() {
    const r = await fetch("/api/conversations", { credentials: "same-origin" });
    return r.status === 200;
  },
  async login(password) {
    const r = await fetch("/api/login", {
      method: "POST", credentials: "same-origin",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ password }),
    });
    return r.ok;
  },
  async logout() {
    await fetch("/api/logout", { method: "POST", credentials: "same-origin" });
  },
  async conversations() {
    const r = await fetch("/api/conversations", { credentials: "same-origin" });
    if (!r.ok) return [];
    const d = await r.json();
    return Array.isArray(d) ? d : (d.conversations || []);
  },
  async conversation(id) {
    const r = await fetch("/api/conversation/" + id, { credentials: "same-origin" });
    if (!r.ok) return null;
    return await r.json();
  },
  async translate(text, target) {
    const r = await fetch("/api/translate", {
      method: "POST", credentials: "same-origin",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text, target }),
    });
    if (!r.ok) return text;
    const d = await r.json();
    return d.text || text;
  },
  async search(q, lang) {
    const r = await fetch("/api/search?q=" + encodeURIComponent(q) + "&lang=" + encodeURIComponent(lang || "si"), { credentials: "same-origin" });
    if (!r.ok) return { results: [] };
    return await r.json();
  },
  async ask(convId, question, files) {
    const fd = new FormData();
    fd.append("question", question);
    (files || []).forEach(f => fd.append("documents", f));
    const r = await fetch("/api/conversation/" + convId + "/ask", {
      method: "POST", credentials: "same-origin", body: fd,
    });
    if (!r.ok) throw new Error("ask failed " + r.status);
    return await r.json();
  },
};
window.AILAPI = API;

/* ============================== LOGIN ============================== */
function Login({ t, onOk }) {
  const [pw, setPw] = React.useState("");
  const [err, setErr] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [mobileOpen, setMobileOpen] = React.useState(false);
  const [searchQuery, setSearchQuery] = React.useState("");
  const [searchResults, setSearchResults] = React.useState([]);
  const submit = async () => {
    setBusy(true); setErr(false);
    const ok = await API.login(pw);
    setBusy(false);
    if (ok) onOk(); else setErr(true);
  };
  return (
    <div className="login-screen">
      <div className="login-card">
        <Emblem size={56} variant="badge" />
        <div className="login-brand">AI <em>Lawyer</em></div>
        <div className="login-sub">{t.ui.brandSub}</div>
        <input
          type="password"
          className={"login-input" + (err ? " err" : "")}
          placeholder={t.ui.loginPh || "Geslo"}
          value={pw}
          onChange={e => setPw(e.target.value)}
          onKeyDown={e => { if (e.key === "Enter") submit(); }}
          autoFocus
        />
        {err && <div className="login-err">{t.ui.loginErr || "Napačno geslo"}</div>}
        <button className="login-btn" onClick={submit} disabled={busy || !pw}>
          {busy ? "…" : (t.ui.loginBtn || "Vstop")}
        </button>
      </div>
    </div>
  );
}

/* ============================== COMPOSER ============================== */
function Composer({ t, value, setValue, onSend, attached, addAttach, removeAttach, busy }) {
  const [focus, setFocus] = React.useState(false);
  const ref = React.useRef(null);
  const fileRef = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.style.height = "auto";
    el.style.height = Math.min(el.scrollHeight, 160) + "px";
  }, [value]);
  const onKey = (e) => {
    if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); if (!busy) onSend(); }
  };
  return (
    <div className="composer-wrap">
      {attached.length > 0 && (
        <div className="attached-row">
          {attached.map((d, i) => (
            <span className="chip" key={i}>
              <span style={{ display:"inline-block", width:14, height:17, borderRadius:2,
                background:"linear-gradient(180deg,#fff,#dfe6f0)", border:"1px solid var(--line-2)" }} />
              {d.name}
              <span className="x" onClick={() => removeAttach(i)}>×</span>
            </span>
          ))}
        </div>
      )}
      <div className={"composer" + (focus ? " focus" : "")}>
        <input ref={fileRef} type="file" multiple style={{ display: "none" }}
          accept=".pdf,.docx,.txt,.png,.jpg,.jpeg,.heic"
          onChange={e => { addAttach(Array.from(e.target.files)); e.target.value = ""; }} />
        <button className="attach" onClick={() => fileRef.current && fileRef.current.click()} title="Attach">
          <Icon name="clip" size={18} />
        </button>
        <textarea ref={ref} rows={1} value={value}
          placeholder={t.ui.composerPh}
          onChange={(e) => setValue(e.target.value)}
          onKeyDown={onKey}
          onFocus={() => setFocus(true)} onBlur={() => setFocus(false)} />
        <button className="send" onClick={onSend} disabled={!value.trim() || busy}>
          <Icon name="send" size={18} color="#fff" />
        </button>
      </div>
      <div className="composer-hint">
        <b>{t.ui.hintA}</b>, {t.ui.hintB} · {t.ui.hintC}
      </div>
    </div>
  );
}

/* ---------- mapiranje backend -> UI ---------- */
// Backend conversation.messages: role user|intake|panel|synthesis
function parseTrans(m) {
  // vrne {base, si?, en?, de?} ali null
  if (!m || !m.translations) return null;
  try { return JSON.parse(m.translations); } catch (e) { return null; }
}
function buildExchanges(data, lang) {
  const msgs = data.messages || [];
  const users = msgs.filter(m => m.role === "user");
  const out = [];
  for (const um of users) {
    const intake = msgs.find(m => m.role === "intake" && m.parent_message_id === um.id);
    const panel = msgs.filter(m => m.role === "panel" && m.parent_message_id === um.id);
    const synth = msgs.find(m => m.role === "synthesis" && m.parent_message_id === um.id);
    // jezik osnove: iz translations.base (ce obstaja), sicer trenutni
    const synthT = parseTrans(synth);
    const intakeT = parseTrans(intake);
    const baseLang = (synthT && synthT.base) || (intakeT && intakeT.base) || lang;
    out.push({
      id: "db" + um.id, lang: baseLang, origLang: baseLang,
      user: { text: um.content, docs: um.documents || [] },
      intake: intake ? intake.content : null,
      intakeTrans: intakeT,
      models: panel.map(p => ({
        model: p.model, label: p.label, vendor: p.vendor,
        answer: p.content, status: "done",
        trans: parseTrans(p),
      })),
      synthText: synth ? synth.content : null,
      synthTrans: synthT,
      allDone: !!synth,
    });
  }
  return out;
}

/* ============================== APP ============================== */
function App() {
  const [authed, setAuthed] = React.useState(null); // null=preverjam
  const [lang, setLang] = React.useState(() => localStorage.getItem("ail_lang") || "si");
  const t = I18N[lang];
  const [convList, setConvList] = React.useState([]);
  const [activeConv, setActiveConv] = React.useState(0); // 0 = nov
  const [exchanges, setExchanges] = React.useState([]);
  const [input, setInput] = React.useState("");
  const [attached, setAttached] = React.useState([]);
  const [busy, setBusy] = React.useState(false);
  const [mobileOpen, setMobileOpen] = React.useState(false);
  const [searchQuery, setSearchQuery] = React.useState("");
  const [searchResults, setSearchResults] = React.useState([]);
  const threadRef = React.useRef(null);

  React.useEffect(() => { localStorage.setItem("ail_lang", lang); }, [lang]);
  React.useEffect(() => {
    const v = new URLSearchParams(location.search).get("v") || "b";
    document.body.dataset.variant = v;
  }, []);

  // preveri sejo ob zagonu
  React.useEffect(() => { API.me().then(setAuthed); }, []);

  // nalozi seznam pogovorov ob prijavi
  const loadConvs = React.useCallback(async () => {
    const list = await API.conversations();
    setConvList(list);
  }, []);
  React.useEffect(() => { if (authed) loadConvs(); }, [authed, loadConvs]);

  // nalozi izbran pogovor
  const openConv = React.useCallback(async (id) => {
    setActiveConv(id);
    setMobileOpen(false);
    if (id === 0) { setExchanges([]); return; }
    const data = await API.conversation(id);
    if (data) setExchanges(buildExchanges(data, lang));
  }, [lang]);

  React.useEffect(() => {
    const el = threadRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [exchanges, busy]);

  // Prevajanje vsebine ob preklopu jezika (Gemini Flash, s predpomnjenjem)
  const [transCache, setTransCache] = React.useState({}); // {exId: {lang: {intake, synth, models:[...]}}}
  const [translating, setTranslating] = React.useState(false);

  React.useEffect(() => {
    let cancelled = false;
    async function run() {
      // Avtomatski prevod onemogocen — prevod je na zahtevo (gumb pri sintezi).
      // Vmesnik se preklopi takoj; pravna vsebina ostane v izvirniku.
      return;
      // eslint-disable-next-line no-unreachable
      const toDo = exchanges.filter(ex => {
        if (!ex.origLang) return false;
        if (ex.origLang === lang) return false;
        const c = transCache[ex.id];
        return !(c && c[lang]);
      });
      if (toDo.length === 0) return;
      setTranslating(true);
      for (const ex of toDo) {
        if (cancelled) break;
        try {
          const parts = {};
          if (ex.intake) parts.intake = await API.translate(ex.intake, lang);
          if (ex.synthText) parts.synth = await API.translate(ex.synthText, lang);
          parts.models = [];
          for (const m of ex.models) {
            parts.models.push(await API.translate(m.answer, lang));
          }
          if (!cancelled) {
            setTransCache(prev => ({
              ...prev,
              [ex.id]: { ...(prev[ex.id] || {}), [lang]: parts },
            }));
          }
        } catch (e) { /* ignore */ }
      }
      if (!cancelled) setTranslating(false);
    }
    run();
    return () => { cancelled = true; };
  }, [lang, exchanges]);

  // iskanje po projektih in vsebini (debounce)
  React.useEffect(() => {
    const qq = searchQuery.trim();
    if (qq.length < 2) { setSearchResults([]); return; }
    let cancelled = false;
    const tmr = setTimeout(async () => {
      try {
        const d = await API.search(qq, lang);
        if (!cancelled) setSearchResults(d.results || []);
      } catch (e) { if (!cancelled) setSearchResults([]); }
    }, 280);
    return () => { cancelled = true; clearTimeout(tmr); };
  }, [searchQuery, lang]);

  // vrni exchange z besedilom v izbranem jeziku (uporabi vnaprej shranjene prevode)
  const localizedEx = (ex) => {
    if (!ex.origLang) return ex;
    // Za vsak del: uporabi prevod za trenutni jezik ce obstaja, sicer izvirnik.
    // (Backend shrani prevod v jezik osnove SAMO za dele, kjer je model odstopil od jezika.)
    // intake: sestavi JSON s prevedenimi polji (ce so na voljo)
    let intake = ex.intake;
    if (ex.intakeTrans && ex.intakeTrans[lang] && typeof ex.intake === "string"
        && ex.intake.indexOf("@@INTAKE_JSON@@") === 0) {
      try {
        const base = JSON.parse(ex.intake.slice("@@INTAKE_JSON@@".length));
        const tr = ex.intakeTrans[lang];
        intake = "@@INTAKE_JSON@@" + JSON.stringify({
          summary: tr.summary || base.summary,
          goal: tr.goal || base.goal,
          task: tr.task || base.task,
          legal_area: tr.legal_area || base.legal_area,
          jurisdiction: base.jurisdiction,
          mode: base.mode,
        });
      } catch (e) {}
    }
    const synthText = (ex.synthTrans && ex.synthTrans[lang]) ? ex.synthTrans[lang] : ex.synthText;
    const models = ex.models.map(m => ({
      ...m,
      answer: (m.trans && m.trans[lang]) ? m.trans[lang] : m.answer,
    }));
    return { ...ex, intake, synthText, models };
  };

  const send = async () => {
    const text = input.trim();
    if (!text || busy) return;
    setBusy(true);
    const files = attached.slice();
    // optimisticno pokazi vprasanje
    const tempId = "tmp" + Date.now();
    setExchanges(prev => [...prev, {
      id: tempId, lang, user: { text, docs: files.map(f => ({ filename: f.name })) },
      intake: null, models: [], synthText: null, allDone: false, pending: true,
    }]);
    setInput(""); setAttached([]);
    try {
      const res = await API.ask(activeConv, text, files);
      const newConvId = res.conversation_id || activeConv;
      // ponovno nalozi cel pogovor iz baze (da dobimo intake+panel+synth pravilno)
      const data = await API.conversation(newConvId);
      if (data) setExchanges(buildExchanges(data, lang));
      setActiveConv(newConvId);
      loadConvs();
    } catch (e) {
      setExchanges(prev => prev.map(x => x.id === tempId
        ? { ...x, synthText: (t.ui.errAsk || "Napaka pri obdelavi. Poskusite znova."), allDone: true, pending: false }
        : x));
    } finally {
      setBusy(false);
    }
  };

  const addAttach = (fileArr) => {
    setAttached(prev => {
      const room = 5 - prev.length;
      return [...prev, ...fileArr.slice(0, Math.max(0, room))];
    });
  };
  const removeAttach = (i) => setAttached(prev => prev.filter((_, j) => j !== i));
  const newConversation = () => { setActiveConv(0); setExchanges([]); setInput(""); setAttached([]); setMobileOpen(false); };
  const doLogout = async () => { await API.logout(); setAuthed(false); };

  if (authed === null) return <div className="boot">…</div>;
  if (!authed) return <Login t={t} onOk={() => setAuthed(true)} />;

  const sidebarConvs = convList.map(c => ({
    id: c.id, title: c.title || "—",
    ago: "", count: "",
  }));
  const activeTitle = (convList.find(c => c.id === activeConv) || {}).title || (t.ui.newConv);

  return (
    <div className="app">
      <Sidebar t={t} conversations={sidebarConvs} activeId={activeConv}
        onSelect={(id) => { setSearchQuery(""); openConv(id); }} onNew={() => { setSearchQuery(""); newConversation(); }} onLogout={doLogout}
        mobileOpen={mobileOpen} onClose={() => setMobileOpen(false)}
        search={{ query: searchQuery, setQuery: setSearchQuery, results: searchResults }} />
      {mobileOpen && <div className="sidebar-overlay" onClick={() => setMobileOpen(false)} />}
      <div className="main">
        <TopBar t={t} title={activeTitle} lang={lang} setLang={setLang} onLogout={doLogout} onMenu={() => setMobileOpen(true)} />
        <div className="thread scroll" ref={threadRef}>
          <div className="thread-inner">
            {exchanges.length === 0 && (
              <div className="empty-hint">
                <Emblem size={48} variant="badge" />
                <p>{t.ui.emptyHint || "Zastavite pravno vprašanje ali naložite dokument."}</p>
              </div>
            )}
            {exchanges.map((ex0) => {
              const ex = localizedEx(ex0);
              const et = t;
              return (
                <React.Fragment key={ex.id}>
                  <UserMessage t={et} text={ex.user.text} docs={ex.user.docs} />
                  {ex.intake && <IntakeBlock t={et} content={ex.intake} />}
                  {(ex.models.length > 0 || ex.pending) &&
                    <Panel t={et} models={ex.models} allDone={ex.allDone} pending={ex.pending} />}
                  {ex.allDone && ex.synthText && <SynthesisHero t={et} content={ex.synthText} lang={lang} />}
                </React.Fragment>
              );
            })}
            {translating && <div className="thinking-row"><span className="dots3"><span/><span/><span/></span> {t.ui.translating || "Prevajanje…"}</div>}
            {busy && <div className="thinking-row"><span className="dots3"><span/><span/><span/></span> {t.ui.panelLabel}</div>}
          </div>
          <div className="disclaimer">
            <span className="s">§</span>
            <span>{t.ui.disclaimer}</span>
          </div>
        </div>
        <Composer t={t} value={input} setValue={setInput} onSend={send}
          attached={attached} addAttach={addAttach} removeAttach={removeAttach} busy={busy} />
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
