"use client";

import React, { useMemo, useState, useEffect, useRef, useCallback } from "react";
import { motion, AnimatePresence } from "framer-motion";
import Link from "next/link";
import { useParams } from "next/navigation";
import TopStatsBar from "@/components/shared/TopStatsBar";
import useCourseStore from "@/stores/useCourseStore";
import type { RemedyNode } from "@/stores/useCourseStore";
import useUserStore from "@/stores/useUserStore";

/* ═══════════════════ Fallback mock nodes (for demo courseIds with no mock data) ═══════════════════ */

interface MapNode {
  id: string;
  title: string;
  status: "completed" | "available" | "locked";
  x: number;
  y: number;
}

/* ── Per-library-course metadata: each course has its own independent title + nodes ── */
const LIBRARY_COURSE_META: Record<string, { title: string; nodes: { id: string; title: string; status: "completed" | "available" | "locked" }[] }> = {
  ja: {
    title: "Japanese",
    nodes: [
      { id: "ja-n1", title: "Hiragana Basics", status: "completed" },
      { id: "ja-n2", title: "Katakana Basics", status: "available" },
      { id: "ja-n3", title: "Greetings & Self-Introduction", status: "locked" },
      { id: "ja-n4", title: "Numbers & Counting", status: "locked" },
      { id: "ja-n5", title: "Basic Particles (は・が・を)", status: "locked" },
      { id: "ja-n6", title: "Daily Conversation", status: "locked" },
      { id: "ja-n7", title: "Verb Conjugation", status: "locked" },
      { id: "ja-n8", title: "Adjectives & Descriptions", status: "locked" },
      { id: "ja-n9", title: "Kanji Level 1", status: "locked" },
      { id: "ja-n10", title: "Reading Comprehension", status: "locked" },
      { id: "ja-n11", title: "Listening Practice", status: "locked" },
      { id: "ja-n12", title: "Travel Japanese", status: "locked" },
      { id: "ja-n13", title: "Polite vs Casual Speech", status: "locked" },
      { id: "ja-n14", title: "Culture & Etiquette", status: "locked" },
      { id: "ja-n15", title: "Final Challenge", status: "locked" },
    ],
  },
  es: {
    title: "Spanish",
    nodes: [
      { id: "es-n1", title: "Alphabet & Pronunciation", status: "completed" },
      { id: "es-n2", title: "Greetings & Introductions", status: "available" },
      { id: "es-n3", title: "Articles & Gender", status: "locked" },
      { id: "es-n4", title: "Basic Verbs (Ser/Estar)", status: "locked" },
      { id: "es-n5", title: "Numbers & Time", status: "locked" },
      { id: "es-n6", title: "Food & Ordering", status: "locked" },
      { id: "es-n7", title: "Present Tense Conjugation", status: "locked" },
      { id: "es-n8", title: "Prepositions & Directions", status: "locked" },
      { id: "es-n9", title: "Past Tense (Pretérito)", status: "locked" },
      { id: "es-n10", title: "Shopping & Money", status: "locked" },
      { id: "es-n11", title: "Subjunctive Mood", status: "locked" },
      { id: "es-n12", title: "Travel Conversation", status: "locked" },
      { id: "es-n13", title: "Reading Practice", status: "locked" },
      { id: "es-n14", title: "Culture & Idioms", status: "locked" },
      { id: "es-n15", title: "Final Challenge", status: "locked" },
    ],
  },
  py: {
    title: "Python",
    nodes: [
      { id: "py-n1", title: "Variables & Data Types", status: "completed" },
      { id: "py-n2", title: "Control Flow (if/else)", status: "available" },
      { id: "py-n3", title: "Loops (for/while)", status: "locked" },
      { id: "py-n4", title: "Functions & Scope", status: "locked" },
      { id: "py-n5", title: "Lists & Tuples", status: "locked" },
      { id: "py-n6", title: "Dictionaries & Sets", status: "locked" },
      { id: "py-n7", title: "String Manipulation", status: "locked" },
      { id: "py-n8", title: "File I/O", status: "locked" },
      { id: "py-n9", title: "Error Handling", status: "locked" },
      { id: "py-n10", title: "Modules & Packages", status: "locked" },
      { id: "py-n11", title: "List Comprehensions", status: "locked" },
      { id: "py-n12", title: "OOP Basics", status: "locked" },
      { id: "py-n13", title: "Decorators & Generators", status: "locked" },
      { id: "py-n14", title: "Testing with pytest", status: "locked" },
      { id: "py-n15", title: "Final Project", status: "locked" },
    ],
  },
  py2: {
    title: "Python Advanced",
    nodes: [
      { id: "py2-n1", title: "Async & Concurrency", status: "completed" },
      { id: "py2-n2", title: "Metaprogramming", status: "available" },
      { id: "py2-n3", title: "Design Patterns", status: "locked" },
      { id: "py2-n4", title: "Type Hints & Mypy", status: "locked" },
      { id: "py2-n5", title: "Performance Optimization", status: "locked" },
      { id: "py2-n6", title: "Context Managers", status: "locked" },
      { id: "py2-n7", title: "Networking & APIs", status: "locked" },
      { id: "py2-n8", title: "Data Processing (Pandas)", status: "locked" },
      { id: "py2-n9", title: "Database Integration", status: "locked" },
      { id: "py2-n10", title: "Web Scraping", status: "locked" },
      { id: "py2-n11", title: "CLI Tools (Click/Typer)", status: "locked" },
      { id: "py2-n12", title: "Package Publishing", status: "locked" },
      { id: "py2-n13", title: "CI/CD Pipelines", status: "locked" },
      { id: "py2-n14", title: "Security Best Practices", status: "locked" },
      { id: "py2-n15", title: "Capstone Project", status: "locked" },
    ],
  },
  gen: {
    title: "Study Skills",
    nodes: [
      { id: "gen-n1", title: "Active Recall", status: "completed" },
      { id: "gen-n2", title: "Spaced Repetition", status: "available" },
      { id: "gen-n3", title: "Note-Taking Methods", status: "locked" },
      { id: "gen-n4", title: "Mind Mapping", status: "locked" },
      { id: "gen-n5", title: "Pomodoro Technique", status: "locked" },
      { id: "gen-n6", title: "Goal Setting (SMART)", status: "locked" },
      { id: "gen-n7", title: "Critical Thinking", status: "locked" },
      { id: "gen-n8", title: "Speed Reading", status: "locked" },
      { id: "gen-n9", title: "Memory Palace", status: "locked" },
      { id: "gen-n10", title: "Group Study Strategies", status: "locked" },
      { id: "gen-n11", title: "Test Preparation", status: "locked" },
      { id: "gen-n12", title: "Time Management", status: "locked" },
      { id: "gen-n13", title: "Self-Assessment", status: "locked" },
      { id: "gen-n14", title: "Growth Mindset", status: "locked" },
      { id: "gen-n15", title: "Final Review", status: "locked" },
    ],
  },
  ml: {
    title: "ML Basics",
    nodes: [
      { id: "ml-n1", title: "What is Machine Learning?", status: "completed" },
      { id: "ml-n2", title: "Supervised vs Unsupervised", status: "available" },
      { id: "ml-n3", title: "Linear Regression", status: "locked" },
      { id: "ml-n4", title: "Logistic Regression", status: "locked" },
      { id: "ml-n5", title: "Decision Trees", status: "locked" },
      { id: "ml-n6", title: "K-Nearest Neighbors", status: "locked" },
      { id: "ml-n7", title: "Support Vector Machines", status: "locked" },
      { id: "ml-n8", title: "Neural Network Intro", status: "locked" },
      { id: "ml-n9", title: "Training & Validation", status: "locked" },
      { id: "ml-n10", title: "Overfitting & Regularization", status: "locked" },
      { id: "ml-n11", title: "Feature Engineering", status: "locked" },
      { id: "ml-n12", title: "Model Evaluation Metrics", status: "locked" },
      { id: "ml-n13", title: "Ensemble Methods", status: "locked" },
      { id: "ml-n14", title: "Deploying Models", status: "locked" },
      { id: "ml-n15", title: "Final Challenge", status: "locked" },
    ],
  },
  ds: {
    title: "Data Science",
    nodes: [
      { id: "ds-n1", title: "Data Science Overview", status: "completed" },
      { id: "ds-n2", title: "Data Collection & Cleaning", status: "available" },
      { id: "ds-n3", title: "Exploratory Data Analysis", status: "locked" },
      { id: "ds-n4", title: "Statistical Foundations", status: "locked" },
      { id: "ds-n5", title: "Data Visualization", status: "locked" },
      { id: "ds-n6", title: "Hypothesis Testing", status: "locked" },
      { id: "ds-n7", title: "Regression Analysis", status: "locked" },
      { id: "ds-n8", title: "Clustering Techniques", status: "locked" },
      { id: "ds-n9", title: "Time Series Analysis", status: "locked" },
      { id: "ds-n10", title: "Natural Language Processing", status: "locked" },
      { id: "ds-n11", title: "Big Data Tools", status: "locked" },
      { id: "ds-n12", title: "Dashboard & Reporting", status: "locked" },
      { id: "ds-n13", title: "A/B Testing", status: "locked" },
      { id: "ds-n14", title: "Ethics in Data Science", status: "locked" },
      { id: "ds-n15", title: "Capstone Project", status: "locked" },
    ],
  },
};

