"use client";

import { useEffect, useRef } from "react";
import Image from "next/image";
import Link from "next/link";
import { AnimatePresence, motion } from "motion/react";
import { useNewsletterSignup } from "../lib/useNewsletterSignup";

const features = [
  {
    icon: "/icon-sparkle-wrapper.svg",
    title: "7 Tage. Ein Fokus.",
    desc: "Wöchentliche Impulse für mehr Klarheit und weniger Reibung.",
  },
  {
    icon: "/icon-checklist.png",
    title: "Praktisch & umsetzbar.",
    desc: "Direkt anwendbare Methoden für deinen Alltag.",
  },
  {
    icon: "/icon-heart.png",
    title: "Kein Spam. Versprochen.",
    desc: "Nur wertvolle Inhalte, wenn du sie brauchst.",
  },
];

/**
 * The Navbar's "Newsletter" CTA opens this modal rather than navigating —
 * matches the original Figma prototype (Newsletter-Button → Overlay
 * newsletter-overlay), unlike the Werkzeuge "Impulse & Tipps" card's
 * "Anmelden" link, which goes to the full /newsletter detail page instead.
 * Different entry points, different weight: a quick-access nav CTA gets a
 * lightweight in-place signup, a content card gets the full page.
 */
export function NewsletterModal({ open, onClose }: { open: boolean; onClose: () => void }) {
  const dialogRef = useRef<HTMLDivElement>(null);
  const closeButtonRef = useRef<HTMLButtonElement>(null);
  const { email, emailError, consent, handleConsentChange, status, error, successMessage, emailRef, handleEmailChange, handleEmailBlur, handleSubmit } =
    useNewsletterSignup("newsletter-modal");

  // Background scroll lock while open — intercepts and cancels the wheel/
  // touch input that would cause scrolling, instead of toggling
  // overflow/position on body or html. Two earlier approaches (plain
  // overflow:hidden on body; position:fixed with a negative top offset to
  // compensate) each fixed one symptom while causing another — overflow
  // alone reset scrollY to 0 for a frame when opened away from the top of
  // the page (e.g. parked at #ueber-bjoern), and position:fixed took body
  // out of normal flow, which detached the Navbar's `position: sticky`
  // from its scrolling container and made it visibly snap. Neither is a
  // risk here: scrollY, overflow and layout are never touched at all, so
  // there's nothing that can jump, reflow, or need restoring on close —
  // the events that would move the page just never get to.
  // `{ passive: false }` is required for preventDefault() to have any
  // effect on wheel/touchmove. Events that originate inside the dialog
  // (which has its own overflow-y-auto) are let through untouched, so the
  // modal's own content still scrolls normally.
  useEffect(() => {
    if (!open) return;

    const isInsideDialog = (target: EventTarget | null) =>
      target instanceof Node && !!dialogRef.current?.contains(target);

    const onWheel = (e: WheelEvent) => {
      if (!isInsideDialog(e.target)) e.preventDefault();
    };
    const onTouchMove = (e: TouchEvent) => {
      if (!isInsideDialog(e.target)) e.preventDefault();
    };

    document.addEventListener("wheel", onWheel, { passive: false });
    document.addEventListener("touchmove", onTouchMove, { passive: false });
    return () => {
      document.removeEventListener("wheel", onWheel);
      document.removeEventListener("touchmove", onTouchMove);
    };
  }, [open]);

  // Focus management: move focus into the modal on open, trap Tab within
  // it, close + return focus on Escape — same pattern as the Navbar's
  // mobile drawer.
  useEffect(() => {
    if (!open) return;

    // preventScroll: true — otherwise the browser's implicit
    // scrollIntoView on focus() sees this button sitting close to the
    // viewport's top edge and "corrects" for the global
    // scroll-padding-top (reserved for the sticky Navbar, see
    // globals.css) by nudging the whole page upward — pointless here
    // since the modal is position:fixed and always fully in view
    // regardless of document scroll, but that nudge is exactly the
    // "page scrolls up a bit and the modal lands in a weird position"
    // jank on open.
    closeButtonRef.current?.focus({ preventScroll: true });

    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape") {
        e.preventDefault();
        onClose();
        return;
      }
      if (e.key !== "Tab") return;
      const focusables = dialogRef.current?.querySelectorAll<HTMLElement>(
        'a[href], button:not([disabled]), input:not([disabled])'
      );
      if (!focusables || focusables.length === 0) return;
      const first = focusables[0];
      const last = focusables[focusables.length - 1];
      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus({ preventScroll: true });
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus({ preventScroll: true });
      }
    };

    document.addEventListener("keydown", onKeyDown);
    return () => document.removeEventListener("keydown", onKeyDown);
  }, [open, onClose]);

  return (
    // AnimatePresence, not a plain `if (!open) return null` — that would
    // unmount the modal instantly on close with no way to play an exit
    // animation first. Keeping it mounted (conditionally rendering the
    // child) lets Framer Motion finish the fade-out before removing it.
    <AnimatePresence>
      {open && (
        <motion.div
          className="fixed inset-0 z-[60] flex items-center justify-center p-4 md:p-8 bg-[rgba(134,134,134,0.9)]"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.25, ease: "easeOut" }}
          onClick={(e) => {
            if (e.target === e.currentTarget) onClose();
          }}
        >
          <motion.div
            ref={dialogRef}
            role="dialog"
            aria-modal="true"
            aria-labelledby="newsletter-modal-heading"
            initial={{ opacity: 0, y: 16, scale: 0.97 }}
            animate={{ opacity: 1, y: 0, scale: 1 }}
            exit={{ opacity: 0, y: 16, scale: 0.97 }}
            transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
            // Capped narrower than the Desktop 75rem through the whole
            // 640-1023px Tablet band — this dialog is a modal, not a page
            // section, so at Tablet it should read as a compact centered
            // card, not stretch to near-full-viewport-width once stacked
            // single-column (see the flex-col/flex-row split below):
            // full-bleed-width + a single stacked photo/text column made
            // the photo huge and the text below it look lost/disconnected
            // (reported 2026-07-29, after first trying a lg:-gated
            // structural stack with no width cap). max-w-[75rem] only
            // takes over once the 2-column split itself starts at lg:.
            className="relative bg-bg-base rounded-md overflow-hidden w-full max-w-[36rem] lg:max-w-[75rem] max-h-[90vh] overflow-y-auto"
          >
        {/* sticky, not absolute — the dialog itself is the scrolling
            container (overflow-y-auto above), so an absolute-positioned
            child scrolls away with the rest of the content instead of
            staying pinned to the visible top-right corner (reported
            2026-07-29). sticky top-6 keeps it fixed to the scrolled
            viewport's top edge; ml-auto pushes it to the right within
            the dialog's normal block flow (sticky positioning doesn't
            use right-* the way absolute does); -mb-6 cancels its own
            height (size-6 = 1.5rem) so it doesn't push the modal-top
            content below it down — same visual overlap as the old
            absolute positioning, just still visible after scrolling. */}
        <button
          ref={closeButtonRef}
          type="button"
          onClick={onClose}
          aria-label="Schließen"
          className="sticky top-6 ml-auto mr-6 -mb-6 z-20 size-6 flex items-center justify-center active:scale-90 transition-transform"
        >
          <Image alt="" src="/icon-close.png" width={24} height={24} className="size-full object-contain" />
        </button>

        {/* modal-top: photo + copy/form, stacked below lg: — paired with
            the dialog's own narrower max-w-[36rem] cap through Tablet
            (see above), so the stacked photo stays a reasonably-sized
            4:3 banner instead of blowing up to near-full-viewport-width. */}
        <div className="flex flex-col lg:flex-row items-stretch border-b border-border">
          <div className="relative w-full lg:flex-1 aspect-[4/3] lg:aspect-auto">
            <Image
              src="/newsletter-modal-photo.jpg"
              alt="Notizbuch mit Kaffee und Stift"
              fill
              sizes="(min-width: 1024px) 50vw, 36rem"
              className="object-cover"
            />
          </div>

          <div className="flex flex-col gap-6 items-start justify-center flex-1 min-w-0 px-8 py-10 md:px-[4.6875rem] md:py-[6.25rem]">
            {/* -scale-y-100 is required, not just -rotate-4 — the SVG
                itself is authored upside-down (matches how Newsletter.tsx
                uses this exact same asset); without it the icon renders
                flipped. Hidden below lg: — removed on mobile 2026-07-24. */}
            <div className="hidden lg:block w-16 h-14 -rotate-4 -scale-y-100">
              <Image alt="" src="/newsletter-icon.svg" width={64} height={56} className="w-full h-full" />
            </div>

            <p
              id="newsletter-modal-heading"
              className="font-semibold text-h-feature text-text-primary leading-[1.15]"
              style={{ fontFamily: "var(--font-lora)" }}
            >
              Kleine Impulse – große Wirkung
            </p>

            <p className="text-body text-text-primary">
              Melde dich an und bekomme meine Sonntags-Impulse ab jetzt jede Woche direkt in dein Postfach: kurze Gedanken, praktische Impulse und kleine Anstöße für mehr Klarheit im Alltag.
            </p>

            {/* Quick-access signup here, full pitch (Testimonials, "So
                funktioniert's") one click away for anyone not ready to
                type an email yet — same "lightweight modal vs. full page"
                split this file's own top comment describes, just also
                offering the other side of it from within the modal
                itself instead of only via the Navbar. */}
            <Link
              href="/newsletter"
              onClick={onClose}
              className="text-label text-text-muted underline hover:text-brand transition-colors -mt-3"
            >
              Mehr über die wöchentlichen Impulse erfahren
            </Link>

            {status === "success" ? (
              <p className="text-body text-text-primary font-medium">{successMessage}</p>
            ) : (
              <form onSubmit={handleSubmit} className="flex flex-col gap-5 items-start w-full">
                <div className="flex flex-col gap-2 items-start w-full">
                  <input
                    ref={emailRef}
                    type="email"
                    required
                    value={email}
                    onChange={(e) => handleEmailChange(e.target.value)}
                    onBlur={(e) => handleEmailBlur(e.target.value)}
                    placeholder="Deine E-Mail-Adresse"
                    aria-invalid={Boolean(emailError)}
                    className={`w-full bg-bg-white border rounded-sm px-6 py-3 text-body text-text-muted font-normal outline-none transition-colors ${
                      emailError ? "border-red-600 focus:border-red-600" : "border-border focus:border-brand"
                    }`}
                  />
                  {/* Always rendered (min-h reserves one line's worth of
                      space) rather than conditionally mounted — this sits
                      inside the same row the photo on the left stretches
                      to match (items-stretch, md:aspect-auto), so an error
                      popping in and out used to grow/shrink the whole
                      modal, visibly resizing the photo along with it. */}
                  <p className="text-label text-red-600 font-normal -mt-2 min-h-[1.05rem]">{emailError}</p>
                  <button
                    type="submit"
                    disabled={status === "submitting"}
                    className="w-full bg-brand rounded-sm px-7 py-[0.875rem] font-bold text-h4 text-text-primary text-left hover:bg-brand-hover active:scale-[0.97] transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base disabled:opacity-60 disabled:pointer-events-none"
                  >
                    {status === "submitting" ? "Wird gesendet…" : "Jetzt anmelden"}
                  </button>
                </div>
                <label className="flex gap-2 items-center w-full cursor-pointer">
                  <input
                    type="checkbox"
                    required
                    checked={consent}
                    onChange={(e) => handleConsentChange(e.target.checked)}
                    className="size-4 shrink-0 rounded-xs border border-border accent-brand"
                  />
                  <span className="text-label text-text-primary">
                    Ich akzeptiere die{" "}
                    <Link
                      href="/datenschutz"
                      target="_blank"
                      rel="noopener noreferrer"
                      className="underline hover:text-brand"
                    >
                      Datenschutzerklärung
                    </Link>
                    .
                  </span>
                </label>
                {/* Same reserved-space fix as emailError above — this is
                    the "already subscribed" message, the one that actually
                    prompted it. */}
                <p className="text-label text-red-600 font-normal min-h-[1.05rem]">
                  {status === "error" ? error : ""}
                </p>
              </form>
            )}
          </div>
        </div>

        {/* modal-bottom: 3 feature cards. Figma's export had items-end on
            this row, but that's a byproduct of nested -scale-y-100
            flip-wrappers Figma uses for baseline-grid tricks (visually
            cancels out to top-alignment in the actual design) — taken
            literally it bottom-aligned the icon with the last line of
            description text instead of the title, which read as broken.
            items-start + a fixed icon bounding box (icons have different
            native proportions, e.g. the sparkle glyph isn't square) fixes
            it without needing the flip trick. */}
        {/* Same lg: exception as modal-top above, for the same narrower-
            dialog-width-through-Tablet reason. */}
        <div className="flex flex-col lg:flex-row items-start px-8 md:px-20 py-6 md:py-9 gap-8 lg:gap-6">
          {features.map((f) => (
            <div key={f.title} className="flex-1 flex gap-6 items-start w-full">
              <div className="relative h-10 w-10 shrink-0 flex items-center justify-center">
                <Image alt="" src={f.icon} fill sizes="40px" className="object-contain" />
              </div>
              <div className="flex flex-col gap-3 items-start flex-1 min-w-0">
                <p
                  className="font-semibold text-h-small text-text-primary w-full"
                  style={{ fontFamily: "var(--font-lora)" }}
                >
                  {f.title}
                </p>
                <p className="text-body text-text-primary w-full">{f.desc}</p>
              </div>
            </div>
          ))}
          </div>
          </motion.div>
        </motion.div>
      )}
    </AnimatePresence>
  );
}
