/* ==========================================================================
   SVG Icons — defined once, injected into HTML by JS
   ========================================================================== */

const ICONS = {
  copy: '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"/></svg>',
  check: '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/></svg>',
  moon: '<svg class="icon-moon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z"/></svg>',
  sun: '<svg class="icon-sun" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z"/></svg>',
};

/* ── Clipboard helper with file:// fallback ── */

function copyText(text) {
  if (navigator.clipboard && window.isSecureContext) {
    return navigator.clipboard.writeText(text);
  }

  return new Promise((resolve, reject) => {
    const ta = document.createElement('textarea');
    ta.value = text;
    ta.setAttribute('readonly', '');
    ta.style.position = 'fixed';
    ta.style.top = '-9999px';
    ta.style.left = '-9999px';
    document.body.appendChild(ta);
    ta.focus();
    ta.select();
    ta.setSelectionRange(0, ta.value.length);

    try {
      const ok = document.execCommand('copy');
      if (ok) resolve();
      else reject(new Error('execCommand copy failed'));
    } catch (err) {
      reject(err);
    } finally {
      ta.remove();
    }
  });
}

/* ── Inject icons into all buttons on load ── */

document.querySelectorAll('.copy-btn').forEach(btn => {
  btn.insertAdjacentHTML('afterbegin', ICONS.copy);
});

document.querySelectorAll('.theme-toggle').forEach(btn => {
  btn.innerHTML = ICONS.moon + ICONS.sun;
});

/* ==========================================================================
   Copy buttons — event delegation
   ========================================================================== */

(() => {
  const COPIED_DURATION = 1500;

  function flashCopied(btn) {
    const original = btn.innerHTML;
    btn.classList.add('copied');
    btn.innerHTML = ICONS.check + ' Copied';
    setTimeout(() => {
      btn.classList.remove('copied');
      btn.innerHTML = original;
    }, COPIED_DURATION);
  }

  function getTextToCopy(btn) {
    if (btn.dataset.copy === 'main') {
      return document.getElementById('main-content')?.innerText ?? '';
    }
    const item = btn.closest('.follow-up-item');
    if (item) {
      const p = item.querySelector('p');
      if (!p) return '';
      const clone = p.cloneNode(true);
      clone.querySelectorAll('.follow-up-number').forEach(el => el.remove());
      return clone.innerText.trim();
    }
    return '';
  }

  document.addEventListener('click', (e) => {
    const btn = e.target.closest('.copy-btn');
    if (!btn) return;

    const text = getTextToCopy(btn);
    if (text) {
      copyText(text)
        .then(() => flashCopied(btn))
        .catch((err) => console.warn('Copy failed:', err));
    }
  });
})();

/* ==========================================================================
   Prompt cards — auto copy button on top-right
   ========================================================================== */

(() => {
  const PROMPT_SELECTOR = 'article pre:not(.mermaid)';
  const COPIED_DURATION = 1200;

  function decoratePromptCards() {
    document.querySelectorAll(PROMPT_SELECTOR).forEach((pre) => {
      if (pre.closest('.prompt-card')) return;

      const card = document.createElement('div');
      card.className = 'prompt-card';
      pre.parentNode?.insertBefore(card, pre);
      card.appendChild(pre);

      const btn = document.createElement('button');
      btn.type = 'button';
      btn.className = 'prompt-copy-btn';
      btn.setAttribute('aria-label', 'Copy prompt');
      btn.setAttribute('title', 'Copy prompt');
      btn.textContent = 'COPY';
      card.appendChild(btn);
    });
  }

  function flashCopied(btn) {
    btn.classList.add('copied');
    btn.textContent = 'COPIED';
    setTimeout(() => {
      btn.classList.remove('copied');
      btn.textContent = 'COPY';
    }, COPIED_DURATION);
  }

  document.addEventListener('click', (e) => {
    const btn = e.target.closest('.prompt-copy-btn');
    if (!btn) return;

    const pre = btn.closest('.prompt-card')?.querySelector('pre');
    if (!pre) return;
    const text = (pre.querySelector('code')?.innerText ?? pre.innerText ?? '').replace(/\n$/, '');
    if (!text) return;

    copyText(text)
      .then(() => flashCopied(btn))
      .catch((err) => console.warn('Prompt copy failed:', err));
  });

  decoratePromptCards();
})();

/* ==========================================================================
   Theme toggle — detect system, persist override, toggle
   ========================================================================== */

