import { memo, useEffect, useCallback, useState, useMemo } from "react";
import { motion, AnimatePresence } from "motion/react";
import type { Message } from "./types";

interface MessageNavDotsProps {
  messages: Message[];
  scrollContainerRef: React.RefObject<HTMLDivElement | null>;
}

/** Elegant message timeline navigation — a visual TOC for the conversation. */
export const MessageNavDots = memo(function MessageNavDots({
  messages,
  scrollContainerRef,
}: MessageNavDotsProps) {
  const [activeIndex, setActiveIndex] = useState(0);
  const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);

  const userNodes = useMemo(() => {
    const nodes: { msgIndex: number; dotIndex: number; preview: string }[] = [];
    let dotIdx = 0;
    messages.forEach((m, i) => {
      if (m.role === "user") {
        const preview = m.content.slice(0, 36).replace(/\n/g, " ");
        nodes.push({ msgIndex: i, dotIndex: dotIdx, preview: preview || "\u2026" });
        dotIdx++;
      }
    });
    return nodes;
  }, [messages]);

  const totalDots = userNodes.length;

  // Track active index via scroll position.
  const handleScroll = useCallback(() => {
    const container = scrollContainerRef.current;
    if (!container || totalDots <= 1) return;

    const { scrollTop, scrollHeight, clientHeight } = container;
    const progress =
      scrollHeight > clientHeight
        ? Math.max(0, Math.min(1, scrollTop / (scrollHeight - clientHeight)))
        : 0;
    const idx = Math.round(progress * (totalDots - 1));
    setActiveIndex(idx);
  }, [scrollContainerRef, totalDots]);

  useEffect(() => {
    const container = scrollContainerRef.current;
    if (!container || totalDots <= 1) return;
    container.addEventListener("scroll", handleScroll, { passive: true });
    return () => container.removeEventListener("scroll", handleScroll);
  }, [handleScroll, scrollContainerRef, totalDots]);

  const handleDotClick = useCallback(
    (dotIndex: number) => {
      const container = scrollContainerRef.current;
      if (!container) return;
      const msgIndex = userNodes[dotIndex]?.msgIndex;
      if (msgIndex === undefined) return;
      const el = container.querySelector<HTMLElement>(
        `[data-msg-index="${msgIndex}"]`,
      );
      el?.scrollIntoView({ behavior: "smooth", block: "center" });
    },
    [scrollContainerRef, userNodes],
  );

  // Early return AFTER all hooks to keep them stable.
  if (totalDots <= 1) return null;

  // Fill height of the active progress line.
  const progressPct =
    totalDots > 1 ? (activeIndex / (totalDots - 1)) * 100 : 0;

  return (
    <div
      aria-label="Message navigation"
      className="absolute right-2 top-4 bottom-4 z-20 flex flex-col items-center"
      style={{ width: 40 }}
    >
      {/* Timeline container */}
      <div className="relative flex flex-1 flex-col items-center justify-between py-2">
        {/* Background track */}
        <div className="absolute inset-y-2 left-1/2 w-px -translate-x-1/2 bg-border/40" />
        {/* Active progress fill */}
        <motion.div
          className="absolute top-2 left-1/2 w-px -translate-x-1/2 bg-primary/40"
          animate={{ height: `${progressPct}%` }}
          initial={false}
          transition={{ duration: 0.35, ease: "easeOut" }}
        />

        {userNodes.map(({ msgIndex, dotIndex, preview }) => {
          const isActive = dotIndex === activeIndex;
          const isHovered = dotIndex === hoveredIndex;
          const isPast = dotIndex < activeIndex;

          return (
            <div
              key={msgIndex}
              className="relative z-10 flex items-center"
              onMouseEnter={() => setHoveredIndex(dotIndex)}
              onMouseLeave={() => setHoveredIndex(null)}
            >
              {/* Hover preview tooltip */}
              <AnimatePresence>
                {isHovered && (
                  <motion.div
                    initial={{ opacity: 0, x: 6, scale: 0.92 }}
                    animate={{ opacity: 1, x: 0, scale: 1 }}
                    exit={{ opacity: 0, x: 6, scale: 0.92 }}
                    transition={{ duration: 0.18, ease: "easeOut" }}
                    className="absolute right-8 top-1/2 -translate-y-1/2 whitespace-nowrap rounded-lg border border-border/60 bg-card/95 px-3 py-1.5 text-[11px] text-muted-foreground shadow-lg backdrop-blur-sm"
                  >
                    <span className="font-medium text-foreground">
                      {dotIndex + 1}.
                    </span>{" "}
                    {preview}
                    {preview.length >= 36 && "\u2026"}
                  </motion.div>
                )}
              </AnimatePresence>

              {/* The dot */}
              <motion.button
                type="button"
                aria-label={`Jump to message ${dotIndex + 1}`}
                onClick={() => handleDotClick(dotIndex)}
                whileTap={{ scale: 0.85 }}
                className="relative flex cursor-pointer items-center justify-center rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                style={{ width: 20, height: 20 }}
              >
                {/* Active glow ring */}
                <AnimatePresence>
                  {isActive && (
                    <motion.span
                      initial={{ scale: 0.5, opacity: 0 }}
                      animate={{ scale: 1.6, opacity: 0.18 }}
                      exit={{ scale: 0.5, opacity: 0 }}
                      transition={{ duration: 0.4, ease: "easeOut" }}
                      className="absolute inset-0 rounded-full bg-primary"
                    />
                  )}
                </AnimatePresence>

                {/* Dot core */}
                <motion.span
                  animate={{
                    scale: isActive ? 1.15 : isHovered ? 1.05 : 1,
                  }}
                  initial={false}
                  transition={{ duration: 0.25, ease: "easeOut" }}
                  className={[
                    "relative block rounded-full border-2 shadow-sm transition-colors",
                    isActive && "size-3 border-primary bg-primary shadow-primary/25",
                    isPast && !isActive && "size-2.5 border-primary/40 bg-primary/45",
                    !isActive && !isPast && "size-2 border-muted-foreground/20 bg-muted-foreground/25",
                    isHovered && !isActive && "border-primary/40",
                  ]
                    .filter(Boolean)
                    .join(" ")}
                />

                {/* Number badge on active */}
                <AnimatePresence>
                  {isActive && (
                    <motion.span
                      initial={{ opacity: 0, scale: 0.6 }}
                      animate={{ opacity: 1, scale: 1 }}
                      exit={{ opacity: 0, scale: 0.6 }}
                      transition={{ duration: 0.2, delay: 0.05 }}
                      className="absolute -bottom-5 left-1/2 -translate-x-1/2 text-[9px] font-semibold tabular-nums text-primary/70"
                    >
                      {dotIndex + 1}
                    </motion.span>
                  )}
                </AnimatePresence>
              </motion.button>
            </div>
          );
        })}
      </div>
    </div>
  );
});
