"use client";

import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { cn } from "@/lib/utils";

/**
 * Full-page mobile menu with a curved reveal.
 *
 * The panel rises behind an SVG whose leading edge starts bowed and flattens
 * as it lands — a curtain with weight, not a rectangle sliding up.
 *
 * Two things that are easy to get wrong here:
 *
 *  · The dialog must NOT paint its own background. .ca-ink-surface normally
 *    sets background-color, which would make the panel opaque before the
 *    curtain ever moves and hide the entire effect. The class is kept for its
 *    token overrides only; the SVG paints the ground.
 *
 *  · The panel MUST be portalled to <body>. It lives inside <header>, which
 *    carries backdrop-blur on inner pages — and an ancestor with backdrop-filter
 *    (or transform, or filter) becomes the containing block for position:fixed
 *    descendants. Without the portal the dialog resolves `inset-0` against the
 *    64px-tall header and renders as a thin strip. The homepage header has no
 *    blur, which is why the bug only ever showed on inner pages.
 *
 *  · Everything needs an exit, not just the curtain. Without one the copy
 *    hangs in mid-air over the page beneath while the curtain slides away,
 *    which reads as lag rather than animation. Content leaves first and
 *    faster, then the curtain follows it down.
 */

export interface CurvedMenuItem {
  to: string;
  label: string;
}

export interface CurvedMenuProps {
  items: CurvedMenuItem[];
  current?: string;
  triggerClassName?: string;
  email?: string;
}

/** Bowed leading edge → flat. Same command structure so the path can tween. */
const CURVE_BOWED = "M0 34 Q 50 -6 100 34 L100 100 L0 100 Z";
const CURVE_FLAT = "M0 0 Q 50 0 100 0 L100 100 L0 100 Z";

const EASE = [0.33, 0.1, 0.25, 1] as const;
const OPEN_MS = 0.62;
const CLOSE_MS = 0.42; // leaving is quicker than arriving — it always should be