(() => {
  const STORAGE_KEY = 'theme';
  const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');

  function setTheme(theme) {
    document.documentElement.dataset.theme = theme;
  }

  function toggleTheme() {
    const current = document.documentElement.dataset.theme;
    const next = current === 'dark' ? 'light' : 'dark';
    setTheme(next);
    localStorage.setItem(STORAGE_KEY, next);
  }

  // Follow system changes only when no stored override
  darkQuery.addEventListener('change', (e) => {
    if (!localStorage.getItem(STORAGE_KEY)) {
      setTheme(e.matches ? 'dark' : 'light');
    }
  });

  // Toggle button
  document.addEventListener('click', (e) => {
    if (e.target.closest('.theme-toggle')) {
      toggleTheme();
    }
  });
})();

/* ==========================================================================
   Staggered reveal — fade-in + up + blur on load
   ========================================================================== */

(() => {
  const INITIAL_DELAY = 200;
  const STAGGER = 100;
  const CAP = 20;

  // Collect elements in reading order, expanding containers into children
  function collectElements() {
    const els = [];
    for (const child of document.body.children) {
      if (child.tagName === 'SCRIPT' || child.tagName === 'LINK' || child.tagName === 'STYLE') continue;
      if (child.id === 'main-content' || child.tagName === 'ARTICLE') {
        for (const sub of child.children) els.push(sub);
      } else if (child.id === 'follow-ups') {
        for (const sub of child.children) els.push(sub);
      } else {
        els.push(child);
      }
    }
    return els;
  }

  const elements = collectElements();

  // Hide all immediately
  elements.forEach(el => el.classList.add('reveal'));

  // Stagger reveal, cap at element CAP
  setTimeout(() => {
    elements.forEach((el, i) => {
      const delay = i < CAP ? i * STAGGER : CAP * STAGGER;
      setTimeout(() => el.classList.add('visible'), delay);
    });
  }, INITIAL_DELAY);
})();

/* ==========================================================================
   Diagram expand viewer — full width modal with zoom + pan
   ========================================================================== */

