"use client";

import { useState, useEffect, useCallback, useRef } from "react";
import { useParams, useRouter } from "next/navigation";
import { motion, AnimatePresence, useAnimation } from "framer-motion";
import {
  ArrowLeft,
  Send,
  Loader2,
  Package,
  Flame,
  Users,
  Share2,
  LogIn,
  Copy,
  Trash2,
  LogOut,
  MapPin,
  Calendar,
  Sparkles,
  ClipboardCheck,
  ChevronRight,
  Lightbulb,
  Shuffle,
} from "lucide-react";
import confetti from "canvas-confetti";
import { getCachedProfile, isRegistered } from "@/lib/userId";
import ShareCardModal from "@/components/ShareCardModal";
import Button from "@/components/Button";
import BlindboxMyIdeas, { type MyIdea } from "@/components/BlindboxMyIdeas";
import BlindboxDrawnHistory, { type DrawnIdea } from "@/components/BlindboxDrawnHistory";
import WeekendTimeSelector from "@/components/WeekendTimeSelector";
import BlindboxPlan from "@/components/BlindboxPlan";
import ContractCompletionModal, { type PendingContract } from "@/components/ContractCompletionModal";
import { useToast } from "@/hooks/useToast";
import { useShare } from "@/hooks/useShare";
import { BlindboxRoomSkeleton } from "@/components/Skeleton";
import type { UserProfile, WeekendPlanData } from "@/types";

interface RoomInfo {
  id: string;
  code: string;
  name: string;
  creatorId: string;
  city: string | null;
  lat: number | null;
  lng: number | null;
  poolCount: number;
  members: { id: string; username: string; avatar: string }[];
}

type Phase = "pool" | "shaking" | "reveal" | "time_select" | "planning" | "plan_reveal";

const PLAN_STATUS_STEPS = [
  "正在分析你们的想法...",
  "正在搜索地点...",
  "正在规划路线...",
  "快好了...",
];

const IDEA_INSPIRATIONS = [
  "去城市最高楼看日落",
  "挑战一人做一道菜",
  "找一家从没去过的店",
  "逛一个从没去过的街区",
  "去公园野餐",
  "看一场午夜电影",
  "在家做一顿异国料理",
  "骑车去郊外探险",
  "去二手市场淘宝",
  "去博物馆逛半天",
  "试一项没玩过的运动",
  "随机坐公交到终点站",
  "一起画画或做手工",
  "找个天台看星星",
  "去一家评分最低的餐厅",
  "穿最好看的衣服去拍照",
  "交换手机玩一个小时",
  "一起去做志愿者",
];

function pickRandom<T>(arr: T[], n: number): T[] {
  const shuffled = [...arr].sort(() => Math.random() - 0.5);
  return shuffled.slice(0, n);
}