const FALLBACK_NODES: { id: string; title: string; status: "completed" | "available" | "locked" }[] = [
  { id: "demo-n1", title: "Introduction", status: "completed" },
  { id: "demo-n2", title: "Core Concepts", status: "available" },
  { id: "demo-n3", title: "Deep Dive", status: "locked" },
  { id: "demo-n4", title: "Practice Lab", status: "locked" },
  { id: "demo-n5", title: "Advanced Topics", status: "locked" },
  { id: "demo-n6", title: "Connections", status: "locked" },
  { id: "demo-n7", title: "Analysis", status: "locked" },
  { id: "demo-n8", title: "Synthesis", status: "locked" },
  { id: "demo-n9", title: "Application", status: "locked" },
  { id: "demo-n10", title: "Case Study", status: "locked" },
  { id: "demo-n11", title: "Research", status: "locked" },
  { id: "demo-n12", title: "Review", status: "locked" },
  { id: "demo-n13", title: "Mastery", status: "locked" },
  { id: "demo-n14", title: "Integration", status: "locked" },
  { id: "demo-n15", title: "Final Challenge", status: "locked" },
];

/* ═══════════════════ Page ═══════════════════ */

export default function MapClient() {
  const params = useParams();
  const courseId = params.courseId as string;

  const loadMockCourses = useCourseStore((s) => s.loadMockCourses);
  const getCourse = useCourseStore((s) => s.getCourse);
  const getNodesForCourse = useCourseStore((s) => s.getNodesForCourse);
  const getRemedyNodesForCourse = useCourseStore((s) => s.getRemedyNodesForCourse);
  const setLastActiveCourse = useUserStore((s) => s.setLastActiveCourse);

  const [loaded, setLoaded] = useState(false);

  useEffect(() => {
    loadMockCourses();
    setLoaded(true);
  }, [loadMockCourses]);

  /* Lock body scroll while map page is mounted (zoom 1.05 causes overflow) */
  useEffect(() => {
    const prev = document.documentElement.style.overflow;
    document.documentElement.style.overflow = "hidden";
    document.body.style.overflow = "hidden";
    return () => {
      document.documentElement.style.overflow = prev;
      document.body.style.overflow = "";
    };
  }, []);

  useEffect(() => {
    if (courseId) setLastActiveCourse(courseId);
  }, [courseId, setLastActiveCourse]);

  const course = getCourse(courseId);
  const storeNodes = getNodesForCourse(courseId);

  // Build MapNode array – use store data if available, otherwise fallback
  // Dynamic positions: zigzag pattern, each node spaced vertically
  const NODES: MapNode[] = useMemo(() => {
    const X_PATTERN = [50, 28, 68, 32, 58, 40, 65, 30, 55, 42, 62, 35, 58, 45, 50];
    const buildPositions = (nodes: { id: string; title: string; status: "completed" | "available" | "locked" }[]) => {
      const count = nodes.length;
      const spacing = 120; // px per node
      return nodes.map((n, i) => ({
        id: n.id,
        title: n.title,
        status: n.status,
        x: X_PATTERN[i % X_PATTERN.length],
        y: (count - 1 - i) * spacing + 60, // bottom-to-top, first node at bottom
      }));
    };

    if (storeNodes.length > 0) {
      return buildPositions(storeNodes);
    }
    if (!loaded) return [];
    // Use per-course fallback nodes if available
    const libMeta = LIBRARY_COURSE_META[courseId];
    if (libMeta) return buildPositions(libMeta.nodes);
    return buildPositions(FALLBACK_NODES);
  }, [storeNodes, loaded, courseId]);

  // Total height for scrollable map
  const mapHeight = Math.max(560, NODES.length * 120 + 120);

  /* ── Scroll to bottom on load ── */
  const mapContainerRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
    if (NODES.length > 0 && mapContainerRef.current) {
      mapContainerRef.current.scrollTop = mapContainerRef.current.scrollHeight;
    }
  }, [NODES.length]);

  /* ── Mouse drag to pan ── */
  const isDragging = useRef(false);
  const dragStart = useRef({ x: 0, y: 0, scrollLeft: 0, scrollTop: 0 });

  const handleMouseDown = useCallback((e: React.MouseEvent) => {
    const container = mapContainerRef.current;
    if (!container) return;
    isDragging.current = true;
    dragStart.current = {
      x: e.clientX,
      y: e.clientY,
      scrollLeft: container.scrollLeft,
      scrollTop: container.scrollTop,
    };
    container.style.cursor = "grabbing";
    container.style.userSelect = "none";
  }, []);

  const handleMouseMove = useCallback((e: React.MouseEvent) => {
    if (!isDragging.current) return;
    const container = mapContainerRef.current;
    if (!container) return;
    const dx = e.clientX - dragStart.current.x;
    const dy = e.clientY - dragStart.current.y;
    container.scrollTop = dragStart.current.scrollTop - dy;
    container.scrollLeft = dragStart.current.scrollLeft - dx;
  }, []);

  const handleMouseUp = useCallback(() => {
    isDragging.current = false;
    const container = mapContainerRef.current;
    if (container) {
      container.style.cursor = "grab";
      container.style.userSelect = "";
    }
  }, []);

  const pageTitle = course?.unitTitle ?? LIBRARY_COURSE_META[courseId]?.title ?? "Course Map";
  const remedyNodes = getRemedyNodesForCourse(courseId);
  return (
    <div className="relative overflow-hidden bg-gradient-to-br from-[#edf7fb] via-[#c9e6f2] to-[#a3d5e8]" style={{ zoom: 1.05, height: "calc(100vh / 1.05)" }}>
      <TopStatsBar backHref="/home" pageTitle={pageTitle} />

      {/* Background effects */}
      <MapBg />
      <FloatingParticles />

      <div className="relative z-10 flex gap-6 px-6 max-w-7xl mx-auto" style={{ height: "calc(100vh / 1.05 - 56px)" }}>
        {/* ── Center: Map canvas (drag to pan, hidden scrollbar) ── */}
        <div
          ref={mapContainerRef}
          className="flex-1 min-w-0 overflow-y-auto rounded-2xl scrollbar-hide"
          style={{ cursor: "grab" }}
          onMouseDown={handleMouseDown}
          onMouseMove={handleMouseMove}
          onMouseUp={handleMouseUp}
          onMouseLeave={handleMouseUp}
        >
          <div className="relative" style={{ height: mapHeight, minHeight: 560 }}>
            {/* SVG path connecting nodes */}
            <svg
              className="absolute inset-0 w-full h-full pointer-events-none z-0"
              viewBox={`0 0 100 ${mapHeight}`}
              preserveAspectRatio="none"
              fill="none"
            >
              <defs>
                <linearGradient id="pathGrad" x1="0" y1="1" x2="0" y2="0">
                  <stop offset="0%" stopColor="#4a9e9b" stopOpacity="0.8" />
                  <stop offset="100%" stopColor="#8a7a60" stopOpacity="0.5" />
                </linearGradient>
              </defs>
              {NODES.slice(0, -1).map((node, i) => {
                const next = NODES[i + 1];
                const nx = node.x;
                const ny = node.y;
                const nnx = next.x;
                const nny = next.y;
                const mx = (nx + nnx) / 2;
                const my = (ny + nny) / 2;
                const isActive =
                  node.status !== "locked" || next.status !== "locked";
                return (
                  <path
                    key={i}
                    d={`M ${nx} ${ny} Q ${mx + (i % 2 === 0 ? 12 : -12)} ${my} ${nnx} ${nny}`}
                    stroke={isActive ? "url(#pathGrad)" : "#999"}
                    strokeWidth="1.8"
                    strokeDasharray="4 3"
                    opacity={isActive ? 0.85 : 0.45}
                    vectorEffect="non-scaling-stroke"
                  />
                );
              })}
            </svg>

            {/* Nodes */}
            {NODES.map((node, i) => {
              const remedy = remedyNodes.find((r) => r.sourceNodeId === node.id);
              return (
                <MapNodeCircle key={node.id} node={node} index={i} remedyNode={remedy} />
              );
            })}
          </div>
        </div>

        {/* ── Right: AI Chat Assistant ── */}
        <div className="w-[340px] flex-shrink-0 pt-8">
          <AIChatAssistant />
        </div>
      </div>
    </div>
  );
}

