const stage = document.getElementById("stage");
const introGate = document.getElementById("introGate");
const invitationScene = document.getElementById("invitationScene");
const envelopeWrap = document.getElementById("envelopeWrap");
const particlesLayer = document.getElementById("particles");
const angelImage = document.querySelector(".angel img");
const heroVideo = null; // replaced by static image
const daysValue = document.getElementById("daysValue");
const hoursValue = document.getElementById("hoursValue");
const minutesValue = document.getElementById("minutesValue");
const secondsValue = document.getElementById("secondsValue");
const weddingDate = new Date("2026-05-10T00:00:00+05:30");

let isOpen = false;
let introStarted = false;

const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

function setFallbackImage() {
  if (!angelImage) return;

  angelImage.addEventListener("error", () => {
    const fallback = angelImage.dataset.fallbackSrc;
    if (fallback && !angelImage.src.endsWith(fallback)) {
      angelImage.src = fallback;
    }
  });
}

function createParticles() {
  const particleCount = prefersReducedMotion ? 16 : 56;

  for (let index = 0; index < particleCount; index += 1) {
    const particle = document.createElement("span");
    const size = randomBetween(2, 5.8);
    particle.className = "particle";
    particle.style.setProperty("--x", `${randomBetween(0, 100)}%`);
    particle.style.setProperty("--y", `${randomBetween(0, 100)}%`);
    particle.style.setProperty("--size", `${size}px`);
    particle.style.setProperty("--opacity", randomBetween(0.18, 0.82).toFixed(2));
    particle.style.setProperty("--duration", `${randomBetween(4.8, 11).toFixed(2)}s`);
    particle.style.setProperty("--delay", `${randomBetween(-9, 0).toFixed(2)}s`);
    particle.style.setProperty("--move-x", `${randomBetween(-18, 18).toFixed(1)}px`);
    particle.style.setProperty("--move-y", `${randomBetween(-34, 16).toFixed(1)}px`);
    particlesLayer.appendChild(particle);
  }
}

/* Scroll-linked intro: a 100vh runway above the stage drives a 0→1
   progress (`--p`) on the gate. Bg cells & poster interpolate off it,
   so the disintegration tracks scroll exactly. One rAF write per frame. */
const introRunway = document.getElementById("introRunway");
let introTicking = false;
let introDone = false;

function applyIntroProgress(p) {
  if (!introGate) return;
  const clamped = Math.max(0, Math.min(1, p));
  introGate.style.setProperty("--p", clamped.toFixed(4));

  // Reveal the stage early so it's already painted behind the fading gate.
  if (clamped > 0.02 && !isOpen) {
    isOpen = true;
    document.body.classList.add("is-open");
    if (stage) stage.classList.add("is-open");
  }

  if (clamped >= 0.999) {
    if (!introDone) {
      introDone = true;
      introGate.classList.add("is-done");
      document.body.classList.add("intro-complete");
      // Auto-open envelope when intro scroll completes — no click required.
      if (envelopeWrap) envelopeWrap.classList.add("is-opening", "is-open");
    }
  } else if (introDone) {
    introDone = false;
    introGate.classList.remove("is-done");
    document.body.classList.remove("intro-complete");
  }
}

function onIntroScroll() {
  if (introTicking) return;
  introTicking = true;
  requestAnimationFrame(() => {
    const runwayHeight = introRunway ? introRunway.offsetHeight : window.innerHeight;
    const p = runwayHeight > 0 ? window.scrollY / runwayHeight : 1;
    applyIntroProgress(p);
    introTicking = false;
  });
}

function startIntro() {
  window.scrollTo({ top: window.innerHeight, behavior: "smooth" });
}

function revealInvitation({ delay = 0 } = {}) {
  if (isOpen) return;

  isOpen = true;

  window.setTimeout(() => {
    document.body.classList.add("is-open");
    stage.classList.add("is-open");
    envelopeWrap.classList.add("is-opening", "is-open");
    playHeroVideo({ restart: true });
    
    // Add intro-complete after the envelope has finished its opening animation (1.5s)
    window.setTimeout(() => {
      document.body.classList.add("intro-complete");
    }, 1500);
  }, delay);
}