export default function BlindboxRoomPage() {
  const { code } = useParams<{ code: string }>();
  const router = useRouter();

  const [profile, setProfile] = useState<UserProfile | null>(null);
  const [room, setRoom] = useState<RoomInfo | null>(null);
  const [isMember, setIsMember] = useState(false);
  const [joiningRoom, setJoiningRoom] = useState(false);
  const [pageLoading, setPageLoading] = useState(true);

  const [input, setInput] = useState("");
  const [submitting, setSubmitting] = useState(false);
  const [suggestions, setSuggestions] = useState(() => pickRandom(IDEA_INSPIRATIONS, 4));
  const [suggestionsLoading, setSuggestionsLoading] = useState(false);
  const [suggestionsSource, setSuggestionsSource] = useState<"static" | "ai">("static");
  const [poolCount, setPoolCount] = useState(0);
  const [myIdeas, setMyIdeas] = useState<MyIdea[]>([]);
  const [drawnHistory, setDrawnHistory] = useState<DrawnIdea[]>([]);
  const [phase, setPhase] = useState<Phase>("pool");
  const [revealedIdea, setRevealedIdea] = useState<DrawnIdea | null>(null);
  const [submitFlash, setSubmitFlash] = useState(false);
  const [error, setError] = useState("");
  const [showInvite, setShowInvite] = useState(false);
  const [showShareCard, setShowShareCard] = useState(false);
  const toast = useToast();
  const [confirmLeave, setConfirmLeave] = useState(false);
  const [leaving, setLeaving] = useState(false);
  const [locating, setLocating] = useState(false);
  const [planId, setPlanId] = useState<string | null>(null);
  const [planDays, setPlanDays] = useState<WeekendPlanData[]>([]);
  const [planAccepted, setPlanAccepted] = useState(false);
  const [generating, setGenerating] = useState(false);
  const [planStatusMessages, setPlanStatusMessages] = useState<string[]>([]);
  const planLogRef = useRef<HTMLDivElement>(null);
  const [showPlanShareCard, setShowPlanShareCard] = useState(false);
  const [activeContract, setActiveContract] = useState<{
    id: string;
    days: WeekendPlanData[];
    endTime: string | null;
  } | null>(null);
  const [pendingContracts, setPendingContracts] = useState<PendingContract[]>([]);

  const boxControls = useAnimation();
  const inputRef = useRef<HTMLInputElement>(null);
  const timersRef = useRef<ReturnType<typeof setTimeout>[]>([]);
  const confettiAliveRef = useRef(false);

  useEffect(() => {
    return () => {
      timersRef.current.forEach(clearTimeout);
      confettiAliveRef.current = false;
    };
  }, []);

  useEffect(() => {
    if (!isRegistered()) {
      router.replace("/blindbox");
      return;
    }
    setProfile(getCachedProfile());
  }, [router]);

  const fetchRoom = useCallback(async () => {
    if (!code) return;
    try {
      const res = await fetch(`/api/blindbox/room/${code}`);
      if (!res.ok) {
        router.replace("/blindbox");
        return;
      }
      const data: RoomInfo = await res.json();
      setRoom(data);

      const p = getCachedProfile();
      const memberCheck = data.members.some((m) => m.id === p?.id);
      setIsMember(memberCheck);
      setPoolCount(data.poolCount);
    } catch {
      router.replace("/blindbox");
    } finally {
      setPageLoading(false);
    }
  }, [code, router]);

  useEffect(() => {
    fetchRoom();
  }, [fetchRoom]);

  const fetchIdeas = useCallback(async () => {
    const p = getCachedProfile();
    if (!room || !p) return;
    try {
      const res = await fetch(`/api/blindbox?roomId=${room.id}&userId=${p.id}`);
      if (res.ok) {
        const data = await res.json();
        setPoolCount(data.poolCount ?? 0);
        setMyIdeas(data.myIdeas ?? []);
        setDrawnHistory(data.drawn ?? []);
      }
    } catch { /* ignore */ }
  }, [room]);

  const fetchSuggestions = useCallback(async () => {
    const p = getCachedProfile();
    if (!room || !p) return;
    setSuggestionsLoading(true);
    try {
      const res = await fetch(`/api/blindbox/suggest?roomId=${room.id}&userId=${p.id}`);
      if (res.ok) {
        const data = await res.json();
        if (data.suggestions?.length > 0) {
          setSuggestions(data.suggestions);
          setSuggestionsSource("ai");
          setSuggestionsLoading(false);
          return;
        }
      }
    } catch { /* ignore */ }
    setSuggestions(pickRandom(IDEA_INSPIRATIONS, 4));
    setSuggestionsSource("static");
    setSuggestionsLoading(false);
  }, [room]);

  const refreshSuggestions = useCallback(() => {
    if (suggestionsSource === "ai") {
      fetchSuggestions();
    } else {
      setSuggestions(pickRandom(IDEA_INSPIRATIONS, 4));
    }
  }, [suggestionsSource, fetchSuggestions]);

  const fetchAcceptedPlan = useCallback(async () => {
    const p = getCachedProfile();
    if (!room || !p) return;
    try {
      const res = await fetch(`/api/blindbox/plan?mode=latest&roomId=${room.id}&userId=${p.id}`);
      if (!res.ok) return;
      const data = await res.json();
      if (data.plan) {
        setActiveContract({
          id: data.plan.id,
          days: data.plan.days,
          endTime: data.plan.endTime ?? null,
        });
      }
    } catch { /* ignore */ }
  }, [room]);

  useEffect(() => {
    if (isMember && room) {
      fetchIdeas();
      fetchAcceptedPlan();
      fetchSuggestions();
    }
  }, [isMember, room, fetchIdeas, fetchAcceptedPlan, fetchSuggestions]);

  // Check for expired contracts on load
  useEffect(() => {
    const p = getCachedProfile();
    if (!isMember || !p) return;
    (async () => {
      try {
        const res = await fetch(`/api/blindbox/plan?mode=pending&userId=${p.id}`);
        if (!res.ok) return;
        const data = await res.json();
        if (data.pending?.length) setPendingContracts(data.pending);
      } catch { /* ignore */ }
    })();
  }, [isMember]);

  // Browser notification timer for active contract
  useEffect(() => {
    if (!activeContract?.endTime) return;
    const end = new Date(activeContract.endTime).getTime();
    const now = Date.now();
    const ms = end - now;
    if (ms <= 0) return;

    if (typeof Notification !== "undefined" && Notification.permission === "default") {
      Notification.requestPermission();
    }

    const timer = setTimeout(() => {
      if (typeof Notification !== "undefined" && Notification.permission === "granted") {
        const n = new Notification("周末契约到期", {
          body: "你的周末契约已结束，完成了吗？",
          icon: "/icon-192x192.png",
        });
        n.onclick = () => { window.focus(); n.close(); };
      }
      // Refresh pending contracts
      const p = getCachedProfile();
      if (p) {
        fetch(`/api/blindbox/plan?mode=pending&userId=${p.id}`)
          .then((r) => r.json())
          .then((d) => { if (d.pending?.length) setPendingContracts(d.pending); })
          .catch(() => {});
      }
    }, ms);

    return () => clearTimeout(timer);
  }, [activeContract?.endTime]);

  useEffect(() => {
    if (planLogRef.current) {
      planLogRef.current.scrollTop = planLogRef.current.scrollHeight;
    }
  }, [planStatusMessages]);

  useEffect(() => {
    if (isMember && inputRef.current) {
      const t = setTimeout(() => inputRef.current?.focus(), 300);
      timersRef.current.push(t);
    }
  }, [isMember]);

  const handleJoinRoom = async () => {
    if (joiningRoom || !profile || !room) return;
    setJoiningRoom(true);
    try {
      const res = await fetch("/api/blindbox/room/join", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ userId: profile.id, code }),
      });
      if (res.ok) {
        setIsMember(true);
        fetchRoom();
      }
    } catch { /* ignore */ }
    finally { setJoiningRoom(false); }
  };

  const handleSetLocation = useCallback(async () => {
    if (locating || !profile || !room) return;
    setLocating(true);
    try {
      const pos = await new Promise<GeolocationPosition>((resolve, reject) =>
        navigator.geolocation.getCurrentPosition(resolve, reject, { timeout: 10000 }),
      );
      const { latitude: lat, longitude: lng } = pos.coords;
      const regeoRes = await fetch(`/api/location/regeo?lat=${lat}&lng=${lng}`);
      const regeo = regeoRes.ok ? await regeoRes.json() : {};
      const cityName = regeo.name || "未知位置";

      const patchRes = await fetch(`/api/blindbox/room/${room.code}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ userId: profile.id, city: cityName, lat, lng }),
      });
      if (!patchRes.ok) throw new Error("保存位置失败");

      setRoom((prev) => prev ? { ...prev, city: cityName, lat, lng } : prev);
      toast.show("位置已设置");
    } catch {
      toast.show("获取位置失败，请允许定位权限");
    } finally {
      setLocating(false);
    }
  }, [locating, profile, room, toast]);

  const handleGeneratePlan = useCallback(async (timeConfig: { date: string; startHour: number; endHour: number }) => {
    if (generating || !profile || !room) return;
    setGenerating(true);
    setPhase("planning");
    setError("");
    setPlanStatusMessages([PLAN_STATUS_STEPS[0]]);
    const payload = {
      roomId: room.id,
      userId: profile.id,
      availableTime: timeConfig,
    };
    const stepRef = { current: 0 };
    const fallbackTimer = setInterval(() => {
      stepRef.current = (stepRef.current + 1) % PLAN_STATUS_STEPS.length;
      setPlanStatusMessages((prev) => [...prev, PLAN_STATUS_STEPS[stepRef.current]]);
    }, 2800);
    try {
      const res = await fetch("/api/blindbox/plan/stream", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });
      if (!res.ok) {
        const data = await res.json().catch(() => ({}));
        throw new Error(data.error || "生成失败");
      }
      const reader = res.body?.getReader();
      const decoder = new TextDecoder();
      if (!reader) throw new Error("无法读取响应");
      let buffer = "";
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const blocks = buffer.split("\n\n");
        buffer = blocks.pop() ?? "";
        for (const block of blocks) {
          let eventType = "";
          let data = "";
          for (const line of block.split("\n")) {
            if (line.startsWith("event:")) eventType = line.slice(6).trim();
            else if (line.startsWith("data:")) data = line.slice(5).trim();
          }
          if (eventType === "status") setPlanStatusMessages((prev) => [...prev, data]);
          else if (eventType === "plan") {
            const parsed = JSON.parse(data);
            setPlanId(parsed.id);
            setPlanDays(parsed.days);
            setPlanAccepted(false);
            setPhase("plan_reveal");
            fireConfetti();
          } else if (eventType === "error") {
            setError(data || "生成计划失败");
            setPhase("pool");
          }
        }
      }
    } catch (e) {
      try {
        const res = await fetch("/api/blindbox/plan", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload),
        });
        if (!res.ok) {
          const data = await res.json().catch(() => ({}));
          throw new Error(data.error || "生成失败");
        }
        const data = await res.json();
        setPlanId(data.id);
        setPlanDays(data.days);
        setPlanAccepted(false);
        setPhase("plan_reveal");
        fireConfetti();
      } catch (fallbackErr) {
        setError(fallbackErr instanceof Error ? fallbackErr.message : "生成计划失败");
        setPhase("pool");
      }
    } finally {
      clearInterval(fallbackTimer);
      setGenerating(false);
    }
  }, [generating, profile, room]);

  const handleSubmit = async () => {
    const text = input.trim();
    if (!text || submitting || !profile || !room) return;
    setSubmitting(true);
    setError("");
    try {
      const res = await fetch("/api/blindbox", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ roomId: room.id, userId: profile.id, content: text }),
      });
      if (!res.ok) {
        const data = await res.json();
        throw new Error(data.error || "提交失败");
      }
      const data = await res.json();
      setInput("");
      setPoolCount((c) => c + 1);
      setMyIdeas((prev) => [{
        id: data.id,
        content: text,
        createdAt: new Date().toISOString(),
        ...data.tags && {
          category: data.tags.category,
          timeSlot: data.tags.timeSlot,
          estimatedMinutes: data.tags.estimatedMinutes,
          costLevel: data.tags.costLevel,
          intensity: data.tags.intensity,
          needsBooking: data.tags.needsBooking,
          searchQuery: data.tags.searchQuery,
          searchType: data.tags.searchType,
        },
      }, ...prev]);
      setSubmitFlash(true);
      timersRef.current.push(setTimeout(() => setSubmitFlash(false), 600));
      boxControls.start({
        scale: [1, 1.08, 1],
        rotate: [0, -3, 3, 0],
        transition: { duration: 0.5 },
      });
      fetchSuggestions();
    } catch (e) {
      setError(e instanceof Error ? e.message : "提交失败");
    } finally {
      setSubmitting(false);
    }
  };

  const handleEditIdea = useCallback(async (ideaId: string, newContent: string) => {
    if (!profile) return;
    const trimmed = newContent.trim();
    if (!trimmed || trimmed.length > 200) return;
    try {
      const res = await fetch("/api/blindbox", {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ideaId, userId: profile.id, content: trimmed }),
      });
      if (!res.ok) {
        const data = await res.json();
        throw new Error(data.error || "编辑失败");
      }
      const data = await res.json();
      setMyIdeas((prev) => prev.map((i) => (i.id === ideaId ? {
        ...i,
        content: trimmed,
        ...data.tags && {
          category: data.tags.category,
          timeSlot: data.tags.timeSlot,
          estimatedMinutes: data.tags.estimatedMinutes,
          costLevel: data.tags.costLevel,
          intensity: data.tags.intensity,
          needsBooking: data.tags.needsBooking,
          searchQuery: data.tags.searchQuery,
          searchType: data.tags.searchType,
        },
      } : i)));
    } catch (e) {
      toast.show(e instanceof Error ? e.message : "编辑失败");
    }
  }, [profile, toast]);

  const handleDeleteIdea = useCallback(async (ideaId: string) => {
    if (!profile) return;
    try {
      const res = await fetch("/api/blindbox", {
        method: "DELETE",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ideaId, userId: profile.id }),
      });
      if (!res.ok) {
        const data = await res.json();
        throw new Error(data.error || "删除失败");
      }
      setMyIdeas((prev) => prev.filter((i) => i.id !== ideaId));
      setPoolCount((c) => Math.max(0, c - 1));
    } catch (e) {
      toast.show(e instanceof Error ? e.message : "删除失败");
    }
  }, [profile]);

  const handleDraw = async () => {
    if (poolCount === 0 || !profile || !room) {
      setError("盒子是空的，先往里面塞点想法吧！");
      return;
    }

    setPhase("shaking");
    setError("");

    await boxControls.start({
      rotate: [0, -8, 8, -10, 10, -12, 12, -8, 8, -4, 4, 0],
      scale: [1, 1.05, 0.95, 1.08, 0.92, 1.1, 0.9, 1.05, 0.95, 1],
      transition: { duration: 2.5, ease: "easeInOut" },
    });

    try {
      const res = await fetch("/api/blindbox/draw", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ roomId: room.id, userId: profile.id }),
      });

      if (!res.ok) {
        const data = await res.json();
        throw new Error(data.error || "抽取失败");
      }

      const idea = await res.json();
      setRevealedIdea(idea);
      setPhase("reveal");
      setPoolCount((c) => Math.max(0, c - 1));
      setDrawnHistory((prev) => [idea, ...prev]);
      fireConfetti();
    } catch (e) {
      setError(e instanceof Error ? e.message : "抽取失败");
      setPhase("pool");
    }
  };

  const fireConfetti = () => {
    const colors = ["#a855f7", "#6366f1", "#ec4899", "#f59e0b", "#10b981"];
    confetti({ particleCount: 100, spread: 120, origin: { y: 0.4 }, colors, startVelocity: 45, ticks: 250 });
    confettiAliveRef.current = true;
    const end = Date.now() + 3000;
    const frame = () => {
      if (Date.now() > end || !confettiAliveRef.current) return;
      confetti({ particleCount: 3, angle: 60, spread: 55, origin: { x: 0, y: 0.6 }, colors, startVelocity: 35, ticks: 150 });
      confetti({ particleCount: 3, angle: 120, spread: 55, origin: { x: 1, y: 0.6 }, colors, startVelocity: 35, ticks: 150 });
      requestAnimationFrame(frame);
    };
    timersRef.current.push(setTimeout(frame, 200));
  };

  const { share, copyToClipboard } = useShare();

  const handleCopyCode = useCallback(
    () => room ? copyToClipboard(room.code, "房间号已复制") : undefined,
    [room, copyToClipboard],
  );

  const handleShare = useCallback(() => {
    if (!room) return;
    const url = typeof window !== "undefined" ? `${window.location.origin}/blindbox/${room.code}` : "";
    share(
      { title: `周末契约 · ${room.name}`, text: `来和我一起玩周末盲盒吧！房间号：${room.code}`, url },
      handleCopyCode,
    );
  }, [room, share, handleCopyCode]);

  const isCreator = profile?.id === room?.creatorId;

  const handleBackToLobby = useCallback(() => {
    router.push("/blindbox");
    router.refresh();
  }, [router]);

  /** Non-creator: leave room (remove membership). Creator: delete room (after confirm). */
  const handleLeaveOrDelete = async () => {
    if (!confirmLeave) {
      setConfirmLeave(true);
      timersRef.current.push(setTimeout(() => setConfirmLeave(false), 3000));
      return;
    }
    if (leaving || !profile || !room) return;
    setLeaving(true);
    try {
      const res = await fetch(`/api/blindbox/room/${room.code}`, {
        method: "DELETE",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ userId: profile.id }),
      });
      if (!res.ok) {
        const data = await res.json();
        throw new Error(data.error || "操作失败");
      }
      router.push("/blindbox");
      router.refresh();
    } catch (e) {
      toast.show(e instanceof Error ? e.message : "操作失败");
      setConfirmLeave(false);
    } finally {
      setLeaving(false);
    }
  };

  if (pageLoading) {
    return <BlindboxRoomSkeleton />;
  }

  if (!room) return null;

  return (
    <div className={`relative flex h-dvh flex-col items-center bg-background px-5 py-6 scrollbar-none ${
      phase === "plan_reveal" ? "overflow-hidden" : "overflow-y-auto"
    }`}>

      {/* Header */}
      <div className="flex w-full max-w-sm items-center gap-3">
        <button
          onClick={() => router.push("/blindbox")}
          aria-label="返回"
          className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-surface ring-1 ring-border transition-colors active:bg-elevated"
        >
          <ArrowLeft size={16} className="text-muted" />
        </button>
        <div className="min-w-0 flex-1">
          <p className="truncate text-sm font-bold text-heading">{room.name}</p>
          <div className="mt-0.5 flex items-center gap-2">
            <p className="text-[10px] text-dim">房间 {room.code}</p>
            <button
              onClick={handleSetLocation}
              disabled={locating}
              className="flex items-center gap-0.5 text-[10px] font-medium text-purple-400/70 transition-colors active:text-purple-400"
            >
              {locating ? (
                <Loader2 size={9} className="animate-spin" />
              ) : (
                <MapPin size={9} />
              )}
              {room.city || "设置位置"}
            </button>
          </div>
        </div>

        {/* Members */}
        <div className="flex -space-x-1.5">
          {room.members.slice(0, 4).map((m) => (
            <div
              key={m.id}
              className="flex h-7 w-7 items-center justify-center rounded-full bg-elevated text-xs ring-2 ring-background"
              title={m.username}
            >
              {m.avatar}
            </div>
          ))}
          {room.members.length > 4 && (
            <div className="flex h-7 w-7 items-center justify-center rounded-full bg-elevated text-[10px] font-bold text-muted ring-2 ring-background">
              +{room.members.length - 4}
            </div>
          )}
        </div>

        <button
          onClick={() => setShowInvite(!showInvite)}
          aria-label="邀请成员"
          className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-purple-600/15 text-purple-400 ring-1 ring-purple-500/20 transition-colors active:bg-purple-600/25"
        >
          <Share2 size={14} />
        </button>
      </div>

      {/* Active contract indicator */}
      {activeContract && phase !== "plan_reveal" && (
        <motion.button
          className="mt-3 flex w-full max-w-sm items-center gap-2 rounded-xl bg-purple-600/10 px-3 py-2 ring-1 ring-purple-500/20 transition-colors active:bg-purple-600/20"
          initial={{ opacity: 0, y: -8 }}
          animate={{ opacity: 1, y: 0 }}
          onClick={() => {
            setPlanId(activeContract.id);
            setPlanDays(activeContract.days);
            setPlanAccepted(true);
            setPhase("plan_reveal");
          }}
        >
          <ClipboardCheck size={14} className="shrink-0 text-purple-400" />
          <span className="text-xs font-medium text-purple-300">契约进行中</span>
          <span className="text-[10px] text-purple-400/50">
            {activeContract.days.map((d) => d.date).join(" + ")}
          </span>
          <ChevronRight size={12} className="ml-auto text-purple-400/40" />
        </motion.button>
      )}

      {/* Invite panel */}
      <AnimatePresence>
        {showInvite && (
          <motion.div
            className="mt-3 w-full max-w-sm overflow-hidden rounded-xl bg-surface ring-1 ring-border"
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: "auto", opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
          >
            <div className="flex items-center gap-3 p-3">
              <span className="text-xs text-muted">房间号</span>
              <span className="font-mono text-base font-bold tracking-[0.2em] text-purple-400">
                {room.code}
              </span>
              <div className="flex-1" />
              <button
                onClick={handleCopyCode}
                className="flex h-8 items-center gap-1 rounded-lg bg-elevated px-3 text-xs font-medium text-secondary ring-1 ring-border transition-colors active:bg-subtle"
              >
                <Copy size={12} /> 复制
              </button>
              <button
                onClick={handleShare}
                className="flex h-8 items-center gap-1 rounded-lg bg-purple-600 px-3 text-xs font-medium text-white transition-colors active:bg-purple-500"
              >
                <Share2 size={12} /> 分享
              </button>
            </div>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Non-member state */}
      {!isMember ? (
        <motion.div
          className="mt-16 flex flex-col items-center gap-4"
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
        >
          <Package size={40} className="text-purple-400/50" strokeWidth={1.5} />
          <p className="text-sm text-tertiary">你还不是这个房间的成员</p>
          <Button
            onClick={handleJoinRoom}
            variant="purple"
            size="lg"
            loading={joiningRoom}
            icon={<LogIn size={16} />}
          >
            加入房间
          </Button>
        </motion.div>
      ) : (
        <>
          {/* Blind Box Visual — hidden during plan phases */}
          {phase !== "planning" && phase !== "plan_reveal" && (
            <div className="mt-8 flex flex-col items-center">
              <motion.div
                animate={boxControls}
                className="relative flex h-36 w-36 items-center justify-center"
              >
                <div className="absolute inset-0 rounded-3xl bg-linear-to-br from-purple-600/20 to-indigo-600/20 blur-xl" />
                <div className="relative flex h-32 w-32 flex-col items-center justify-center rounded-2xl bg-linear-to-br from-indigo-900 to-purple-900 shadow-2xl shadow-purple-900/50 ring-1 ring-purple-700/30">
                  <div className="absolute -top-2 left-1/2 h-5 w-[90%] -translate-x-1/2 rounded-t-xl bg-linear-to-r from-purple-700 to-indigo-700 shadow-md" />
                  <div className="absolute top-3 h-full w-3 bg-linear-to-b from-amber-400/60 to-amber-400/10" />
                  <div className="absolute top-6 h-3 w-full bg-linear-to-r from-amber-400/0 via-amber-400/60 to-amber-400/0" />
                  <motion.div
                    animate={submitFlash ? { scale: [1, 1.3, 1] } : {}}
                    transition={{ duration: 0.3 }}
                  >
                    <Package size={40} className="relative z-10 text-purple-300/60" strokeWidth={1.5} />
                  </motion.div>
                  <motion.div
                    className="absolute -right-2 -top-2 text-lg"
                    animate={{ rotate: [0, 15, -15, 0], scale: [1, 1.2, 1] }}
                    transition={{ duration: 2, repeat: Infinity, repeatDelay: 3 }}
                  >
                    ✨
                  </motion.div>
                </div>
              </motion.div>

              <motion.p
                className="mt-4 text-sm font-semibold text-muted"
                key={poolCount}
                initial={{ scale: 1.2, opacity: 0 }}
                animate={{ scale: 1, opacity: 1 }}
              >
                盒子里已有{" "}
                <span className="text-lg font-black text-purple-400">{poolCount}</span>{" "}
                个想法
              </motion.p>
            </div>
          )}

          {/* Pool / Shaking / Reveal phases */}
          <AnimatePresence mode="wait">
            {phase === "pool" && (
              <motion.div
                key="pool"
                className="mt-8 flex w-full max-w-sm flex-col items-center gap-5"
                initial={{ opacity: 0, y: 20 }}
                animate={{ opacity: 1, y: 0 }}
                exit={{ opacity: 0, y: -20 }}
              >
                <div className="flex w-full gap-2">
                  <input
                    ref={inputRef}
                    type="text"
                    placeholder="塞入一个疯狂的周末想法..."
                    value={input}
                    onChange={(e) => { setInput(e.target.value); setError(""); }}
                    onKeyDown={(e) => { if (e.key === "Enter") handleSubmit(); }}
                    maxLength={200}
                    disabled={submitting}
                    className="h-12 flex-1 rounded-xl border-none bg-surface px-4 text-sm text-foreground outline-none ring-1 ring-border transition-all placeholder:text-dim focus:ring-2 focus:ring-purple-600 disabled:opacity-50"
                  />
                  <button
                    onClick={handleSubmit}
                    disabled={!input.trim() || submitting}
                    aria-label="提交想法"
                    className="flex h-12 w-12 items-center justify-center rounded-xl bg-purple-600 text-white transition-colors hover:bg-purple-500 disabled:opacity-30"
                  >
                    {submitting ? <Loader2 size={18} className="animate-spin" /> : <Send size={18} />}
                  </button>
                </div>

                {!input && (
                  <div className="flex w-full flex-wrap items-center gap-1.5">
                    {suggestionsSource === "ai" ? (
                      <Sparkles size={13} className="mr-0.5 shrink-0 text-purple-400/80" />
                    ) : (
                      <Lightbulb size={13} className="mr-0.5 shrink-0 text-amber-500/80" />
                    )}
                    {suggestionsLoading ? (
                      <>
                        {[1, 2, 3].map((i) => (
                          <div key={i} className="h-6 w-20 animate-pulse rounded-full bg-surface/60 ring-1 ring-border/40" />
                        ))}
                      </>
                    ) : (
                      suggestions.map((s) => (
                        <button
                          key={s}
                          onClick={() => { setInput(s); inputRef.current?.focus(); }}
                          className="rounded-full bg-surface/80 px-2.5 py-1 text-xs text-secondary ring-1 ring-border/60 transition-all hover:bg-purple-600/10 hover:text-purple-400 hover:ring-purple-600/30 active:scale-95"
                        >
                          {s}
                        </button>
                      ))
                    )}
                    <button
                      onClick={refreshSuggestions}
                      disabled={suggestionsLoading}
                      aria-label="换一批灵感"
                      className="ml-auto flex h-6 w-6 items-center justify-center rounded-full text-muted transition-colors hover:bg-surface hover:text-secondary disabled:opacity-30"
                    >
                      {suggestionsLoading ? <Loader2 size={12} className="animate-spin" /> : <Shuffle size={12} />}
                    </button>
                  </div>
                )}

                <div className="flex w-full gap-2">
                  <motion.button
                    onClick={handleDraw}
                    disabled={poolCount === 0}
                    className="relative flex h-14 flex-1 items-center justify-center gap-2 overflow-hidden rounded-2xl bg-linear-to-r from-red-600 to-rose-500 text-sm font-black text-white shadow-lg shadow-red-900/40 transition-shadow hover:shadow-xl hover:shadow-red-900/50 disabled:opacity-40 disabled:shadow-none"
                    whileHover={{ scale: 1.02 }}
                    whileTap={{ scale: 0.97 }}
                  >
                    <div className="absolute inset-0 bg-linear-to-r from-transparent via-white/10 to-transparent -translate-x-full animate-[shimmer_3s_infinite]" />
                    <Flame size={18} />
                    抽一个
                  </motion.button>
                  <motion.button
                    onClick={() => {
                      if (!room?.city) {
                        toast.show("请先点击房间名下方设置位置");
                        return;
                      }
                      setPhase("time_select");
                    }}
                    disabled={poolCount < 2}
                    className="relative flex h-14 flex-1 items-center justify-center gap-2 overflow-hidden rounded-2xl bg-linear-to-r from-purple-600 to-indigo-600 text-sm font-black text-white shadow-lg shadow-purple-900/40 transition-shadow hover:shadow-xl hover:shadow-purple-900/50 disabled:opacity-40 disabled:shadow-none"
                    whileHover={{ scale: 1.02 }}
                    whileTap={{ scale: 0.97 }}
                  >
                    <Calendar size={18} />
                    周末计划
                  </motion.button>
                </div>

                {error && (
                  <motion.p
                    className="text-center text-xs font-medium text-rose-400"
                    initial={{ opacity: 0, y: -4 }}
                    animate={{ opacity: 1, y: 0 }}
                  >
                    {error}
                  </motion.p>
                )}

                {planDays.length > 0 && !planAccepted && (
                  <motion.button
                    onClick={() => setPhase("plan_reveal")}
                    className="flex w-full items-center justify-between rounded-xl bg-purple-600/10 px-4 py-2.5 ring-1 ring-purple-500/30 active:bg-purple-600/20"
                    initial={{ opacity: 0, y: 4 }}
                    animate={{ opacity: 1, y: 0 }}
                  >
                    <div className="flex items-center gap-2">
                      <Sparkles size={14} className="text-purple-400" />
                      <span className="text-xs font-bold text-purple-300">有一个待确认的计划</span>
                    </div>
                    <ChevronRight size={14} className="text-purple-400" />
                  </motion.button>
                )}
              </motion.div>
            )}

            {phase === "shaking" && (
              <motion.div
                key="shaking"
                className="mt-8 flex flex-col items-center gap-4"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
              >
                <p className="text-sm font-bold text-purple-300 animate-pulse">
                  命运正在决定...
                </p>
                <div className="flex gap-1">
                  {[0, 1, 2].map((i) => (
                    <motion.div
                      key={i}
                      className="h-2 w-2 rounded-full bg-purple-400"
                      animate={{ y: [0, -8, 0] }}
                      transition={{ duration: 0.6, repeat: Infinity, delay: i * 0.15 }}
                    />
                  ))}
                </div>
              </motion.div>
            )}

            {phase === "reveal" && revealedIdea && (
              <motion.div
                key="reveal"
                className="mt-8 flex w-full max-w-sm flex-col items-center gap-5"
                initial={{ opacity: 0, scale: 0.8 }}
                animate={{ opacity: 1, scale: 1 }}
                transition={{ type: "spring", damping: 15, stiffness: 200 }}
              >
                <div className="relative w-full overflow-hidden rounded-2xl bg-linear-to-br from-purple-900 via-indigo-900 to-purple-950 p-6 shadow-2xl shadow-purple-900/50 ring-1 ring-purple-600/30">
                  <div className="absolute left-3 top-3 h-6 w-6 border-l-2 border-t-2 border-purple-400/30 rounded-tl-sm" />
                  <div className="absolute right-3 top-3 h-6 w-6 border-r-2 border-t-2 border-purple-400/30 rounded-tr-sm" />
                  <div className="absolute bottom-3 left-3 h-6 w-6 border-b-2 border-l-2 border-purple-400/30 rounded-bl-sm" />
                  <div className="absolute bottom-3 right-3 h-6 w-6 border-b-2 border-r-2 border-purple-400/30 rounded-br-sm" />

                  <div className="relative z-10 text-center">
                    <p className="text-xs font-bold tracking-[0.3em] text-purple-400/70">
                      ✦ 周末契约 ✦
                    </p>
                    <motion.p
                      className="mt-4 text-xl font-black leading-relaxed text-white"
                      initial={{ opacity: 0, y: 10 }}
                      animate={{ opacity: 1, y: 0 }}
                      transition={{ delay: 0.3 }}
                    >
                      {revealedIdea.content}
                    </motion.p>
                    <div className="mx-auto mt-4 h-px w-16 bg-linear-to-r from-transparent via-purple-400/50 to-transparent" />

                    {/* Attribution */}
                    <div className="mt-3 flex items-center justify-center gap-2 text-[11px] text-purple-400/50">
                      {revealedIdea.user && (
                        <span>
                          {revealedIdea.user.avatar} {revealedIdea.user.username} 投入
                        </span>
                      )}
                      {revealedIdea.drawnBy && (
                        <>
                          <span>·</span>
                          <span>
                            {revealedIdea.drawnBy.avatar} {revealedIdea.drawnBy.username} 抽中
                          </span>
                        </>
                      )}
                    </div>

                    <p className="mt-2 text-[10px] font-medium text-purple-400/40">
                      此契约一旦开启，绝不反悔
                    </p>
                  </div>
                </div>

                <div className="flex items-center gap-3">
                  <Button
                    onClick={() => setShowShareCard(true)}
                    variant="purple"
                    shape="pill"
                    icon={<Share2 size={14} />}
                  >
                    分享契约
                  </Button>
                  <Button
                    onClick={() => { setPhase("pool"); setRevealedIdea(null); setShowShareCard(false); }}
                    variant="secondary"
                    shape="pill"
                  >
                    继续投入想法
                  </Button>
                </div>
              </motion.div>
            )}

            {phase === "planning" && (
              <motion.div
                key="planning"
                className="mt-8 flex flex-col items-center gap-4 w-full px-4"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
              >
                <motion.div
                  className="relative flex h-16 w-16 items-center justify-center"
                  animate={{ rotate: [0, 360] }}
                  transition={{ duration: 3, repeat: Infinity, ease: "linear" }}
                >
                  <div className="absolute inset-0 rounded-full bg-purple-600/15 blur-lg" />
                  <Sparkles size={24} className="relative text-purple-400" />
                </motion.div>
                <div className="w-full max-w-sm rounded-xl bg-surface/60 ring-1 ring-border/60 overflow-hidden">
                  <div
                    ref={planLogRef}
                    className="h-40 overflow-y-auto scrollbar-none p-3 flex flex-col gap-1.5"
                  >
                    {planStatusMessages.map((msg, i) => (
                      <motion.div
                        key={i}
                        initial={{ opacity: 0, y: 6 }}
                        animate={{ opacity: 1, y: 0 }}
                        transition={{ duration: 0.2 }}
                        className={`flex items-center gap-2 text-xs ${i === planStatusMessages.length - 1 ? "text-purple-300 font-medium" : "text-dim"}`}
                      >
                        {i === planStatusMessages.length - 1 ? (
                          <Loader2 size={11} className="shrink-0 animate-spin text-purple-400" />
                        ) : (
                          <span className="shrink-0 text-[10px] text-purple-500">✓</span>
                        )}
                        {msg}
                      </motion.div>
                    ))}
                  </div>
                </div>
              </motion.div>
            )}

            {phase === "plan_reveal" && planDays.length > 0 && (
              <motion.div
                key="plan_reveal"
                className="mt-4 flex min-h-0 flex-1 w-full"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
              >
                <BlindboxPlan
                  days={planDays}
                  accepted={planAccepted}
                  regenerating={generating}
                  onAccept={async () => {
                    setPlanAccepted(true);
                    fireConfetti();
                    if (planId && profile) {
                      try {
                        const res = await fetch("/api/blindbox/plan", {
                          method: "PATCH",
                          headers: { "Content-Type": "application/json" },
                          body: JSON.stringify({ planId, userId: profile.id, action: "accept" }),
                        });
                        const data = await res.json();
                        setActiveContract({
                          id: planId,
                          days: planDays,
                          endTime: data.endTime ?? null,
                        });
                      } catch { /* best-effort */ }
                    }
                    toast.show("契约已接受！");
                    timersRef.current.push(setTimeout(() => {
                      setPhase("pool");
                      setPlanId(null);
                      setPlanDays([]);
                      setPlanAccepted(false);
                    }, 1500));
                  }}
                  onRegenerate={() => {
                    setPlanId(null);
                    setPlanDays([]);
                    setPlanAccepted(false);
                    setPhase("time_select");
                  }}
                  onShare={() => setShowPlanShareCard(true)}
                  onBack={() => {
                    setPhase("pool");
                  }}
                />
              </motion.div>
            )}
          </AnimatePresence>

          {/* Time selector modal */}
          <AnimatePresence>
            {phase === "time_select" && (
              <WeekendTimeSelector
                onConfirm={handleGeneratePlan}
                onClose={() => setPhase("pool")}
                loading={generating}
              />
            )}
          </AnimatePresence>

          {myIdeas.length > 0 && phase === "pool" && (
            <BlindboxMyIdeas
              ideas={myIdeas}
              onEdit={handleEditIdea}
              onDelete={handleDeleteIdea}
            />
          )}

          {phase !== "shaking" && phase !== "planning" && (
            <BlindboxDrawnHistory items={drawnHistory} />
          )}
        </>
      )}

      {revealedIdea && room && (
        <ShareCardModal
          open={showShareCard}
          onClose={() => setShowShareCard(false)}
          data={{
            type: "blindbox",
            idea: revealedIdea.content,
            submitter: revealedIdea.user ?? undefined,
            drawer: revealedIdea.drawnBy ?? undefined,
            roomName: room.name,
          }}
        />
      )}

      {planDays.length > 0 && room && (
        <ShareCardModal
          open={showPlanShareCard}
          onClose={() => setShowPlanShareCard(false)}
          data={{
            type: "plan",
            days: planDays,
            roomName: room.name,
          }}
        />
      )}

      {/* Leave / Back — hidden during plan view. Creator: 返回大厅 (no delete) + optional 删除房间. Non-creator: 退出房间. */}
      {isMember && room && phase !== "plan_reveal" && phase !== "planning" && (
        <motion.div
          className="mt-12 flex w-full max-w-sm flex-col items-center gap-2"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ delay: 0.5 }}
        >
          {isCreator ? (
            <>
              <button
                onClick={handleBackToLobby}
                className="flex w-full items-center justify-center gap-2 rounded-xl py-2.5 text-xs font-medium text-muted transition-colors hover:text-foreground active:bg-elevated"
              >
                <ArrowLeft size={13} />
                返回大厅
              </button>
              <button
                onClick={handleLeaveOrDelete}
                disabled={leaving}
                className={`flex w-full items-center justify-center gap-2 rounded-xl py-2 text-xs font-medium transition-colors ${
                  confirmLeave
                    ? "bg-rose-600/15 text-rose-400 ring-1 ring-rose-500/30"
                    : "text-dim hover:text-rose-400/80"
                }`}
              >
                {leaving ? (
                  <Loader2 size={12} className="animate-spin" />
                ) : (
                  <Trash2 size={12} />
                )}
                {confirmLeave ? "确认删除房间？所有想法将被清除" : "删除房间"}
              </button>
            </>
          ) : (
            <button
              onClick={handleLeaveOrDelete}
              disabled={leaving}
              className={`flex w-full items-center justify-center gap-2 rounded-xl py-2.5 text-xs font-medium transition-colors ${
                confirmLeave
                  ? "bg-rose-600/15 text-rose-400 ring-1 ring-rose-500/30"
                  : "text-muted hover:text-rose-400/80"
              }`}
            >
              {leaving ? (
                <Loader2 size={13} className="animate-spin" />
              ) : (
                <LogOut size={13} />
              )}
              {confirmLeave ? "确认退出房间？" : "退出房间"}
            </button>
          )}
        </motion.div>
      )}

      <div className="h-8 shrink-0" />

      {/* Contract expiration check modal */}
      {pendingContracts.length > 0 && profile && (
        <ContractCompletionModal
          contracts={pendingContracts}
          userId={profile.id}
          onDone={() => {
            setPendingContracts([]);
            setActiveContract(null);
          }}
        />
      )}
    </div>
  );
}