/* ═══════════════════ AI Chat Assistant ═══════════════════ */

interface ChatMessage {
  id: number;
  role: "user" | "assistant";
  text: string;
}

function AIChatAssistant() {
  const [input, setInput] = useState("");
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const chatEndRef = React.useRef<HTMLDivElement>(null);

  useEffect(() => {
    chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages]);

  const handleSend = () => {
    const trimmed = input.trim();
    if (!trimmed) return;

    const userMsg: ChatMessage = { id: Date.now(), role: "user", text: trimmed };
    const botMsg: ChatMessage = { id: Date.now() + 1, role: "assistant", text: trimmed };

    setMessages((prev) => [...prev, userMsg, botMsg]);
    setInput("");
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === "Enter" && !e.shiftKey) {
      e.preventDefault();
      handleSend();
    }
  };

  return (
    <motion.div
      initial={{ opacity: 0, x: 40 }}
      animate={{ opacity: 1, x: 0 }}
      transition={{ delay: 0.3, type: "spring", damping: 18 }}
      className="rounded-3xl bg-white/60 backdrop-blur-xl border border-white/50 shadow-lg shadow-teal-200/20 overflow-hidden flex flex-col"
      style={{ height: "78vh", minHeight: 560 }}
    >
      {/* Header */}
      <div className="px-6 pt-6 pb-4 flex items-center gap-3">
        {/* Owl avatar */}
        <div className="relative w-14 h-14 flex-shrink-0">
          <div className="w-14 h-14 rounded-full bg-gradient-to-br from-amber-300 to-amber-500 flex items-center justify-center shadow-md">
            <svg viewBox="0 0 40 40" className="w-9 h-9" fill="none">
              <ellipse cx="20" cy="24" rx="12" ry="10" fill="#C47F17" />
              <circle cx="15" cy="20" r="5" fill="white" />
              <circle cx="25" cy="20" r="5" fill="white" />
              <circle cx="15" cy="20" r="2.5" fill="#2D2D2D" />
              <circle cx="25" cy="20" r="2.5" fill="#2D2D2D" />
              <circle cx="16" cy="19" r="1" fill="white" />
              <circle cx="26" cy="19" r="1" fill="white" />
              <polygon points="20,22 18,25 22,25" fill="#FF9500" />
              <polygon points="10,16 8,8 15,14" fill="#C47F17" />
              <polygon points="30,16 32,8 25,14" fill="#C47F17" />
            </svg>
          </div>
        </div>
        <div>
          <h3 className="font-heading font-bold text-[15px] text-brand-gray-700">
            AI Chat Assistant
          </h3>
          <p className="text-xs text-brand-gray-400 mt-0.5">
            Ask me anything about this course!
          </p>
        </div>
      </div>

      {/* Chat messages area */}
      <div className="flex-1 overflow-y-auto px-5 pb-3 space-y-3">
        {messages.length === 0 && (
          <div className="flex flex-col items-center justify-center py-8 gap-3 text-center">
            <div className="w-12 h-12 rounded-full bg-brand-teal/10 flex items-center justify-center">
              <svg viewBox="0 0 24 24" className="w-6 h-6 text-brand-teal" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" />
              </svg>
            </div>
            <p className="text-xs text-brand-gray-400 leading-relaxed max-w-[200px]">
              Type a message to start chatting with your AI study companion!
            </p>
          </div>
        )}

        {messages.map((msg) => (
          <motion.div
            key={msg.id}
            initial={{ opacity: 0, y: 8 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.2 }}
            className={`flex ${msg.role === "user" ? "justify-end" : "justify-start"}`}
          >
            <div
              className={`max-w-[85%] rounded-2xl px-4 py-2.5 text-[13px] leading-relaxed ${
                msg.role === "user"
                  ? "bg-gradient-to-r from-brand-teal to-[#5fb3af] text-white rounded-br-md"
                  : "bg-white/70 border border-white/60 text-brand-gray-600 rounded-bl-md shadow-sm"
              }`}
            >
              {msg.text}
            </div>
          </motion.div>
        ))}
        <div ref={chatEndRef} />
      </div>

      {/* Divider */}
      <div className="mx-5 h-px bg-gradient-to-r from-transparent via-brand-teal/20 to-transparent" />

      {/* Input area */}
      <div className="px-5 py-4 flex items-end gap-2">
        <textarea
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={handleKeyDown}
          placeholder="Type your message..."
          rows={1}
          className="flex-1 resize-none rounded-xl bg-white/70 border border-white/60 px-4 py-3 text-[13px] text-brand-gray-700 placeholder-brand-gray-300 focus:outline-none focus:ring-2 focus:ring-brand-teal/30 focus:border-brand-teal/40 transition-all"
          style={{ maxHeight: 80 }}
        />
        <motion.button
          onClick={handleSend}
          whileTap={{ scale: 0.9 }}
          className="flex-shrink-0 w-11 h-11 rounded-xl bg-gradient-to-r from-brand-teal to-[#5fb3af] text-white flex items-center justify-center shadow-md shadow-teal-300/30 hover:shadow-lg hover:shadow-teal-300/40 transition-all"
        >
          <svg viewBox="0 0 24 24" className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <line x1="22" y1="2" x2="11" y2="13" />
            <polygon points="22 2 15 22 11 13 2 9 22 2" />
          </svg>
        </motion.button>
      </div>
    </motion.div>
  );
}