function openInvitation() {
  revealInvitation({
    delay: prefersReducedMotion ? 40 : 430,
  });
}

function replayInvitation() {
  isOpen = false;
  introStarted = false;

  document.body.classList.remove("is-open", "intro-complete");
  stage.classList.remove("is-open");
  envelopeWrap.classList.remove("is-open", "is-opening");

  window.scrollTo({ top: 0, behavior: prefersReducedMotion ? "auto" : "smooth" });

  if (introGate) {
    introGate.classList.remove("is-playing", "is-hidden");
  }

  // Reset card & bg cell animations
  const poster = document.getElementById("introPoster");
  if (poster) poster.classList.remove("is-gone-up");
  document.querySelectorAll(".intro-bg-cell").forEach(cell => {
    cell.classList.remove("is-gone-left", "is-gone-right");
  });

  if (heroVideo) {
    heroVideo.pause();
    try { heroVideo.currentTime = 0; } catch (_) {}
  }

  void envelopeWrap.offsetWidth;
}

function updateParallax(event) {
  if (prefersReducedMotion) return;

  const rect = invitationScene.getBoundingClientRect();
  const x = (event.clientX - rect.left) / rect.width - 0.5;
  const y = (event.clientY - rect.top) / rect.height - 0.5;

  stage.style.setProperty("--parallax-x", `${x * 24}px`);
  stage.style.setProperty("--parallax-y", `${y * 18}px`);
}

function resetParallax() {
  stage.style.setProperty("--parallax-x", "0px");
  stage.style.setProperty("--parallax-y", "0px");
}

function playHeroVideo() {
  // no-op: video replaced with static image
}

function updateCountdown() {
  if (!daysValue || !hoursValue || !minutesValue || !secondsValue) return;

  const remaining = Math.max(weddingDate.getTime() - Date.now(), 0);
  const day    = 24 * 60 * 60 * 1000;
  const hour   = 60 * 60 * 1000;
  const minute = 60 * 1000;
  const second = 1000;
  const days    = Math.floor(remaining / day);
  const hours   = Math.floor((remaining % day) / hour);
  const minutes = Math.floor((remaining % hour) / minute);
  const seconds = Math.floor((remaining % minute) / second);

  function setWithFlip(el, val, pad) {
    const next = String(val).padStart(pad, "0");
    if (el.textContent === next) return;
    el.textContent = next;
    el.classList.remove("is-flipping");
    void el.offsetWidth;
    el.classList.add("is-flipping");
  }

  setWithFlip(daysValue,    days,    2);
  setWithFlip(hoursValue,   hours,   2);
  setWithFlip(minutesValue, minutes, 2);
  setWithFlip(secondsValue, seconds, 2);
}

function randomBetween(min, max) {
  return Math.random() * (max - min) + min;
}

setFallbackImage();
createParticles();
updateCountdown();

window.setInterval(updateCountdown, 1000);

// Always wire up the scroll listener — the intro must always be visible
// on page load regardless of prefers-reduced-motion. The CSS media query
// handles simplifying motion for those users; we never skip the intro display.
window.addEventListener("scroll", onIntroScroll, { passive: true });
window.addEventListener("resize", onIntroScroll, { passive: true });
onIntroScroll();

envelopeWrap.addEventListener("click", openInvitation);
envelopeWrap.addEventListener("keydown", (event) => {
    if (event.key === "Enter" || event.key === " ") {
      event.preventDefault();
      openInvitation(event);
    }
  });

  invitationScene.addEventListener("mousemove", updateParallax);
invitationScene.addEventListener("mouseleave", resetParallax);

/* â”€â”€ Global Reveal Observer â”€â”€ */
const revealObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add("active");
      // Optional: unobserve if you only want it to animate once
      // revealObserver.unobserve(entry.target);
    }
  });
}, { 
  threshold: 0.12,
  rootMargin: "0px 0px -50px 0px" // Trigger slightly before it enters fully
});

document.querySelectorAll(".reveal").forEach(section => {
  revealObserver.observe(section);
});