(() => {
  const BTN_CLASS = 'diagram-expand-btn';
  const CONTAINER_SELECTOR = '.diagram-container';
  const MIN_ZOOM = 0.6;
  const MAX_ZOOM = 2.2;
  const STEP = 0.15;
  const CLOSE_ANIM_MS = 360;
  let modal = null;
  let cleanupPan = null;
  let handleEsc = null;
  let baseSvgWidth = 1200;

  function ensureExpandButtons() {
    document.querySelectorAll(CONTAINER_SELECTOR).forEach((container) => {
      if (container.querySelector(`.${BTN_CLASS}`)) return;
      const btn = document.createElement('button');
      btn.type = 'button';
      btn.className = BTN_CLASS;
      btn.textContent = 'EXPAND';
      btn.setAttribute('aria-label', 'Expand diagram');
      container.appendChild(btn);
    });
  }

  function getSourceSvg(container) {
    return container.querySelector('.mermaid-host svg, .mermaid svg');
  }

  function getSvgBaseWidth(svg) {
    const vb = svg.viewBox?.baseVal;
    if (vb && vb.width > 0) return vb.width;

    const widthAttr = parseFloat(svg.getAttribute('width') || '');
    if (Number.isFinite(widthAttr) && widthAttr > 0) return widthAttr;

    const rect = svg.getBoundingClientRect();
    if (rect.width > 0) return rect.width;

    return 1200;
  }

  function centerViewport(viewport) {
    viewport.scrollLeft = Math.max(0, (viewport.scrollWidth - viewport.clientWidth) / 2);
    viewport.scrollTop = Math.max(0, (viewport.scrollHeight - viewport.clientHeight) / 2);
  }

  function bindMousePan(viewport) {
    let active = false;
    let startX = 0;
    let startY = 0;
    let startLeft = 0;
    let startTop = 0;

    function onPointerDown(e) {
      if (e.pointerType !== 'mouse' || e.button !== 0) return;
      active = true;
      viewport.classList.add('is-dragging');
      startX = e.clientX;
      startY = e.clientY;
      startLeft = viewport.scrollLeft;
      startTop = viewport.scrollTop;
    }

    function onPointerMove(e) {
      if (!active) return;
      e.preventDefault();
      const dx = e.clientX - startX;
      const dy = e.clientY - startY;
      viewport.scrollLeft = startLeft - dx;
      viewport.scrollTop = startTop - dy;
    }

    function endPan() {
      if (!active) return;
      active = false;
      viewport.classList.remove('is-dragging');
    }

    viewport.addEventListener('pointerdown', onPointerDown);
    window.addEventListener('pointermove', onPointerMove, { passive: false });
    window.addEventListener('pointerup', endPan);
    window.addEventListener('pointercancel', endPan);

    return () => {
      viewport.removeEventListener('pointerdown', onPointerDown);
      window.removeEventListener('pointermove', onPointerMove);
      window.removeEventListener('pointerup', endPan);
      window.removeEventListener('pointercancel', endPan);
    };
  }

  function setZoom(next, { recenter = false } = {}) {
    if (!modal) return;
    const canvas = modal.querySelector('.diagram-modal__canvas');
    const resetBtn = modal.querySelector('[data-diagram-zoom="reset"]');
    const viewport = modal.querySelector('.diagram-modal__viewport');
    if (!canvas || !resetBtn) return;
    const clamped = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, next));
    canvas.dataset.zoom = String(clamped);
    canvas.style.width = `${Math.round(baseSvgWidth * clamped)}px`;
    resetBtn.textContent = `${Math.round(clamped * 100)}%`;

    if (recenter && viewport) {
      requestAnimationFrame(() => centerViewport(viewport));
    }
  }

  function closeModal() {
    if (!modal) return;
    const activeModal = modal;
    modal = null;

    if (cleanupPan) {
      cleanupPan();
      cleanupPan = null;
    }
    if (handleEsc) {
      document.removeEventListener('keydown', handleEsc);
      handleEsc = null;
    }

    activeModal.classList.add('is-closing');
    activeModal.classList.remove('is-open');
    window.setTimeout(() => {
      activeModal.remove();
      document.body.classList.remove('has-diagram-modal');
    }, CLOSE_ANIM_MS);
  }

  function openModal(container) {
    const svg = getSourceSvg(container);
    if (!svg) return;

    closeModal();

    modal = document.createElement('div');
    modal.className = 'diagram-modal';
    modal.innerHTML = `
      <div class="diagram-modal__panel" role="dialog" aria-modal="true" aria-label="Expanded diagram viewer">
        <div class="diagram-modal__toolbar">
          <span class="diagram-modal__title">DIAGRAM PREVIEW</span>
          <div class="diagram-modal__controls">
            <button type="button" data-diagram-zoom="out" aria-label="Alejar">-</button>
            <button type="button" data-diagram-zoom="reset" aria-label="Restablecer zoom">100%</button>
            <button type="button" data-diagram-zoom="in" aria-label="Acercar">+</button>
            <button type="button" class="diagram-modal__close" aria-label="Close diagram">CLOSE</button>
          </div>
        </div>
        <div class="diagram-modal__viewport">
          <div class="diagram-modal__stage">
            <div class="diagram-modal__canvas" data-zoom="1"></div>
          </div>
        </div>
      </div>
    `;

    const canvas = modal.querySelector('.diagram-modal__canvas');
    const viewport = modal.querySelector('.diagram-modal__viewport');
    if (!canvas || !viewport) return;

    const clone = svg.cloneNode(true);
    canvas.appendChild(clone);
    baseSvgWidth = getSvgBaseWidth(clone);

    document.body.appendChild(modal);
    document.body.classList.add('has-diagram-modal');
    cleanupPan = bindMousePan(viewport);
    setZoom(1, { recenter: true });
    requestAnimationFrame(() => modal?.classList.add('is-open'));

    handleEsc = (e) => {
      if (e.key === 'Escape') closeModal();
    };
    document.addEventListener('keydown', handleEsc);
  }

  document.addEventListener('click', (e) => {
    const expandBtn = e.target.closest(`.${BTN_CLASS}`);
    if (expandBtn) {
      const container = expandBtn.closest(CONTAINER_SELECTOR);
      if (container) openModal(container);
      return;
    }

    if (!modal) return;

    if (e.target === modal || e.target.closest('.diagram-modal__close')) {
      closeModal();
      return;
    }

    const zoomBtn = e.target.closest('[data-diagram-zoom]');
    if (!zoomBtn) return;
    const current = Number(modal.querySelector('.diagram-modal__canvas')?.dataset.zoom ?? '1');
    const action = zoomBtn.getAttribute('data-diagram-zoom');
    if (action === 'in') setZoom(current + STEP);
    if (action === 'out') setZoom(current - STEP);
    if (action === 'reset') setZoom(1, { recenter: true });
  });

  ensureExpandButtons();
})();

/* ==========================================================================
   Mermaid diagrams — render + dark-mode re-render
   Depends on mermaid.min.js being loaded before this script.
   ========================================================================== */

