// Generator app — mounted on /vzor (single-doctype: zápůjčka).
// Žiadny doctype state machine — táto aplikácia má jediný typ zmluvy
// (smlouva o půjčce / zápůjčka podľa § 2390 NOZ). Ak by bolo niekedy
// treba viac typov, vráti sa stav z git history pred refactorom 2026-05-03.
const GenApp = () => {
  const [creditors, setCreditors] = React.useState([{ id: 1, type: 'person' }]);
  const [debtors, setDebtors] = React.useState([{ id: 2, type: 'person' }]);

  // Solidaritný režim pri 2+ dlžníkoch — defaultne ON (§ 1872 NOZ).
  // Pri OFF sa do textu zmluvy vloží odkaz na podielové plnenie podľa
  // § 1869 NOZ. Phase 4 prepoji checkbox v Paper.jsx.
  const [solidaryLiability, setSolidaryLiability] = React.useState(true);

  const [optionals, setOptionals] = React.useState({
    'c-arbitration': true,    // rozhodčia doložka — defaultne ON (čl. VII. ods. 2)
    'c-other-terms': false,   // další ujednání (krok 8) — defaultne OFF
  });
  const [activeArticle, setActiveArticle] = React.useState('art-1');
  const [activeTipKey, setActiveTipKey] = React.useState(null);
  const [tipModalKey, setTipModalKey] = React.useState(null);
  const [progress, setProgress] = React.useState(0);

  // Click na TIP tlačidlo: aktualizuj sidebar (desktop) aj otvor modal (mobile).
  // Scroll-spy v Paper si naďalej volá iba setActiveTipKey, takže modal nereaguje
  // na rolovanie — otvorí sa len na zámerný klik.
  const handleTipClick = React.useCallback((key) => {
    setActiveTipKey(key);
    setTipModalKey(key);
  }, []);

  // Document title — single VZOR_TITLE z config.js, s rok-replace pre {YYYY}.
  React.useEffect(() => {
    if (window.VZOR_TITLE) {
      document.title = window.VZOR_TITLE.replace(/\(\d{4}\)/, '(' + new Date().getFullYear() + ')');
    }
  }, []);

  // Compute progress from filled .ed fields inside .paper (+ selects count as pre-filled)
  React.useEffect(() => {
    const compute = () => {
      const paper = document.querySelector('article.paper');
      if (!paper) return;
      // Only count visible fields (skip fields inside hidden/optional sections)
      const eds = Array.from(paper.querySelectorAll('.ed[data-placeholder]'))
        .filter(el => el.offsetParent !== null); // visible only
      if (!eds.length) { setProgress(0); return; }
      const filled = eds.filter(el => (el.textContent || '').trim().length > 0).length;
      const pct = Math.round((filled / eds.length) * 100);
      setProgress(pct);
    };

    // Initial compute after React settles
    const t = setTimeout(compute, 150);

    // Listen for any input inside the paper (bubbling)
    const handler = () => compute();
    document.addEventListener('input', handler, true);
    // Also recompute when selects change (they're native <select>)
    document.addEventListener('change', handler, true);
    // And when optional clauses toggle (they change visible field count)
    const mo = new MutationObserver(() => { clearTimeout(window.__progTimer); window.__progTimer = setTimeout(compute, 50); });
    const paper = document.querySelector('article.paper');
    if (paper) mo.observe(paper, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style'] });

    return () => {
      clearTimeout(t);
      document.removeEventListener('input', handler, true);
      document.removeEventListener('change', handler, true);
      mo.disconnect();
    };
  }, []);

  const toggleOptional = (id) => setOptionals(o => ({ ...o, [id]: !o[id] }));

  return (
    <>
      <GenHeader progress={progress} />
      <div className="gen-shell">
        <Paper
          creditors={creditors} setCreditors={setCreditors}
          debtors={debtors} setDebtors={setDebtors}
          solidaryLiability={solidaryLiability} setSolidaryLiability={setSolidaryLiability}
          optionals={optionals}
          setActiveArticle={(id) => { setActiveArticle(id); }}
          setActiveTipKey={setActiveTipKey}
          onTipClick={handleTipClick}
          activeTipKey={activeTipKey}
        />
        <RightSidebar
          optionals={optionals}
          toggleOptional={toggleOptional}
          activeArticle={activeArticle}
          activeTipKey={activeTipKey}
          clearTipKey={() => setActiveTipKey(null)}
        />
      </div>
      {window.LEGAL_TEXTS && window.LEGAL_TEXTS.html && (
        <section
          className="legal-article"
          dangerouslySetInnerHTML={{ __html: window.LEGAL_TEXTS.html }}
        />
      )}
      <TipModal
        optionals={optionals}
        tipKey={tipModalKey}
        onClose={() => { setTipModalKey(null); setActiveTipKey(null); }}
      />
    </>
  );
};

ReactDOM.createRoot(document.getElementById('gen-root')).render(<GenApp />);

// ------------------------------------------------------------
// Copy / "Save as Word" sanitizer.
// When users select-and-copy paragraphs from the paper, browsers
// serialize EVERY <option> inside each <select> — producing
// garbage like "zakázanémožné len s písomným súhlasom...". Here
// we intercept `copy` events on the paper and rewrite the
// clipboard so each select contributes only its CURRENT label.
// ------------------------------------------------------------
document.addEventListener('copy', (e) => {
  const sel = document.getSelection();
  if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
  const range = sel.getRangeAt(0);
  const paper = document.querySelector('article.paper');
  if (!paper || !paper.contains(range.commonAncestorContainer) && !paper.contains(range.startContainer)) return;

  // Clone the selected fragment, then collapse each <select> to the selected option only.
  const frag = range.cloneContents();
  const wrap = document.createElement('div');
  wrap.appendChild(frag);

  // cloneContents() loses the select's `value` property, but <option selected> is preserved
  // in the cloned DOM only if the original HAD the attribute. React sets `value` on the
  // <select> via the DOM property, not the attribute — so we look up each cloned select's
  // live counterpart and read its real value.
  const liveSelects = paper.querySelectorAll('select.ed-select');
  const clonedSelects = wrap.querySelectorAll('select.ed-select');
  // Map cloned → live by index among all selects that intersect the selection.
  // The clone preserves order, so we walk live selects and pick the ones visually
  // inside the selection range.
  const liveInRange = [];
  for (const s of liveSelects) {
    const r = document.createRange();
    r.selectNode(s);
    if (range.compareBoundaryPoints(Range.END_TO_START, r) <= 0 &&
        range.compareBoundaryPoints(Range.START_TO_END, r) >= 0) {
      liveInRange.push(s);
    }
  }
  clonedSelects.forEach((cs, i) => {
    const live = liveInRange[i];
    const val = live ? live.value : cs.value;
    // Remove every <option> except the selected one; replace <select> with the
    // bare label text so plain-text copy doesn't show "▼" chrome either.
    const picked = Array.from(cs.querySelectorAll('option')).find(o => o.value === val)
                || cs.querySelector('option[selected]')
                || cs.querySelector('option');
    const label = picked ? picked.textContent : '';
    const textNode = document.createTextNode(label);
    cs.parentNode.replaceChild(textNode, cs);
  });

  // Strip TIP buttons and other non-content chrome.
  // .ed-select-print is the print-only mirror of each <select>'s selected
  // option; once we've inlined the live label in place of the <select> above,
  // keeping the mirror would duplicate every choice in the clipboard text.
  wrap.querySelectorAll('.tip-btn, .tip-pin, .party-remove, .party-add, .seg-toggle, .seg-add, .no-copy, .party-type-switches, .ed-select-print').forEach(el => el.remove());

  const html = wrap.innerHTML;
  const text = wrap.innerText || wrap.textContent || '';

  e.clipboardData.setData('text/html', html);
  e.clipboardData.setData('text/plain', text);
  e.preventDefault();
});
