"use client";

import Link from "next/link";
import { useEffect, useRef, useState, useSyncExternalStore } from "react";
import { createPortal } from "react-dom";
import styles from "./HeroBanner.module.css";
import { QueueSection } from "@/components/motion/QueueSection";
import { QueueItem } from "@/components/motion/QueueItem";

// Official K-mark path data (also extracted standalone to
// public/brand/k-mark.svg) — kept inline here too so the 3 stacked layers
// below can reference a single same-document <path> via <use href="#...">,
// matching the approved reference's own technique exactly.
const K_MARK_PATH =
  "M8956.143,2707.143C8956.143,2684.546 8965.119,2662.874 8981.098,2646.895C8997.076,2630.917 9018.748,2621.94 9041.345,2621.94L9041.857,2621.94L9041.857,2621.429C9064.59,2621.429 9086.392,2630.459 9102.466,2646.534C9118.541,2662.608 9127.571,2684.41 9127.571,2707.143L9127.571,2820.907C9127.571,2832.674 9133.899,2843.533 9144.137,2849.334C9154.376,2855.134 9166.942,2854.982 9177.036,2848.934L9598.016,2596.697C9606.962,2591.336 9618.1,2591.201 9627.175,2596.342C9636.249,2601.484 9641.857,2611.108 9641.857,2621.537L9641.857,2843.117C9641.857,2863.637 9627.158,2881.211 9606.961,2884.838L9165.019,2964.205C9164.721,2964.259 9164.419,2964.286 9164.117,2964.286C9164.419,2964.286 9164.721,2964.313 9165.019,2964.366L9606.961,3043.733C9627.158,3047.36 9641.857,3064.934 9641.857,3085.454L9641.857,3307.034C9641.857,3317.464 9636.249,3327.088 9627.175,3332.229C9618.1,3337.37 9606.962,3337.235 9598.016,3331.875L9177.036,3079.638C9166.942,3073.59 9154.376,3073.437 9144.137,3079.238C9133.899,3085.039 9127.571,3095.897 9127.571,3107.664L9127.571,3221.443C9127.571,3244.172 9118.542,3265.97 9102.47,3282.042C9086.399,3298.114 9064.6,3307.143 9041.871,3307.143L9041.857,3307.143C9019.124,3307.143 8997.322,3298.112 8981.248,3282.038C8965.173,3265.963 8956.143,3244.161 8956.143,3221.429L8956.143,2707.143ZM8998.429,3221.429L8999.857,3221.429C9023.053,3221.429 9041.857,3202.625 9041.857,3179.429L9041.857,3083.839C9041.857,3017.811 9095.383,2964.286 9161.41,2964.286C9095.383,2964.286 9041.857,2910.76 9041.857,2844.733L9041.857,2749.143C9041.857,2725.947 9023.053,2707.143 8999.857,2707.143C8999.857,2707.143 8999.857,2707.143 8999.857,2707.143C8999.068,2707.143 8998.429,2707.782 8998.429,2708.571C8998.429,2738.28 8998.429,3221.429 8998.429,3221.429Z";

const K_MARK_ID = "hero-k-mark";
const GRAIN_FILTER_ID = "hero-grain-filter";

// FR-SIG-003: real signature-event data already flows into this component
// via the `signatureEvent` prop (sourced from getSignatureEvent() in
// page.tsx) — swap in a live fetch here only if this component ever needs
// to load it independently of the page.
// TODO: wire up any additional live event fields (e.g. imagery) once the
// API exposes them for the hero.

export interface HeroBannerProps {
  signatureEvent?: {
    title: string;
    slug: string;
  };
}

/**
 * Single place to edit what the hero says/links to when a Signature Event
 * is (or isn't) active — kept separate from the 3D canvas/K-mark rendering
 * mechanics below, which don't depend on signature-event data at all.
 */