/* ═══════════════════ Node Circle ═══════════════════ */

function MapNodeCircle({
  node,
  index,
  remedyNode,
}: {
  node: MapNode;
  index: number;
  remedyNode?: RemedyNode;
}) {
  const isClickable =
    node.status === "available" || node.status === "completed";

  const content = (
    <motion.div
      initial={{ opacity: 0, y: 20, scale: 0.8 }}
      animate={{ opacity: 1, y: 0, scale: 1 }}
      transition={{ delay: 0.15 * index, type: "spring", damping: 14 }}
      className="flex flex-col items-center gap-2 cursor-pointer"
      whileHover={isClickable ? { scale: 1.1 } : {}}
      whileTap={isClickable ? { scale: 0.92 } : {}}
    >
      {/* Outer glow ring for available */}
      {node.status === "available" && (
        <>
          <motion.div
            className="absolute w-[88px] h-[88px] rounded-full"
            style={{
              background:
                "radial-gradient(circle, rgba(122,199,196,0.25) 0%, transparent 70%)",
            }}
            animate={{ scale: [1, 1.4, 1], opacity: [0.4, 0.7, 0.4] }}
            transition={{ repeat: Infinity, duration: 2.5, ease: "easeInOut" }}
          />
          {/* Petal / scallop border */}
          <motion.div
            className="absolute w-[76px] h-[76px]"
            animate={{ rotate: [0, 360] }}
            transition={{ repeat: Infinity, duration: 20, ease: "linear" }}
          >
            <svg viewBox="0 0 76 76" className="w-full h-full" fill="none">
              {Array.from({ length: 12 }).map((_, i) => {
                const angle = (i * 30 * Math.PI) / 180;
                const cx = 38 + 32 * Math.cos(angle);
                const cy = 38 + 32 * Math.sin(angle);
                return (
                  <circle
                    key={i}
                    cx={cx}
                    cy={cy}
                    r="8"
                    fill="none"
                    stroke="#7AC7C4"
                    strokeWidth="1"
                    opacity="0.3"
                  />
                );
              })}
            </svg>
          </motion.div>
        </>
      )}

      {/* Completed outer ring decoration */}
      {node.status === "completed" && (
        <motion.div
          className="absolute w-[78px] h-[78px]"
          animate={{ rotate: [0, -360] }}
          transition={{ repeat: Infinity, duration: 25, ease: "linear" }}
        >
          <svg viewBox="0 0 78 78" className="w-full h-full" fill="none">
            {Array.from({ length: 10 }).map((_, i) => {
              const angle = (i * 36 * Math.PI) / 180;
              const cx = 39 + 33 * Math.cos(angle);
              const cy = 39 + 33 * Math.sin(angle);
              return (
                <circle
                  key={i}
                  cx={cx}
                  cy={cy}
                  r="7"
                  fill="none"
                  stroke="#F5C842"
                  strokeWidth="1"
                  opacity="0.35"
                />
              );
            })}
          </svg>
        </motion.div>
      )}

      {/* Main circle */}
      <div
        className={`relative z-10 h-[64px] w-[64px] rounded-full flex items-center justify-center transition-all ${
          node.status === "completed"
            ? "shadow-lg shadow-amber-300/30"
            : node.status === "available"
            ? "shadow-lg shadow-teal-400/30"
            : "shadow-md"
        }`}
      >
        {/* Background with double border effect */}
        <div
          className={`absolute inset-0 rounded-full ${
            node.status === "completed"
              ? "bg-gradient-to-br from-yellow-300 via-amber-400 to-yellow-500"
              : node.status === "available"
              ? "bg-gradient-to-br from-[#7AC7C4] via-[#5fb3af] to-[#4da8a4]"
              : "bg-gradient-to-br from-[#e0ddd8] via-[#d4d0ca] to-[#c8c4be]"
          }`}
        />
        {/* Inner ring */}
        <div
          className={`absolute inset-[3px] rounded-full border-2 ${
            node.status === "completed"
              ? "border-yellow-200/50"
              : node.status === "available"
              ? "border-white/30"
              : "border-white/20"
          }`}
        />
        {/* Specular highlight */}
        <div className="absolute inset-0 rounded-full overflow-hidden">
          <div
            className="absolute -top-1 left-1/2 -translate-x-1/2 w-[70%] h-[40%] rounded-[50%]"
            style={{
              background:
                node.status === "locked"
                  ? "linear-gradient(180deg, rgba(255,255,255,0.25) 0%, transparent 100%)"
                  : "linear-gradient(180deg, rgba(255,255,255,0.35) 0%, transparent 100%)",
            }}
          />
        </div>

        {/* Icon */}
        <div className="relative z-10">
          {node.status === "completed" ? (
            <svg
              viewBox="0 0 24 24"
              className="h-7 w-7 text-white drop-shadow-sm"
              fill="none"
              stroke="currentColor"
              strokeWidth="3"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <path d="M20 6L9 17l-5-5" />
            </svg>
          ) : node.status === "available" ? (
            <motion.div
              animate={{ scale: [1, 1.15, 1] }}
              transition={{
                repeat: Infinity,
                duration: 2,
                ease: "easeInOut",
              }}
            >
              <svg
                viewBox="0 0 24 24"
                className="h-7 w-7 text-white drop-shadow-sm"
                fill="currentColor"
              >
                <polygon points="12,2 15,9 22,9 16,14 18,22 12,17 6,22 8,14 2,9 9,9" />
              </svg>
            </motion.div>
          ) : (
            <svg
              viewBox="0 0 24 24"
              className="h-6 w-6 text-[#a09a92]"
              fill="none"
              stroke="currentColor"
              strokeWidth="2"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <rect x="3" y="11" width="18" height="11" rx="2" />
              <path d="M7 11V7a5 5 0 0110 0v4" />
            </svg>
          )}
        </div>
      </div>

      {/* Label */}
      <span
        className={`text-[11px] font-heading font-bold text-center max-w-[100px] leading-tight drop-shadow-sm ${
          node.status === "completed"
            ? "text-amber-700"
            : node.status === "available"
            ? "text-teal-700"
            : "text-brand-gray-400"
        }`}
      >
        {node.title}
      </span>
    </motion.div>
  );

  return (
    <div
      className="absolute z-10"
      style={{
        left: `${node.x}%`,
        top: `${node.y}px`,
        transform: "translate(-50%, -50%)",
      }}
    >
      {/* ── Remedy circle node on the LEFT ── */}
      <AnimatePresence>
        {remedyNode && (
          <motion.div
            initial={{ opacity: 0, scale: 0.5, x: 30 }}
            animate={{ opacity: 1, scale: 1, x: 0 }}
            exit={{ opacity: 0, scale: 0.5, x: 30 }}
            transition={{ type: "spring", damping: 16, stiffness: 180 }}
            className="absolute z-20"
            style={{ right: "calc(100% + 60px)", top: "50%", transform: "translateY(-50%)" }}
          >
            {/* Dashed curve connecting to main node */}
            <svg
              className="absolute pointer-events-none"
              style={{ left: "calc(100% + 4px)", top: "50%", transform: "translateY(-50%)" }}
              width="56" height="20" viewBox="0 0 56 20" fill="none"
            >
              <path
                d="M0 10 Q28 0 56 10"
                stroke="#f59e0b"
                strokeWidth="2"
                strokeDasharray="4 3"
                opacity="0.7"
                fill="none"
              />
            </svg>

            {remedyNode.status === "available" ? (
              <Link href={`/play/${remedyNode.sourceNodeId}`}>
                <motion.div
                  className="flex flex-col items-center gap-2 cursor-pointer"
                  whileHover={{ scale: 1.12 }}
                  whileTap={{ scale: 0.92 }}
                >
                  {/* Outer glow */}
                  <motion.div
                    className="absolute w-[80px] h-[80px] rounded-full"
                    style={{ background: "radial-gradient(circle, rgba(245,158,11,0.25) 0%, transparent 70%)" }}
                    animate={{ scale: [1, 1.35, 1], opacity: [0.4, 0.7, 0.4] }}
                    transition={{ repeat: Infinity, duration: 2.5, ease: "easeInOut" }}
                  />
                  {/* Petal ring */}
                  <motion.div
                    className="absolute w-[70px] h-[70px]"
                    animate={{ rotate: [0, 360] }}
                    transition={{ repeat: Infinity, duration: 18, ease: "linear" }}
                  >
                    <svg viewBox="0 0 70 70" className="w-full h-full" fill="none">
                      {Array.from({ length: 10 }).map((_, i) => {
                        const angle = (i * 36 * Math.PI) / 180;
                        const cx = 35 + 29 * Math.cos(angle);
                        const cy = 35 + 29 * Math.sin(angle);
                        return <circle key={i} cx={cx} cy={cy} r="7" fill="none" stroke="#f59e0b" strokeWidth="1" opacity="0.3" />;
                      })}
                    </svg>
                  </motion.div>
                  {/* Main circle */}
                  <div className="relative z-10 h-[58px] w-[58px] rounded-full flex items-center justify-center shadow-lg shadow-amber-400/30">
                    <div className="absolute inset-0 rounded-full bg-gradient-to-br from-amber-300 via-orange-400 to-amber-500" />
                    <div className="absolute inset-[3px] rounded-full border-2 border-amber-200/50" />
                    <div className="absolute inset-0 rounded-full overflow-hidden">
                      <div className="absolute -top-1 left-1/2 -translate-x-1/2 w-[70%] h-[40%] rounded-[50%]" style={{ background: "linear-gradient(180deg, rgba(255,255,255,0.35) 0%, transparent 100%)" }} />
                    </div>
                    <div className="relative z-10">
                      <motion.div animate={{ scale: [1, 1.12, 1] }} transition={{ repeat: Infinity, duration: 2, ease: "easeInOut" }}>
                        <svg viewBox="0 0 24 24" className="h-6 w-6 text-white drop-shadow-sm" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
                          <path d="M2 3h6a4 4 0 014 4v14a3 3 0 00-3-3H2z" />
                          <path d="M22 3h-6a4 4 0 00-4 4v14a3 3 0 013-3h7z" />
                        </svg>
                      </motion.div>
                    </div>
                  </div>
                  <span className="text-[10px] font-heading font-bold text-amber-700 text-center max-w-[90px] leading-tight drop-shadow-sm whitespace-nowrap">補強練習</span>
                </motion.div>
              </Link>
            ) : (
              <div className="flex flex-col items-center gap-2">
                {/* Completed ring – amber style */}
                <motion.div
                  className="absolute w-[70px] h-[70px]"
                  animate={{ rotate: [0, -360] }}
                  transition={{ repeat: Infinity, duration: 25, ease: "linear" }}
                >
                  <svg viewBox="0 0 70 70" className="w-full h-full" fill="none">
                    {Array.from({ length: 10 }).map((_, i) => {
                      const angle = (i * 36 * Math.PI) / 180;
                      const cx = 35 + 29 * Math.cos(angle);
                      const cy = 35 + 29 * Math.sin(angle);
                      return <circle key={i} cx={cx} cy={cy} r="7" fill="none" stroke="#f59e0b" strokeWidth="1" opacity="0.35" />;
                    })}
                  </svg>
                </motion.div>
                {/* Main circle – completed amber */}
                <div className="relative z-10 h-[58px] w-[58px] rounded-full flex items-center justify-center shadow-lg shadow-amber-400/30">
                  <div className="absolute inset-0 rounded-full bg-gradient-to-br from-amber-300 via-orange-400 to-amber-500" />
                  <div className="absolute inset-[3px] rounded-full border-2 border-amber-200/50" />
                  <div className="absolute inset-0 rounded-full overflow-hidden">
                    <div className="absolute -top-1 left-1/2 -translate-x-1/2 w-[70%] h-[40%] rounded-[50%]" style={{ background: "linear-gradient(180deg, rgba(255,255,255,0.35) 0%, transparent 100%)" }} />
                  </div>
                  <div className="relative z-10">
                    <svg viewBox="0 0 24 24" className="h-7 w-7 text-white drop-shadow-sm" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
                      <path d="M20 6L9 17l-5-5" />
                    </svg>
                  </div>
                </div>
                <span className="text-[10px] font-heading font-bold text-amber-700 text-center max-w-[90px] leading-tight drop-shadow-sm whitespace-nowrap">補強完成</span>
              </div>
            )}
          </motion.div>
        )}
      </AnimatePresence>

      {isClickable ? (
        <Link href={`/play/${node.id}`}>{content}</Link>
      ) : (
        content
      )}
    </div>
  );
}

