"use client";

import { useEffect, useId, useRef, useState, type CSSProperties, type ReactNode } from "react";
import type { SectionAnimation } from "@/lib/blocks/layout";

/** Live countdown. Renders the same text on the server and the first client paint, then ticks. */
export function CountdownClock({ target, endedText }: { target: string; endedText: string }) {
  const [now, setNow] = useState<number | null>(null);
  useEffect(() => {
    const tick = () => setNow(Date.now());
    const first = setTimeout(tick, 0);
    const t = setInterval(tick, 1000);
    return () => { clearTimeout(first); clearInterval(t); };
  }, []);
  const left = now === null ? null : Math.max(0, Date.parse(target) - now);
  if (left === 0) return <p className="text-center text-lg font-semibold">{endedText}</p>;
  const s = left === null ? 0 : Math.floor(left / 1000);
  const parts: [string, number][] = [["Days", Math.floor(s / 86400)], ["Hours", Math.floor((s % 86400) / 3600)], ["Mins", Math.floor((s % 3600) / 60)], ["Secs", s % 60]];
  return (
    <div role="timer" aria-label="Time until the event starts" className="grid grid-cols-4 gap-4 text-center">
      {parts.map(([label, n]) => (
        <div key={label}>
          <div className="text-4xl font-light tabular-nums sm:text-6xl">{left === null ? "--" : String(n).padStart(2, "0")}</div>
          <div className="mt-1 text-xs uppercase tracking-widest opacity-80">{label}</div>
        </div>
      ))}
    </div>
  );
}

/** Slides with previous/next buttons and dots. Only the current slide is announced. */
export function CarouselView({ slides }: { slides: ReactNode[] }) {
  const [i, setI] = useState(0);
  const n = slides.length;
  if (n === 0) return null;
  const go = (k: number) => setI((k + n) % n);
  return (
    <div role="group" aria-roledescription="carousel" aria-label="Slides">
      <div aria-live="polite">{slides.map((s, k) => <div key={k} role="group" aria-roledescription="slide" aria-label={`${k + 1} of ${n}`} hidden={k !== i}>{s}</div>)}</div>
      {n > 1 ? (
        <div className="mt-4 flex items-center justify-center gap-3">
          <button type="button" onClick={() => go(i - 1)} aria-label="Previous slide" className="blk-btn blk-btn-inverse border border-[var(--border)] px-3">‹</button>
          <div className="flex gap-2">{slides.map((_, k) => <button key={k} type="button" onClick={() => go(k)} aria-label={`Go to slide ${k + 1}`} aria-current={k === i ? "true" : undefined} className={`size-2.5 rounded-full ${k === i ? "bg-[var(--primary)]" : "bg-[var(--border)]"}`} />)}</div>
          <button type="button" onClick={() => go(i + 1)} aria-label="Next slide" className="blk-btn blk-btn-inverse border border-[var(--border)] px-3">›</button>
        </div>
      ) : null}
    </div>
  );
}

/**
 * A row of logos that moves on by one every few seconds and loops. `perView` logos are visible on wide screens
 * (fewer on small screens). Previous/next buttons; pauses while hovered or focused; no auto-play for reduced-motion visitors.
 */