export function CurvedMenu({
  items,
  current = "/",
  triggerClassName,
  email = "hi@carlosarias.com",
}: CurvedMenuProps) {
  const [open, setOpen] = useState(false);
  const reduce = useReducedMotion() ?? false;
  const panelRef = useRef<HTMLDivElement>(null);
  const triggerRef = useRef<HTMLButtonElement>(null);

  const [mounted, setMounted] = useState(false);
  useEffect(() => setMounted(true), []);

  useEffect(() => {
    if (!open) return;
    const previous = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      document.body.style.overflow = previous;
    };
  }, [open]);

  useEffect(() => {
    if (!open) return;
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") close();
    };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [open]);

  useEffect(() => {
    if (open) panelRef.current?.focus();
  }, [open]);

  const close = () => {
    setOpen(false);
    triggerRef.current?.focus();
  };

  const isActive = (to: string) =>
    to === "/" ? current === "/" : current.startsWith(to);

  const bar = "absolute left-0 h-[1.5px] w-full rounded-full bg-current";

  return (
    <>
      {/* The trigger is also the close control: it lifts above the overlay
          when open and its two bars cross into an X. */}
      <button
        ref={triggerRef}
        type="button"
        onClick={() => setOpen(true)}
        aria-label="Open menu"
        aria-expanded={open}
        aria-haspopup="dialog"
        className={cn(triggerClassName)}
      >
        <span className="relative block h-[14px] w-[22px]" aria-hidden="true">
          <span className={cn(bar, "top-[3px]")} />
          <span className={cn(bar, "top-[11px]")} />
        </span>
      </button>

      {mounted &&
        createPortal(
          <AnimatePresence>
            {open && (
          <motion.div
            key="curved-menu"
            role="dialog"
            aria-modal="true"
            aria-label="Site menu"
            ref={panelRef}
            tabIndex={-1}
            /* bg-transparent is load-bearing — see the note at the top. */
            className="ca-ink-surface fixed inset-0 z-[999] !bg-transparent outline-none"
          >
            {reduce ? (
              <div className="absolute inset-0 bg-background" />
            ) : (
              <motion.svg
                className="absolute inset-0 h-full w-full"
                viewBox="0 0 100 100"
                preserveAspectRatio="none"
                aria-hidden="true"
                initial={{ y: "100%" }}
                animate={{ y: "0%" }}
                exit={{ y: "100%", transition: { duration: CLOSE_MS, ease: EASE } }}
                transition={{ duration: OPEN_MS, ease: EASE }}
              >
                <motion.path
                  fill="var(--color-background)"
                  initial={{ d: CURVE_BOWED }}
                  animate={{ d: CURVE_FLAT }}
                  exit={{ d: CURVE_BOWED, transition: { duration: CLOSE_MS, ease: EASE } }}
                  transition={{ duration: OPEN_MS, ease: EASE }}
                />
              </motion.svg>
            )}

            {/* Content leaves first and faster, so nothing is left floating
                over the page while the curtain drops. */}
            <motion.div
              className="relative flex h-full flex-col px-6 pt-6 pb-10"
              initial={reduce ? false : { opacity: 0 }}
              animate={{ opacity: 1, transition: { duration: 0.3, delay: 0.26 } }}
              exit={{ opacity: 0, y: 18, transition: { duration: 0.2, ease: EASE } }}
            >
              <div className="flex items-center justify-between">
                <span className="ca-menu-seal" aria-hidden="true">
                  印
                </span>

                {/* Lives INSIDE the portal, so it never depends on the header's
                    z-index, ground, or which variant the page rendered. */}
                <button
                  type="button"
                  onClick={close}
                  aria-label="Close menu"
                  className="-mr-3 inline-flex h-12 w-12 items-center justify-center text-foreground"
                >
                  <span className="relative block h-[14px] w-[22px]" aria-hidden="true">
                    <motion.span
                      className={cn(bar, "top-[3px]")}
                      initial={reduce ? false : { rotate: 0, y: 0 }}
                      animate={{ rotate: 45, y: 4 }}
                      exit={{ rotate: 0, y: 0 }}
                      transition={
                        reduce
                          ? { duration: 0 }
                          : { duration: 0.34, delay: 0.16, ease: [0.2, 0.9, 0.3, 1] }
                      }
                    />
                    <motion.span
                      className={cn(bar, "top-[11px]")}
                      initial={reduce ? false : { rotate: 0, y: 0 }}
                      animate={{ rotate: -45, y: -4 }}
                      exit={{ rotate: 0, y: 0 }}
                      transition={
                        reduce
                          ? { duration: 0 }
                          : { duration: 0.34, delay: 0.16, ease: [0.2, 0.9, 0.3, 1] }
                      }
                    />
                  </span>
                </button>
              </div>

              <nav
                className="mt-auto mb-auto flex flex-col gap-1"
                aria-label="Mobile navigation"
              >
                {items.map((item, i) => {
                  const active = isActive(item.to);
                  return (
                    <motion.a
                      key={item.to}
                      href={item.to}
                      aria-current={active ? "page" : undefined}
                      className="ca-menu-item"
                      initial={reduce ? false : { opacity: 0, y: 22 }}
                      animate={{
                        opacity: 1,
                        y: 0,
                        transition: reduce
                          ? { duration: 0 }
                          : { duration: 0.5, delay: 0.3 + i * 0.07, ease: EASE },
                      }}
                    >
                      <span className={active ? "ca-brushed" : undefined}>
                        {item.label}
                      </span>
                      <span className="ca-menu-index" aria-hidden="true">
                        {String(i + 1).padStart(2, "0")}
                      </span>
                    </motion.a>
                  );
                })}
              </nav>

              <motion.div
                className="flex flex-col gap-1"
                initial={reduce ? false : { opacity: 0 }}
                animate={{
                  opacity: 1,
                  transition: reduce ? { duration: 0 } : { duration: 0.5, delay: 0.72 },
                }}
              >
                <span className="font-mono text-[0.7rem] tracking-[0.16em] text-[var(--ca-ink-4)] uppercase">
                  Get in touch
                </span>
                <a href={`mailto:${email}`} className="ca-menu-mail">
                  {email}
                </a>
              </motion.div>
            </motion.div>
          </motion.div>
            )}
          </AnimatePresence>,
          document.body,
        )}
    </>
  );
}

export default CurvedMenu;