/* ═══════════════════ Floating Particles ═══════════════════ */

function FloatingParticles() {
  const particles = useMemo(
    () =>
      Array.from({ length: 24 }, (_, i) => ({
        id: i,
        x: 5 + Math.random() * 90,
        size: 3 + Math.random() * 5,
        delay: Math.random() * 8,
        duration: 10 + Math.random() * 12,
        opacity: 0.08 + Math.random() * 0.15,
        type: i % 4, // 0=dot, 1=ring, 2=diamond, 3=star
      })),
    []
  );

  return (
    <div
      className="pointer-events-none absolute inset-0 z-[1] overflow-hidden"
      aria-hidden="true"
    >
      {particles.map((p) => (
        <motion.div
          key={p.id}
          className="absolute"
          style={{ left: `${p.x}%`, bottom: -20 }}
          animate={{
            y: [0, -(typeof window !== "undefined" ? window.innerHeight + 40 : 900)],
            x: [0, (p.id % 2 === 0 ? 1 : -1) * (15 + Math.random() * 25)],
            rotate: [0, 180 + Math.random() * 180],
          }}
          transition={{
            duration: p.duration,
            delay: p.delay,
            repeat: Infinity,
            ease: "linear",
          }}
        >
          {p.type === 0 && (
            <div
              className="rounded-full bg-brand-teal"
              style={{
                width: p.size,
                height: p.size,
                opacity: p.opacity,
              }}
            />
          )}
          {p.type === 1 && (
            <div
              className="rounded-full border border-brand-teal"
              style={{
                width: p.size * 1.5,
                height: p.size * 1.5,
                opacity: p.opacity,
              }}
            />
          )}
          {p.type === 2 && (
            <div
              className="bg-amber-400"
              style={{
                width: p.size,
                height: p.size,
                opacity: p.opacity,
                transform: "rotate(45deg)",
                borderRadius: 1,
              }}
            />
          )}
          {p.type === 3 && (
            <svg
              viewBox="0 0 24 24"
              fill="#7AC7C4"
              style={{
                width: p.size * 1.8,
                height: p.size * 1.8,
                opacity: p.opacity,
              }}
            >
              <polygon points="12,2 15,9 22,9 16,14 18,22 12,17 6,22 8,14 2,9 9,9" />
            </svg>
          )}
        </motion.div>
      ))}

      {/* Sparkle shimmer dots */}
      {Array.from({ length: 14 }, (_, i) => (
        <motion.div
          key={`sparkle-${i}`}
          className="absolute rounded-full bg-white"
          style={{
            width: 2 + Math.random() * 3,
            height: 2 + Math.random() * 3,
            left: `${8 + Math.random() * 84}%`,
            top: `${10 + Math.random() * 80}%`,
          }}
          animate={{
            opacity: [0, 0.4, 0],
            scale: [0.5, 1.2, 0.5],
          }}
          transition={{
            duration: 2 + Math.random() * 3,
            delay: Math.random() * 5,
            repeat: Infinity,
            ease: "easeInOut",
          }}
        />
      ))}
    </div>
  );
}