export function LogoSliderView({ children, perView, label, interval = 3000 }: { children: ReactNode[]; perView: number; label: string; interval?: number }) {
  const track = useRef<HTMLUListElement>(null);
  const [paused, setPaused] = useState(false);
  const step = (dir: 1 | -1) => {
    const el = track.current;
    const item = el?.firstElementChild as HTMLElement | null;
    if (!el || !item) return;
    const w = item.getBoundingClientRect().width;
    const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 4;
    const atStart = el.scrollLeft <= 4;
    if (dir === 1 && atEnd) el.scrollTo({ left: 0, behavior: "smooth" });
    else if (dir === -1 && atStart) el.scrollTo({ left: el.scrollWidth, behavior: "smooth" });
    else el.scrollBy({ left: dir * w, behavior: "smooth" });
  };
  useEffect(() => {
    if (paused || children.length <= 1 || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const t = setInterval(() => step(1), interval);
    return () => clearInterval(t);
  });
  return (
    <div role="group" aria-roledescription="carousel" aria-label={label} className="blk-logo-slider relative" style={{ "--per-view": perView } as CSSProperties}
      onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)} onFocus={() => setPaused(true)} onBlur={() => setPaused(false)}>
      <ul ref={track} className="flex snap-x snap-mandatory overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
        {children.map((c, k) => (
          <li key={k} aria-roledescription="slide" aria-label={`${k + 1} of ${children.length}`} className="shrink-0 snap-start px-2 [width:calc(100%/2)] sm:[width:calc(100%/3)] lg:[width:calc(100%/var(--per-view))]">{c}</li>
        ))}
      </ul>
      {children.length > 1 ? (
        <div className="mt-4 flex justify-center gap-3">
          <button type="button" onClick={() => step(-1)} aria-label="Previous logos" className="blk-btn blk-btn-inverse border border-[var(--border)] px-3">‹</button>
          <button type="button" onClick={() => step(1)} aria-label="Next logos" className="blk-btn blk-btn-inverse border border-[var(--border)] px-3">›</button>
        </div>
      ) : null}
    </div>
  );
}

/** Accessible tabs (arrow keys move between tabs). */
export function TabsView({ titles, panels }: { titles: string[]; panels: ReactNode[] }) {
  const [i, setI] = useState(0);
  const id = useId();
  const n = titles.length;
  const onKey = (e: React.KeyboardEvent) => {
    const k = e.key === "ArrowRight" ? (i + 1) % n : e.key === "ArrowLeft" ? (i - 1 + n) % n : e.key === "Home" ? 0 : e.key === "End" ? n - 1 : null;
    if (k === null) return;
    e.preventDefault();
    setI(k);
    document.getElementById(`${id}-t${k}`)?.focus();
  };
  return (
    <div>
      <div role="tablist" aria-label="Tabs" onKeyDown={onKey} className="flex flex-wrap gap-1 border-b border-[var(--border)]">
        {titles.map((t, k) => (
          <button key={k} id={`${id}-t${k}`} role="tab" type="button" aria-selected={k === i} aria-controls={`${id}-p${k}`} tabIndex={k === i ? 0 : -1} onClick={() => setI(k)}
            className={`-mb-px cursor-pointer border-b-2 px-4 py-2 text-sm font-semibold ${k === i ? "border-[var(--primary)]" : "border-transparent opacity-70 hover:opacity-100"}`}>{t}</button>
        ))}
      </div>
      {panels.map((p, k) => <div key={k} id={`${id}-p${k}`} role="tabpanel" aria-labelledby={`${id}-t${k}`} hidden={k !== i} className="pt-4">{p}</div>)}
    </div>
  );
}

/**
 * A section that fades/slides in when it scrolls into view. Server HTML is fully visible (works without JavaScript);
 * only sections that start below the fold are hidden and then revealed. Skipped for visitors who prefer reduced motion.
 */
export function Reveal({ anim, className, style, sectionId, children }: { anim: SectionAnimation; className?: string; style?: CSSProperties; sectionId: string; children: ReactNode }) {
  const ref = useRef<HTMLDivElement>(null);
  useEffect(() => {
    const el = ref.current;
    if (!el || window.matchMedia("(prefers-reduced-motion: reduce)").matches || typeof IntersectionObserver === "undefined") return;
    if (el.getBoundingClientRect().top < window.innerHeight * 0.9) return;
    el.dataset.anim = "armed";
    const io = new IntersectionObserver((entries) => {
      if (entries.some((e) => e.isIntersecting)) {
        // Two frames so the hidden state is painted before the transition starts.
        requestAnimationFrame(() => requestAnimationFrame(() => { el.dataset.anim = "in"; }));
        io.disconnect();
      }
    }, { threshold: 0.12 });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return (
    <div ref={ref} className={className} data-section={sectionId} data-anim-type={anim.type} style={{ ...style, "--anim-d": `${anim.duration}ms`, "--anim-delay": `${anim.delay}ms` } as CSSProperties}>
      {children}
    </div>
  );
}