function resolveHeroCopy(signatureEvent: HeroBannerProps["signatureEvent"]) {
  const [titleLine1, titleLine2]: [string, string] = ["", "KOLABORATIV"];

  return {
    titleLine1,
    titleLine2,
    ctaHref: "/events",
    ctaLabel: signatureEvent ? "Event for you" : "Jelajahi Event",
  };
}

export function HeroBanner({ signatureEvent }: HeroBannerProps) {
  const sectionRef = useRef<HTMLElement>(null);
  const canvasRef = useRef<HTMLDivElement>(null);
  const layerRefs = useRef<Array<HTMLDivElement | null>>([]);
  // TEMP DEBUG — remove once the no-cursor auto-float issue is confirmed
  // fixed on a real phone. Shows what the effect actually detected, since a
  // screenshot can't reveal whether the rAF loop is running.
  const debugRef = useRef<HTMLDivElement>(null);

  // The floating desktop CTA is portaled to <body> (see below) so it isn't
  // affected by page.tsx's Hero/About stacking-context dance — only render
  // the portal client-side, to avoid an SSR document-not-defined crash.
  // Client-only mount flag without a setState-in-effect (react-hooks/
  // set-state-in-effect) — useSyncExternalStore's server snapshot is always
  // false, client snapshot always true, no subscription ever fires.
  const mounted = useSyncExternalStore(
    () => () => {},
    () => true,
    () => false,
  );

  // Stays position: fixed (bottom-right) the whole time — it never travels
  // into Event's own territory at all. Hides right as Event *starts*
  // sliding over About, not only once About's own box is fully scrolled
  // past: EventStackSection's wrapper (#home-event-section, in page.tsx) is
  // pulled up by `md:-mt-[100vh]` to visually cover About's tail end, so by
  // the time About's own box bottom would've cleared the viewport, Event
  // has *already* been fully on screen for up to 100vh of scroll — checking
  // About's bottom alone left the CTA floating over Event's own content.
  // Checking #home-event-section's top instead ties the fade to the exact
  // moment Event starts entering from below, which is where About's own
  // content visually ends. Falls back to the old About-bottom check when
  // there's no Event section at all (events.length === 0 in page.tsx, so
  // #home-event-section is never rendered).
  const floatingCtaRef = useRef<HTMLAnchorElement>(null);
  const [visible, setVisible] = useState(true);
  useEffect(() => {
    const about = document.getElementById("home-about-section");
    if (!about) return;

    let rafId = 0;
    function check() {
      rafId = 0;
      const eventSection = document.getElementById("home-event-section");
      if (eventSection) {
        const eventTop = eventSection.getBoundingClientRect().top;
        setVisible(eventTop > window.innerHeight);
      } else {
        const aboutBottom = about!.getBoundingClientRect().bottom;
        setVisible(aboutBottom > window.innerHeight);
      }
    }
    function onScroll() {
      if (!rafId) rafId = requestAnimationFrame(check);
    }

    check();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      if (rafId) cancelAnimationFrame(rafId);
    };
  }, []);

  useEffect(() => {
    const section = sectionRef.current;
    const canvas = canvasRef.current;
    if (!section || !canvas) return;

    const layers = layerRefs.current.filter((el): el is HTMLDivElement => el !== null);
    const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    // Cursor capability, not screen size — a large touchscreen (e.g. a
    // touch laptop/desktop monitor) has no hovering pointer either, so it
    // should auto-float too, not wait for a mousemove that will never come.
    const hasCursor = window.matchMedia("(hover: hover) and (pointer: fine)").matches;

    let entranceTimeout: ReturnType<typeof setTimeout> | undefined;
    let entranceSettleTimeout: ReturnType<typeof setTimeout> | undefined;
    let floatRafId = 0;
    // No-cursor devices drive `canvas.style.transform` from this loop once
    // per animation frame with a tiny sine-wave delta. The .canvas3d CSS
    // class (see HeroBanner.module.css) carries its own permanent
    // `transition: transform 0.8s`, and the entrance pop-in below adds a
    // 1.2s inline one on top of that — either is far longer than a single
    // frame, so each new target arrives before the previous transition gets
    // anywhere close, and the motion reads as nearly frozen instead of
    // floating. Auto-float needs the transition fully off (not just the
    // inline override cleared, since clearing it would only fall back to
    // the CSS class's own 0.8s) so its own per-frame math is what produces
    // the smoothness.
    const autoFloat = !hasCursor && !reduced;
    if (autoFloat) {
      canvas.style.transition = "none";
      // Same reasoning as the canvas: .layer's own CSS class (see
      // HeroBanner.module.css) carries a permanent `transition: transform
      // 0.5s`, which would smear out the per-frame layer updates below into
      // the same near-frozen look the canvas had — the layers need it off
      // too so the extruded depth effect actually reads as moving.
      layers.forEach((layer) => {
        layer.style.transition = "none";
      });
    }

    if (debugRef.current) {
      debugRef.current.textContent =
        `hover:hover+pointer:fine=${hasCursor} reduced-motion=${reduced} ` +
        `touch=${"ontouchstart" in window || navigator.maxTouchPoints > 0} => autoFloat=${autoFloat}`;
    }

    if (!reduced) {
      canvas.style.opacity = "0";
      canvas.style.transform = "rotateX(90deg) rotateZ(0deg) scale(0.85)";
      entranceTimeout = setTimeout(() => {
        canvas.style.transition = "all 1.2s cubic-bezier(0.16, 1, 0.3, 1)";
        canvas.style.opacity = "1";
        canvas.style.transform = "rotateX(55deg) rotateZ(-25deg) scale(1)";

        if (autoFloat) {
          entranceSettleTimeout = setTimeout(() => {
            canvas.style.transition = "none";
          }, 1200);
        }
      }, 250);
    } else {
      canvas.style.transform = "rotateX(55deg) rotateZ(-25deg)";
    }

    // Shared by both the mousemove path and the no-cursor auto-float path
    // below so mobile renders the *identical* look to desktop — not just
    // the outer tilt, but each layer's translateZ + parallax offset, which
    // is what actually reveals the stacked depth (the tinted under-layers
    // peeking out from behind the top one) that reads as a solid extruded
    // K-mark instead of one flat orange shape. x/y are in the same "virtual
    // mouse offset" units handleMouseMove derives from the cursor.
    function applyTilt(x: number, y: number) {
      canvas!.style.transform = `rotateX(${55 + y / 2}deg) rotateZ(${-25 + x / 2}deg)`;

      layers.forEach((layer, i) => {
        const depth = (i + 1) * 15;
        layer.style.transform = `translateZ(${depth}px) translate(${x * (i + 1) * 0.2}px, ${y * (i + 1) * 0.2}px)`;
      });
    }

    function handleMouseMove(e: MouseEvent) {
      const rect = section!.getBoundingClientRect();
      const x = (rect.width / 2 - (e.clientX - rect.left)) / 25;
      const y = (rect.height / 2 - (e.clientY - rect.top)) / 25;
      applyTilt(x, y);
    }

    if (hasCursor && !reduced) {
      section.addEventListener("mousemove", handleMouseMove);
    } else if (autoFloat) {
      // requestAnimationFrame instead of setInterval: stays locked to the
      // display's actual refresh cadence (no drift, no sub-frame jank from
      // firing between paints), and the browser automatically throttles/
      // pauses it when the tab is backgrounded — setInterval would keep
      // firing and burning battery on a hidden tab. `t` advances by elapsed
      // ms (not a fixed per-tick step) so the float speed stays identical
      // regardless of the device's actual frame rate.
      let t = 0;
      let lastTime = performance.now();
      const tick = (now: number) => {
        t += (now - lastTime) * 0.001;
        lastTime = now;
        // Same units as handleMouseMove's x/y (post the /25 divide) — a
        // virtual pointer sweeping a small ellipse, so applyTilt drives the
        // canvas rotation AND the per-layer depth/parallax exactly as it
        // would from a real mousemove, at the same rotateX/rotateZ ±5°
        // amplitude the auto-float used before this shared helper existed.
        const x = Math.cos(t * 0.7) * 10;
        const y = Math.sin(t) * 10;
        applyTilt(x, y);
        floatRafId = requestAnimationFrame(tick);
      };
      floatRafId = requestAnimationFrame(tick);
    }

    return () => {
      if (entranceTimeout) clearTimeout(entranceTimeout);
      if (entranceSettleTimeout) clearTimeout(entranceSettleTimeout);
      if (floatRafId) cancelAnimationFrame(floatRafId);
      section.removeEventListener("mousemove", handleMouseMove);
    };
  }, []);

  const { titleLine1, titleLine2, ctaHref, ctaLabel } = resolveHeroCopy(signatureEvent);

  return (
    <>
    <section ref={sectionRef} id="hero" className={styles.hero}>
      <svg style={{ position: "absolute", width: 0, height: 0 }} aria-hidden="true">
        <filter id={GRAIN_FILTER_ID}>
          <feTurbulence type="fractalNoise" baseFrequency={0.65} numOctaves={3} />
          <feColorMatrix type="saturate" values="0" />
        </filter>
      </svg>
      <div className={styles.heroGrain} style={{ filter: `url(#${GRAIN_FILTER_ID})` }} />

      <svg style={{ position: "absolute", width: 0, height: 0 }} aria-hidden="true">
        <defs>
          <path id={K_MARK_ID} d={K_MARK_PATH} />
        </defs>
      </svg>

      <div className={styles.heroContours} />

      

      <div className={styles.viewport}>
        <div ref={canvasRef} className={styles.canvas3d}>
          <div className={styles.layerFloor} />
          <div
            ref={(el) => {
              layerRefs.current[0] = el;
            }}
            className={`${styles.layer} ${styles.layer1}`}
            aria-hidden="true"
          >
            <svg viewBox="8940 2575 718 778">
              <use href={`#${K_MARK_ID}`} className={styles.kFill} />
            </svg>
          </div>
          <div
            ref={(el) => {
              layerRefs.current[1] = el;
            }}
            className={`${styles.layer} ${styles.layer2}`}
            aria-hidden="true"
          >
            <svg viewBox="8940 2575 718 778">
              <use href={`#${K_MARK_ID}`} className={styles.kFill} />
            </svg>
          </div>
          <div
            ref={(el) => {
              layerRefs.current[2] = el;
            }}
            className={`${styles.layer} ${styles.layer3}`}
            aria-hidden="true"
          >
            <svg viewBox="8940 2575 718 778">
              <use href={`#${K_MARK_ID}`} className={styles.kFill} />
            </svg>
          </div>
        </div>
      </div>

      <QueueSection as="div" className={styles.heroContent} amount={0.1} stagger={0.18}>
        <div className={styles.heroMeta} aria-hidden="true" />

        <div className={styles.heroHeadline}>
          <QueueItem as="h1" className={styles.heroTitle} distance={32}>
            {titleLine1}
            {titleLine1 && <br />}
            <em>{titleLine2}</em>
          </QueueItem>

          <QueueItem as="p" className={styles.heroSubtitle}>
            Your Ticketing and Collaboration Partner
          </QueueItem>
        </div>

        <div className={styles.heroFooter}>
          <div className={styles.ctaInline}>
            <Link href={ctaHref} className={styles.ctaButton}>
              {ctaLabel}
            </Link>
          </div>
        </div>
      </QueueSection>

      <div className={styles.scrollHint} />
    </section>

    {mounted &&
      createPortal(
        <Link
          ref={floatingCtaRef}
          href={ctaHref}
          className={`${styles.ctaButton} ${styles.ctaFloating}`}
          style={{
            opacity: visible ? 1 : 0,
            pointerEvents: visible ? "auto" : "none",
            transition: "opacity 0.3s ease-out",
          }}
        >
          {ctaLabel}
        </Link>,
        document.body,
      )}
    </>
  );
}
