(() => {
  const typewordEl = document.getElementById("typeword");
  const stepperEl = document.getElementById("stepper");
  const revealStepper = createStepperRevealer(stepperEl);
  if (stepperEl) window.setTimeout(revealStepper, 5200);

  if (typewordEl) startTypewriter(typewordEl, { onFirstCycleDone: revealStepper });

  const quoteTextEl = document.getElementById("quoteText");
  const quoteMetaEl = document.getElementById("quoteMeta");
  const quoteWrapEl = quoteTextEl?.closest?.(".quote");
  if (quoteTextEl && quoteMetaEl && quoteWrapEl) startQuoteTicker({ quoteWrapEl, quoteTextEl, quoteMetaEl });
})();

function createStepperRevealer(stepperEl) {
  let shown = false;
  return () => {
    if (shown || !stepperEl) return;
    shown = true;
    stepperEl.hidden = false;
    window.requestAnimationFrame(() => stepperEl.classList.add("homeStepper--show"));
  };
}

function startTypewriter(typewordEl, opts = {}) {
  const words = ["Transparent", "Traceable", "Trust", "TTT"];
  const typeMs = 42;
  const deleteMs = 26;
  const holdMs = 780;
  const finalHoldMs = 1100;
  const betweenMs = 180;
  const onFirstCycleDone = typeof opts.onFirstCycleDone === "function" ? opts.onFirstCycleDone : null;

  let wordIndex = 0;
  let charIndex = 0;
  let isDeleting = false;
  let cycleCount = 0;

  const tick = () => {
    const word = words[wordIndex] || "";
    if (!isDeleting) {
      charIndex = Math.min(word.length, charIndex + 1);
    } else {
      charIndex = Math.max(0, charIndex - 1);
    }

    typewordEl.textContent = word.slice(0, charIndex);

    const atEnd = !isDeleting && charIndex === word.length;
    const atStart = isDeleting && charIndex === 0;

    if (atEnd) {
      isDeleting = true;
      const wait = wordIndex === words.length - 1 ? finalHoldMs : holdMs;
      window.setTimeout(tick, wait);
      return;
    }

    if (atStart) {
      isDeleting = false;
      wordIndex = (wordIndex + 1) % words.length;
      if (wordIndex === 0) {
        cycleCount += 1;
        if (cycleCount === 1 && onFirstCycleDone) onFirstCycleDone();
      }
      window.setTimeout(tick, betweenMs);
      return;
    }

    window.setTimeout(tick, isDeleting ? deleteMs : typeMs);
  };

  tick();
}

async function startQuoteTicker({ quoteWrapEl, quoteTextEl, quoteMetaEl }) {
  const quotes = await loadQuotes();
  if (!Array.isArray(quotes) || quotes.length === 0) return;

  quoteWrapEl.classList.add("quote--show");

  let idx = Math.floor(Math.random() * quotes.length);

  const show = (q) => {
    quoteWrapEl.classList.remove("quote--show");
    quoteWrapEl.classList.add("quote--fade");

    window.setTimeout(() => {
      quoteTextEl.textContent = q.text || "";
      renderQuoteMeta({ quoteMetaEl, q });

      quoteWrapEl.classList.remove("quote--fade");
      quoteWrapEl.classList.add("quote--show");
    }, 220);
  };

  const loop = () => {
    const q = quotes[idx] || {};
    show(q);
    idx = (idx + 1) % quotes.length;

    const duration = estimateReadMs(q.text || "");
    window.setTimeout(loop, duration);
  };

  loop();
}

async function loadQuotes() {
  try {
    const resp = await fetch(resolveIfTttUrl("assets/ifttt-quotes.json"), { cache: "no-store" });
    if (resp.ok) {
      const data = await resp.json();
      if (Array.isArray(data)) return data;
    }
  } catch (e) {}

  return [
    {
      text: "Footnotes aren't decorations. They're load-bearing walls.",
      source: "IF.TTT paper",
      href: "https://infrafabric.io/static/hosted/review/ifttt-paper-update/2025-12-28/review-pack.html",
    },
    {
      text: "If there's no IF.TTT trace, it didn't happen—or shouldn't be trusted.",
      source: "IF.TTT doctrine",
      href: "https://infrafabric.io/static/hosted/review/ifttt-paper-update/2025-12-28/review-pack.html",
    },
    {
      text: "Trust isn't claimed. It's proven.",
      source: "IF.TTT paper",
      href: "https://infrafabric.io/static/hosted/review/ifttt-paper-update/2025-12-28/review-pack.html",
    },
  ];
}

function resolveIfTttUrl(path) {
  try {
    const scriptEl = document.querySelector('script[src$="app.js"]');
    const scriptUrl = scriptEl ? new URL(scriptEl.getAttribute("src"), window.location.href) : new URL(window.location.href);
    return new URL(path, scriptUrl).toString();
  } catch (e) {
    return path;
  }
}

function renderQuoteMeta({ quoteMetaEl, q }) {
  while (quoteMetaEl.firstChild) quoteMetaEl.removeChild(quoteMetaEl.firstChild);

  const source = String(q.source || "").trim();
  const href = String(q.href || "").trim();
  if (!source) return;

  if (href) {
    const a = document.createElement("a");
    a.href = href;
    a.target = "_blank";
    a.rel = "noreferrer";
    a.textContent = source;
    quoteMetaEl.appendChild(a);
    return;
  }

  quoteMetaEl.textContent = source;
}

function estimateReadMs(text) {
  const cleaned = String(text || "").trim();
  if (!cleaned) return 4000;

  const words = cleaned.split(/\s+/).filter(Boolean).length;
  const wpm = 220;
  const ms = (words / wpm) * 60000 + 1200;
  return clamp(ms, 3200, 11000);
}

function clamp(v, min, max) {
  return Math.max(min, Math.min(max, v));
}