(async () => {
  if (typeof mermaid === 'undefined') return;

  /* ── Theme configs ─────────────────────────────────────────────── */

  const LIGHT_THEME = {
    primaryColor: '#f0f0f0',
    primaryTextColor: '#333',
    primaryBorderColor: '#ccc',
    lineColor: '#bbb',
    secondaryColor: '#fff',
    tertiaryColor: '#f5f5f5',
    fontFamily: 'Inter, sans-serif',
    fontSize: '13px',
    edgeLabelBackground: '#fafafa'
  };

  const DARK_THEME = {
    primaryColor: '#2a2a2a',
    primaryTextColor: '#c8c8c8',
    primaryBorderColor: '#444',
    lineColor: '#555',
    secondaryColor: '#252525',
    tertiaryColor: '#2a2a2a',
    fontFamily: 'Inter, sans-serif',
    fontSize: '13px',
    edgeLabelBackground: '#222'
  };

  const FLOW_OPTS = {
    htmlLabels: true,
    curve: 'monotoneX',
    padding: 24,
    nodeSpacing: 40,
    rankSpacing: 50,
    useMaxWidth: false,
    wrappingWidth: 300
  };

  /* ── Color map: light hex → dark hex (single-pass replacement) ── */

  const LIGHT_TO_DARK = {
    '#fff':    '#252525',
    '#ffffff': '#252525',
    '#f0f0f0': '#2a2a2a',
    '#f5f5f5': '#2a2a2a',
    '#f9f9f9': '#2a2a2a',
    '#fafafa': '#222',
    '#eef7ee': '#2a332a',
    '#d0d0d0': '#3a3a3a',
    '#8cb88c': '#3a4a3a',
    '#888':    '#555',
    '#bbb':    '#444',
    '#ccc':    '#444',
    '#333':    '#c8c8c8',
    '#555':    '#999',
    '#666':    '#888',
    '#999':    '#666',
  };

  function buildRegex(map) {
    const escaped = Object.keys(map).map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
    return new RegExp(escaped.join('|'), 'gi');
  }

  function transformColors(code, map) {
    const re = buildRegex(map);
    return code.replace(re, match => map[match.toLowerCase()] || match);
  }

  /* ── Save original sources before Mermaid processes them ──────── */

  const diagrams = [];
  const VERTICAL_FLOW_RE = /\b(?:flowchart|graph)\s+T[DB]\b/i;

  document.querySelectorAll('pre.mermaid').forEach((pre, i) => {
    const code = pre.textContent;
    const host = document.createElement('div');
    host.className = 'mermaid-host';
    const container = pre.closest('.diagram-container');
    const isVertical = VERTICAL_FLOW_RE.test(code);
    if (container) {
      container.classList.toggle('diagram-container--vertical', isVertical);
    }
    pre.replaceWith(host);
    diagrams.push({ host, code, id: `mmd-${i}` });
  });

  /* ── Render function ─────────────────────────────────────────── */

  let generation = 0;

  /* Safari doesn't support rx/ry as CSS properties on SVG <rect>.
     Mermaid's style directive puts them in the inline style string,
     but Safari's CSSOM ignores them. Parse the raw attribute instead. */
  function fixSvgBorderRadius(container) {
    container.querySelectorAll('rect').forEach(rect => {
      const raw = rect.getAttribute('style') || '';
      const rxM = raw.match(/\brx\s*:\s*([\d.]+)/);
      const ryM = raw.match(/\bry\s*:\s*([\d.]+)/);
      if (rxM) rect.setAttribute('rx', rxM[1]);
      if (ryM) rect.setAttribute('ry', ryM[1]);
    });
  }

  async function renderAll(isDark) {
    const gen = ++generation;
    const vars = isDark ? DARK_THEME : LIGHT_THEME;
    const colorMap = isDark ? LIGHT_TO_DARK : null;

    mermaid.initialize({
      startOnLoad: false,
      theme: 'base',
      securityLevel: 'loose',
      flowchart: FLOW_OPTS,
      themeVariables: vars
    });

    for (const d of diagrams) {
      if (gen !== generation) return; // stale render
      const code = colorMap ? transformColors(d.code, colorMap) : d.code;
      const uid = `${d.id}-g${gen}`;
      try {
        const { svg } = await mermaid.render(uid, code);
        d.host.innerHTML = svg;
        fixSvgBorderRadius(d.host);
      } catch (err) {
        console.warn('Mermaid render error:', err);
      }
    }
  }

  /* ── Initial render ──────────────────────────────────────────── */

  // Wait for webfonts before first render to avoid label measurement clipping.
  if (document.fonts?.ready) {
    try {
      await document.fonts.ready;
    } catch (_) {}
  }

  const isDark = document.documentElement.dataset.theme === 'dark';
  await renderAll(isDark);

  /* ── Watch for theme changes via MutationObserver ─────────────── */

  const observer = new MutationObserver((mutations) => {
    for (const m of mutations) {
      if (m.attributeName === 'data-theme') {
        const dark = document.documentElement.dataset.theme === 'dark';
        renderAll(dark);
      }
    }
  });

  observer.observe(document.documentElement, {
    attributes: true,
    attributeFilter: ['data-theme']
  });
})();