/* ═══════════════════ Background ═══════════════════ */

function MapBg() {
  return (
    <div
      className="pointer-events-none absolute inset-0 z-0 overflow-hidden"
      aria-hidden="true"
    >
      {/* Runic circle — bottom-right */}
      <svg
        viewBox="0 0 700 700"
        className="absolute -right-[160px] -bottom-[160px] w-[520px] h-[520px] opacity-[0.08]"
        fill="none"
      >
        <defs>
          <radialGradient id="mapPeach" cx="50%" cy="45%" r="50%">
            <stop offset="0%" stopColor="#D4EEE8" />
            <stop offset="60%" stopColor="#B5DDD4" />
            <stop offset="100%" stopColor="#9ACEC3" />
          </radialGradient>
          <path
            id="mapRune"
            d="M 350,350 m -260,0 a 260,260 0 1,1 520,0 a 260,260 0 1,1 -520,0"
          />
        </defs>
        <circle cx="350" cy="350" r="300" fill="url(#mapPeach)" opacity="0.5" />
        <circle
          cx="350"
          cy="350"
          r="290"
          fill="none"
          stroke="#7AC7C4"
          strokeWidth="1"
          opacity="0.3"
        />
        <circle
          cx="350"
          cy="350"
          r="240"
          fill="none"
          stroke="#7AC7C4"
          strokeWidth="0.5"
          opacity="0.2"
          strokeDasharray="8 6"
        />
        <text
          fill="#7AC7C4"
          fontSize="18"
          fontWeight="500"
          letterSpacing="4"
          opacity="0.3"
        >
          <textPath href="#mapRune">
            ᚠᚢᚦᚨᚱᚲᚷᚹᚺᚾᛁᛃᛇᛈᛉᛊᛏᛒᛖᛗᛚᛜᛝᛞᛟᚠᚢᚦᚨᚱᚲᚷᚹᚺᚾᛁᛃᛇᛈᛉᛊᛏᛒᛖᛗᛚᛜᛝᛞᛟ
          </textPath>
        </text>
      </svg>

      {/* Subtle top-left gradient orb */}
      <div
        className="absolute -left-[100px] -top-[100px] w-[400px] h-[400px] rounded-full opacity-[0.06]"
        style={{
          background:
            "radial-gradient(circle, rgba(122,199,196,0.6) 0%, transparent 70%)",
        }}
      />

      {/* Mid-right soft glow */}
      <div
        className="absolute right-[5%] top-[35%] w-[200px] h-[200px] rounded-full opacity-[0.05]"
        style={{
          background:
            "radial-gradient(circle, rgba(245,200,66,0.5) 0%, transparent 70%)",
        }}
      />
    </div>
  );
}
