// web/src/App.tsx
import React, { useState, useEffect, useRef, useMemo } from "react";
import { Header } from "./components/Header";
import { ChatArea } from "./components/ChatArea";
import { LoginScreen } from "./components/LoginScreen";
import { ProfileEditor } from "./components/ProfileEditor";
import { ReviewBanner } from "./components/ReviewBanner";
import { Onboarding } from "./components/Onboarding";
import { X, ChevronLeft, ChevronRight } from "lucide-react";
import { Button } from "./components/ui/button";
import { Toaster } from "./components/ui/sonner";
import { toast } from "sonner";
import { LeftSidebar } from "./components/sidebar/LeftSidebar";

// backend API bindings
import { apiChat, apiUpload, apiMemoryline, apiQuizStart } from "./lib/api";

// NEW: review-star logic
import {
  type ReviewStarState,
  type ReviewEventType,
  markReviewActive,
  normalizeToday,
  starOpacity,
  energyPct,
} from "./lib/reviewStar";

export interface Message {
  id: string;
  role: "user" | "assistant";
  content: string;
  timestamp: Date;
  references?: string[];
  sender?: GroupMember;
  showNextButton?: boolean;
  questionData?: {
    type: "multiple-choice" | "fill-in-blank" | "open-ended";
    question: string;
    options?: string[];
    correctAnswer: string;
    explanation: string;
    sampleAnswer?: string;
  };
}

export interface User {
  // required identity
  name: string;
  email: string;

  // profile fields
  studentId?: string;
  department?: string;
  yearLevel?: string;
  major?: string;
  bio?: string; // may be generated by Clare, then user can edit in ProfileEditor

  // learning preferences
  learningStyle?: string; // "visual" | "auditory" | ...
  learningPace?: string; // "slow" | "moderate" | "fast"

  // avatar
  avatarUrl?: string;

  // control flags
  onboardingCompleted?: boolean;
}

export interface GroupMember {
  id: string;
  name: string;
  email: string;
  avatar?: string;
  isAI?: boolean;
}

export type SpaceType = "individual" | "group";

export interface CourseInfo {
  id: string;
  name: string;
  instructor: { name: string; email: string };
  teachingAssistant: { name: string; email: string };
}

export interface Workspace {
  id: string;
  name: string;
  type: SpaceType;
  avatar: string;
  members?: GroupMember[];
  category?: "course" | "personal";
  courseName?: string;
  courseInfo?: CourseInfo;
  isEditable?: boolean;
}

export type FileType = "syllabus" | "lecture-slides" | "literature-review" | "other";

export interface UploadedFile {
  file: File;
  type: FileType;
}

export type LearningMode = "general" | "concept" | "socratic" | "exam" | "assignment" | "summary";
export type Language = "auto" | "en" | "zh";
export type ChatMode = "ask" | "review" | "quiz";

export interface SavedItem {
  id: string;
  title: string;
  content: string;
  type: "export" | "quiz" | "summary";
  timestamp: Date;
  isSaved: boolean;
  format?: "pdf" | "text";
  workspaceId: string;
}

export interface SavedChat {
  id: string;
  title: string;
  messages: Message[];
  chatMode: ChatMode;
  timestamp: Date;
}

const DOC_TYPE_MAP: Record<FileType, string> = {
  syllabus: "Syllabus",
  "lecture-slides": "Lecture Slides / PPT",
  "literature-review": "Literature Review / Paper",
  other: "Other Course Document",
};

function mapLanguagePref(lang: Language): string {
  if (lang === "zh") return "中文";
  if (lang === "en") return "English";
  return "Auto";
}

// ✅ localStorage helpers for saved chats
function savedChatsStorageKey(email: string) {
  return `saved_chats::${email}`;
}

function hydrateSavedChats(raw: any): SavedChat[] {
  if (!Array.isArray(raw)) return [];
  return raw
    .map((c: any) => {
      try {
        return {
          ...c,
          timestamp: c?.timestamp ? new Date(c.timestamp) : new Date(),
          messages: Array.isArray(c?.messages)
            ? c.messages.map((m: any) => ({
                ...m,
                timestamp: m?.timestamp ? new Date(m.timestamp) : new Date(),
              }))
            : [],
        } as SavedChat;
      } catch {
        return null;
      }
    })
    .filter(Boolean) as SavedChat[];
}

// ✅ localStorage helpers for user profile
function profileStorageKey(email: string) {
  return `user_profile::${email}`;
}

function hydrateUserFromStorage(base: User): User {
  try {
    const raw = localStorage.getItem(profileStorageKey(base.email));
    if (!raw) return base;
    const saved = JSON.parse(raw) as Partial<User>;
    return { ...base, ...saved };
  } catch {
    return base;
  }
}

function App() {
  const [isDarkMode, setIsDarkMode] = useState(() => {
    const saved = localStorage.getItem("theme");
    return saved === "dark" || (!saved && window.matchMedia("(prefers-color-scheme: dark)").matches);
  });

  const [user, setUser] = useState<User | null>(null);

  // ✅ unified user update helpers
  const updateUser = (patch: Partial<User>) => {
    setUser((prev) => (prev ? { ...prev, ...patch } : prev));
  };

  const handleUserSave = (next: User) => {
    setUser((prev) => {
      if (!prev) return next;
      return {
        ...prev,
        ...next,
        onboardingCompleted: next.onboardingCompleted ?? prev.onboardingCompleted,
      };
    });
  };

  // ✅ persist user profile whenever it changes (per-email)
  useEffect(() => {
    if (!user?.email) return;
    try {
      localStorage.setItem(profileStorageKey(user.email), JSON.stringify(user));
    } catch {
      // ignore
    }
  }, [user]);

  // -------------------------
  // ✅ Course selection (stable)
  // -------------------------
  const MYSPACE_COURSE_KEY = "myspace_selected_course";

  const [currentCourseId, setCurrentCourseId] = useState<string>(() => {
    return localStorage.getItem(MYSPACE_COURSE_KEY) || "course1";
  });

  const availableCourses: CourseInfo[] = [
    {
      id: "course1",
      name: "Introduction to AI",
      instructor: { name: "Dr. Sarah Johnson", email: "sarah.johnson@university.edu" },
      teachingAssistant: { name: "Michael Chen", email: "michael.chen@university.edu" },
    },
    {
      id: "course2",
      name: "Machine Learning",
      instructor: { name: "Prof. David Lee", email: "david.lee@university.edu" },
      teachingAssistant: { name: "Emily Zhang", email: "emily.zhang@university.edu" },
    },
    {
      id: "course3",
      name: "Data Structures",
      instructor: { name: "Dr. Robert Smith", email: "robert.smith@university.edu" },
      teachingAssistant: { name: "Lisa Wang", email: "lisa.wang@university.edu" },
    },
    {
      id: "course4",
      name: "Web Development",
      instructor: { name: "Prof. Maria Garcia", email: "maria.garcia@university.edu" },
      teachingAssistant: { name: "James Brown", email: "james.brown@university.edu" },
    },
  ];

  const [askMessages, setAskMessages] = useState<Message[]>([
    {
      id: "1",
      role: "assistant",
      content:
        "👋 Hi! I'm Clare, your AI teaching assistant. I'm here to help you learn through personalized tutoring. Feel free to ask me anything about the course materials, or upload your documents to get started!",
      timestamp: new Date(),
    },
  ]);

  const [reviewMessages, setReviewMessages] = useState<Message[]>([
    {
      id: "review-1",
      role: "assistant",
      content:
        "📚 Welcome to Review mode! I'll help you review and consolidate your learning. Let's go through what you've learned!",
      timestamp: new Date(),
    },
  ]);

  const [quizMessages, setQuizMessages] = useState<Message[]>([
    {
      id: "quiz-1",
      role: "assistant",
      content:
        "🎯 Welcome to Quiz mode! I'll test your understanding with personalized questions based on your learning history. Ready to start?",
      timestamp: new Date(),
    },
  ]);

  const [learningMode, setLearningMode] = useState<LearningMode>("concept");
  const [language, setLanguage] = useState<Language>("auto");
  const [chatMode, setChatMode] = useState<ChatMode>("ask");

  const messages = chatMode === "ask" ? askMessages : chatMode === "review" ? reviewMessages : quizMessages;

  const prevChatModeRef = useRef<ChatMode>(chatMode);

  useEffect(() => {
    let currentMessages: Message[];
    let setCurrentMessages: (messages: Message[]) => void;

    if (chatMode === "ask") {
      currentMessages = askMessages;
      setCurrentMessages = setAskMessages;
    } else if (chatMode === "review") {
      currentMessages = reviewMessages;
      setCurrentMessages = setReviewMessages;
    } else {
      currentMessages = quizMessages;
      setCurrentMessages = setQuizMessages;
    }

    const hasUserMessages = currentMessages.some((msg) => msg.role === "user");
    const expectedWelcomeId = chatMode === "ask" ? "1" : chatMode === "review" ? "review-1" : "quiz-1";
    const hasWelcomeMessage = currentMessages.some((msg) => msg.id === expectedWelcomeId && msg.role === "assistant");
    const modeChanged = prevChatModeRef.current !== chatMode;

    if ((modeChanged || currentMessages.length === 0 || !hasWelcomeMessage) && !hasUserMessages) {
      const initialMessages: Record<ChatMode, Message[]> = {
        ask: [
          {
            id: "1",
            role: "assistant",
            content:
              "👋 Hi! I'm Clare, your AI teaching assistant. I'm here to help you learn through personalized tutoring. Feel free to ask me anything about the course materials, or upload your documents to get started!",
            timestamp: new Date(),
          },
        ],
        review: [
          {
            id: "review-1",
            role: "assistant",
            content:
              "📚 Welcome to Review mode! I'll help you review and consolidate your learning. Let's go through what you've learned!",
            timestamp: new Date(),
          },
        ],
        quiz: [
          {
            id: "quiz-1",
            role: "assistant",
            content:
              "🎯 Welcome to Quiz mode! I'll test your understanding with personalized questions based on your learning history. Ready to start?",
            timestamp: new Date(),
          },
        ],
      };

      setCurrentMessages(initialMessages[chatMode]);
    }

    prevChatModeRef.current = chatMode;
  }, [chatMode, askMessages.length, reviewMessages.length, quizMessages.length]);

  const [uploadedFiles, setUploadedFiles] = useState<UploadedFile[]>([]);
  const [memoryProgress, setMemoryProgress] = useState(36);

  const [quizState, setQuizState] = useState<{
    currentQuestion: number;
    waitingForAnswer: boolean;
    showNextButton: boolean;
  }>({
    currentQuestion: 0,
    waitingForAnswer: false,
    showNextButton: false,
  });

  const [isTyping, setIsTyping] = useState(false);
  const [leftSidebarOpen, setLeftSidebarOpen] = useState(false);
  const [leftPanelVisible, setLeftPanelVisible] = useState(true);
  const [showProfileEditor, setShowProfileEditor] = useState(false);
  const [showOnboarding, setShowOnboarding] = useState(false);

  const [showReviewBanner, setShowReviewBanner] = useState(() => true);
  const [showClearDialog, setShowClearDialog] = useState(false);

  const [savedItems, setSavedItems] = useState<SavedItem[]>([]);
  const [recentlySavedId, setRecentlySavedId] = useState<string | null>(null);

  const [savedChats, setSavedChats] = useState<SavedChat[]>([]);

  // ✅ load saved chats after login
  useEffect(() => {
    if (!user?.email) return;
    try {
      const raw = localStorage.getItem(savedChatsStorageKey(user.email));
      if (!raw) {
        setSavedChats([]);
        return;
      }
      const parsed = JSON.parse(raw);
      setSavedChats(hydrateSavedChats(parsed));
    } catch {
      setSavedChats([]);
    }
  }, [user?.email]);

  // ✅ persist saved chats whenever changed
  useEffect(() => {
    if (!user?.email) return;
    try {
      localStorage.setItem(savedChatsStorageKey(user.email), JSON.stringify(savedChats));
    } catch {
      // ignore
    }
  }, [savedChats, user?.email]);

  const [groupMembers] = useState<GroupMember[]>([
    { id: "clare", name: "Clare AI", email: "clare@ai.assistant", isAI: true },
    { id: "1", name: "Sarah Johnson", email: "sarah.j@university.edu" },
    { id: "2", name: "Michael Chen", email: "michael.c@university.edu" },
    { id: "3", name: "Emma Williams", email: "emma.w@university.edu" },
  ]);

  const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
  const [currentWorkspaceId, setCurrentWorkspaceId] = useState<string>("individual");

  // ✅ used to prevent duplicate upload per file fingerprint
  const uploadedFingerprintsRef = useRef<Set<string>>(new Set());

  useEffect(() => {
    if (user) {
      const userAvatar = `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(user.email)}`;
      const course1Info = availableCourses.find((c) => c.id === "course1");
      const course2Info = availableCourses.find((c) => c.name === "AI Ethics"); // may be undefined, that's OK

      setWorkspaces([
        { id: "individual", name: "My Space", type: "individual", avatar: userAvatar },
        {
          id: "group-1",
          name: "CS 101 Study Group",
          type: "group",
          avatar: "https://api.dicebear.com/7.x/shapes/svg?seed=cs101group",
          members: groupMembers,
          category: "course",
          courseName: course1Info?.name || "CS 101",
          courseInfo: course1Info,
        },
        {
          id: "group-2",
          name: "AI Ethics Team",
          type: "group",
          avatar: "https://api.dicebear.com/7.x/shapes/svg?seed=aiethicsteam",
          members: groupMembers,
          category: "course",
          courseName: course2Info?.name || "AI Ethics",
          courseInfo: course2Info,
        },
      ]);
    }
  }, [user, groupMembers, availableCourses]);

  const fallbackWorkspace: Workspace = {
    id: "individual",
    name: "My Space",
    type: "individual",
    avatar: user ? `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(user.email)}` : "",
  };

  const currentWorkspace: Workspace =
    workspaces.find((w) => w.id === currentWorkspaceId) || workspaces[0] || fallbackWorkspace;

  const spaceType: SpaceType = currentWorkspace?.type || "individual";

  // =========================
  // ✅ Scheme 1: "My Space" uses Group-like sidebar view model
  // =========================
  const mySpaceCourseInfo = useMemo(() => {
    return availableCourses.find((c) => c.id === currentCourseId);
  }, [availableCourses, currentCourseId]);

  const mySpaceUserMember: GroupMember | null = useMemo(() => {
    if (!user) return null;
    return {
      id: user.email,
      name: user.name,
      email: user.email,
      avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(user.email)}`,
    };
  }, [user]);

  const clareMember: GroupMember = useMemo(
    () => ({ id: "clare", name: "Clare AI", email: "clare@ai.assistant", isAI: true }),
    []
  );

  const sidebarWorkspaces: Workspace[] = useMemo(() => {
    if (!workspaces?.length) return workspaces;
    if (!mySpaceUserMember) return workspaces;

    return workspaces.map((w) => {
      if (w.id !== "individual") return w;

      return {
        ...w,
        category: "course",
        courseName: mySpaceCourseInfo?.name || w.courseName || "My Course",
        courseInfo: mySpaceCourseInfo,
        members: [clareMember, mySpaceUserMember],
      };
    });
  }, [workspaces, mySpaceCourseInfo, mySpaceUserMember, clareMember]);

  const sidebarSpaceType: SpaceType = useMemo(() => {
    return currentWorkspaceId === "individual" ? "group" : spaceType;
  }, [currentWorkspaceId, spaceType]);

  const sidebarGroupMembers: GroupMember[] = useMemo(() => {
    if (currentWorkspaceId === "individual" && mySpaceUserMember) {
      return [clareMember, mySpaceUserMember];
    }
    return groupMembers;
  }, [currentWorkspaceId, mySpaceUserMember, clareMember, groupMembers]);

  // =========================
  // ✅ Stable course switching logic
  // =========================
  const didHydrateMySpaceRef = useRef(false);

  const handleCourseChange = (nextCourseId: string) => {
    if (!nextCourseId) return;

    if (currentWorkspace.type === "group" && currentWorkspace.category === "course") {
      return;
    }

    setCurrentCourseId(nextCourseId);
    try {
      localStorage.setItem(MYSPACE_COURSE_KEY, nextCourseId);
    } catch {
      // ignore
    }
  };

  useEffect(() => {
    if (!currentWorkspace) return;

    if (currentWorkspace.type === "group" && currentWorkspace.category === "course") {
      const cid = currentWorkspace.courseInfo?.id;
      if (cid && cid !== currentCourseId) setCurrentCourseId(cid);
      didHydrateMySpaceRef.current = false;
      return;
    }

    if (currentWorkspace.type === "individual") {
      if (!didHydrateMySpaceRef.current) {
        didHydrateMySpaceRef.current = true;

        const saved = localStorage.getItem(MYSPACE_COURSE_KEY);
        const valid = saved && availableCourses.some((c) => c.id === saved) ? saved : undefined;

        const next = valid || currentCourseId || "course1";
        if (next !== currentCourseId) setCurrentCourseId(next);
      }
    }
  }, [
    currentWorkspaceId,
    currentWorkspace?.type,
    currentWorkspace?.category,
    currentWorkspace?.courseInfo?.id,
    availableCourses,
    currentCourseId,
    currentWorkspace,
  ]);

  useEffect(() => {
    if (currentWorkspace?.type !== "individual") return;
    try {
      const prev = localStorage.getItem(MYSPACE_COURSE_KEY);
      if (prev !== currentCourseId) localStorage.setItem(MYSPACE_COURSE_KEY, currentCourseId);
    } catch {
      // ignore
    }
  }, [currentCourseId, currentWorkspace?.type]);

  useEffect(() => {
    document.documentElement.classList.toggle("dark", isDarkMode);
    localStorage.setItem("theme", isDarkMode ? "dark" : "light");
  }, [isDarkMode]);

  useEffect(() => {
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      document.body.style.overflow = prev;
    };
  }, []);

  useEffect(() => {
    if (!user) return;

    (async () => {
      try {
        const r = await apiMemoryline(user.email);
        const pct = Math.round((r.progress_pct ?? 0) * 100);
        setMemoryProgress(pct);
      } catch {
        // silent
      }
    })();
  }, [user]);

  // =========================
  // ✅ Review Star (按天) state
  // =========================
  const reviewStarKey = useMemo(() => {
    if (!user) return "";
    return `review_star::${user.email}::${currentWorkspaceId}`;
  }, [user, currentWorkspaceId]);

  const [reviewStarState, setReviewStarState] = useState<ReviewStarState | null>(null);

  useEffect(() => {
    if (!user || !reviewStarKey) return;
    if (chatMode !== "review") return;

    const next = normalizeToday(reviewStarKey);
    setReviewStarState(next);
  }, [chatMode, reviewStarKey, user]);

  const handleReviewActivity = (event: ReviewEventType) => {
    if (!user || !reviewStarKey) return;
    const next = markReviewActive(reviewStarKey, event);
    setReviewStarState(next);
  };

  const reviewStarOpacity = starOpacity(reviewStarState);
  const reviewEnergyPct = energyPct(reviewStarState);

  const getCurrentDocTypeForChat = (): string => {
    if (uploadedFiles.length > 0) {
      const last = uploadedFiles[uploadedFiles.length - 1];
      return DOC_TYPE_MAP[last.type] || "Syllabus";
    }
    return "Syllabus";
  };

  const handleSendMessage = async (content: string) => {
    if (!user) return;

    const hasText = !!content.trim();
    const hasFiles = uploadedFiles.length > 0;

    if (!hasText && !hasFiles) return;

    const fileNames = hasFiles ? uploadedFiles.map((f) => f.file.name) : [];
    const fileLine = fileNames.length ? `Uploaded files: ${fileNames.join(", ")}` : "";

    const effectiveContent = hasText
      ? content
      : `I've uploaded file(s). Please read them and help me based on their content.\n${fileLine}`.trim();

    const sender: GroupMember = {
      id: user.email,
      name: user.name,
      email: user.email,
      avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(user.email)}`,
    };

    const userVisibleContent = hasText
      ? content
      : `📎 Sent ${fileNames.length} file(s)\n${fileNames.map((n) => `- ${n}`).join("\n")}`;

    const userMessage: Message = {
      id: Date.now().toString(),
      role: "user",
      content: userVisibleContent,
      timestamp: new Date(),
      sender,
    };

    if (chatMode === "ask") setAskMessages((prev) => [...prev, userMessage]);
    else if (chatMode === "review") setReviewMessages((prev) => [...prev, userMessage]);
    else setQuizMessages((prev) => [...prev, userMessage]);

    if (chatMode === "quiz") {
      setIsTyping(true);

      try {
        const docType = getCurrentDocTypeForChat();

        const r = await apiChat({
          user_id: user.email,
          message: effectiveContent,
          learning_mode: "quiz",
          language_preference: mapLanguagePref(language),
          doc_type: docType,
        });

        const normalizeRefs = (raw: any): string[] => {
          const arr = Array.isArray(raw) ? raw : [];
          return arr
            .map((x) => {
              if (typeof x === "string") {
                const s = x.trim();
                return s ? s : null;
              }
              const a = x?.source_file ? String(x.source_file) : "";
              const b = x?.section ? String(x.section) : "";
              const s = `${a}${a && b ? " — " : ""}${b}`.trim();
              return s || null;
            })
            .filter(Boolean) as string[];
        };

        const refs = normalizeRefs((r as any).refs ?? (r as any).references);

        const assistantMessage: Message = {
          id: (Date.now() + 1).toString(),
          role: "assistant",
          content: r.reply || "",
          timestamp: new Date(),
          references: refs.length ? refs : undefined,
          sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
          showNextButton: false,
        };

        setIsTyping(false);

        setTimeout(() => {
          setQuizMessages((prev) => [...prev, assistantMessage]);
          setQuizState((prev) => ({ ...prev, waitingForAnswer: true, showNextButton: false }));
        }, 50);
      } catch (e: any) {
        setIsTyping(false);
        toast.error(e?.message || "Quiz failed");

        const assistantMessage: Message = {
          id: (Date.now() + 1).toString(),
          role: "assistant",
          content: "Sorry — quiz request failed. Please try again.",
          timestamp: new Date(),
          sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
        };

        setTimeout(() => {
          setQuizMessages((prev) => [...prev, assistantMessage]);
        }, 50);
      }

      return;
    }

    setIsTyping(true);
    try {
      const docType = getCurrentDocTypeForChat();

      const r = await apiChat({
        user_id: user.email,
        message: effectiveContent,
        learning_mode: learningMode,
        language_preference: mapLanguagePref(language),
        doc_type: docType,
      });

      const refs = (r.refs || [])
        .map((x: any) => {
          const a = x?.source_file ? String(x.source_file) : "";
          const b = x?.section ? String(x.section) : "";
          const s = `${a}${a && b ? " — " : ""}${b}`.trim();
          return s || null;
        })
        .filter(Boolean) as string[];

      const assistantMessage: Message = {
        id: (Date.now() + 1).toString(),
        role: "assistant",
        content: r.reply || "",
        timestamp: new Date(),
        references: refs.length ? refs : undefined,
        sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
      };

      setIsTyping(false);

      setTimeout(() => {
        if (chatMode === "ask") setAskMessages((prev) => [...prev, assistantMessage]);
        else if (chatMode === "review") setReviewMessages((prev) => [...prev, assistantMessage]);
      }, 50);

      try {
        const ml = await apiMemoryline(user.email);
        setMemoryProgress(Math.round((ml.progress_pct ?? 0) * 100));
      } catch {
        // ignore
      }
    } catch (e: any) {
      setIsTyping(false);
      toast.error(e?.message || "Chat failed");

      const assistantMessage: Message = {
        id: (Date.now() + 1).toString(),
        role: "assistant",
        content: "Sorry — chat request failed. Please try again.",
        timestamp: new Date(),
        sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
      };

      setTimeout(() => {
        if (chatMode === "ask") setAskMessages((prev) => [...prev, assistantMessage]);
        if (chatMode === "review") setReviewMessages((prev) => [...prev, assistantMessage]);
      }, 50);
    }
  };

  const handleNextQuestion = async () => {
    if (!user) return;

    const prompt = "Please give me another question of the same quiz style.";
    const sender: GroupMember = {
      id: user.email,
      name: user.name,
      email: user.email,
      avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(user.email)}`,
    };

    const userMessage: Message = {
      id: Date.now().toString(),
      role: "user",
      content: prompt,
      timestamp: new Date(),
      sender,
    };

    setQuizMessages((prev) => [...prev, userMessage]);
    setIsTyping(true);

    try {
      const docType = getCurrentDocTypeForChat();
      const r = await apiChat({
        user_id: user.email,
        message: prompt,
        learning_mode: "quiz",
        language_preference: mapLanguagePref(language),
        doc_type: docType,
      });

      const refs = (r.refs || [])
        .map((x: any) => {
          const a = x?.source_file ? String(x.source_file) : "";
          const b = x?.section ? String(x.section) : "";
          const s = `${a}${a && b ? " — " : ""}${b}`.trim();
          return s || null;
        })
        .filter(Boolean) as string[];

      const assistantMessage: Message = {
        id: (Date.now() + 1).toString(),
        role: "assistant",
        content: r.reply || "",
        timestamp: new Date(),
        references: refs.length ? refs : undefined,
        sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
        showNextButton: false,
      };

      setIsTyping(false);

      setTimeout(() => {
        setQuizMessages((prev) => [...prev, assistantMessage]);
        setQuizState((prev) => ({
          ...prev,
          currentQuestion: prev.currentQuestion + 1,
          waitingForAnswer: true,
          showNextButton: false,
        }));
      }, 50);
    } catch (e: any) {
      setIsTyping(false);
      toast.error(e?.message || "Quiz failed");

      const assistantMessage: Message = {
        id: (Date.now() + 1).toString(),
        role: "assistant",
        content: "Sorry — quiz request failed. Please try again.",
        timestamp: new Date(),
        sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
      };

      setTimeout(() => setQuizMessages((prev) => [...prev, assistantMessage]), 50);
    }
  };

  const handleStartQuiz = async () => {
    if (!user) return;

    setIsTyping(true);
    try {
      const docType = getCurrentDocTypeForChat();

      const r = await apiQuizStart({
        user_id: user.email,
        language_preference: mapLanguagePref(language),
        doc_type: docType,
        learning_mode: "quiz",
      });

      const refs = (r.refs || [])
        .map((x: any) => {
          const a = x?.source_file ? String(x.source_file) : "";
          const b = x?.section ? String(x.section) : "";
          const s = `${a}${a && b ? " — " : ""}${b}`.trim();
          return s || null;
        })
        .filter(Boolean) as string[];

      const assistantMessage: Message = {
        id: Date.now().toString(),
        role: "assistant",
        content: r.reply || "",
        timestamp: new Date(),
        references: refs.length ? refs : undefined,
        sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
        showNextButton: false,
      };

      setIsTyping(false);

      setTimeout(() => {
        setQuizMessages((prev) => [...prev, assistantMessage]);
        setQuizState({ currentQuestion: 0, waitingForAnswer: true, showNextButton: false });
      }, 50);
    } catch (e: any) {
      setIsTyping(false);
      toast.error(e?.message || "Start quiz failed");

      const assistantMessage: Message = {
        id: Date.now().toString(),
        role: "assistant",
        content: "Sorry — could not start the quiz. Please try again.",
        timestamp: new Date(),
        sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
      };

      setTimeout(() => setQuizMessages((prev) => [...prev, assistantMessage]), 50);
    }
  };

  // =========================
  //  File Upload (FIXED)
  // =========================

  const handleFileUpload = async (input: File[] | FileList | null | undefined) => {
    const files = Array.isArray(input) ? input : input ? Array.from(input) : [];
    if (!files.length) return;

    const newFiles: UploadedFile[] = files.map((file) => ({ file, type: "other" as FileType }));

    setUploadedFiles((prev) => [...prev, ...newFiles]);

    if (!user) return;

    for (const f of files) {
      const fp = `${f.name}::${f.size}::${f.lastModified}`;
      if (uploadedFingerprintsRef.current.has(fp)) continue;
      uploadedFingerprintsRef.current.add(fp);

      try {
        await apiUpload({
          user_id: user.email,
          doc_type: DOC_TYPE_MAP["other"] || "Other Course Document",
          file: f,
        });
        toast.success(`File uploaded: ${f.name}`);
      } catch (e: any) {
        toast.error(e?.message || `Upload failed: ${f.name}`);
        uploadedFingerprintsRef.current.delete(fp);
      }
    }
  };

  const handleRemoveFile = (arg: any) => {
    setUploadedFiles((prev) => {
      if (!prev.length) return prev;

      let idx = -1;

      if (typeof arg === "number") {
        idx = arg;
      } else {
        const file =
          arg?.file instanceof File
            ? (arg as UploadedFile).file
            : arg instanceof File
            ? (arg as File)
            : null;

        if (file) {
          idx = prev.findIndex(
            (x) =>
              x.file.name === file.name && x.file.size === file.size && x.file.lastModified === file.lastModified
          );
        }
      }

      if (idx < 0 || idx >= prev.length) return prev;

      const removed = prev[idx]?.file;
      const next = prev.filter((_, i) => i !== idx);

      if (removed) {
        const fp = `${removed.name}::${removed.size}::${removed.lastModified}`;
        uploadedFingerprintsRef.current.delete(fp);
      }

      return next;
    });
  };

  const handleFileTypeChange = async (index: number, type: FileType) => {
    if (!user) return;

    const target = uploadedFiles[index]?.file;
    if (!target) return;

    setUploadedFiles((prev) => prev.map((f, i) => (i === index ? { ...f, type } : f)));

    const fp = `${target.name}::${target.size}::${target.lastModified}`;
    if (uploadedFingerprintsRef.current.has(fp)) return;
    uploadedFingerprintsRef.current.add(fp);

    try {
      await apiUpload({
        user_id: user.email,
        doc_type: DOC_TYPE_MAP[type] || "Other Course Document",
        file: target,
      });
      toast.success("File uploaded to backend");
    } catch (e: any) {
      toast.error(e?.message || "Upload failed");
      uploadedFingerprintsRef.current.delete(fp);
    }
  };

  const isCurrentChatSaved = (): SavedChat | null => {
    if (messages.length <= 1) return null;

    return (
      savedChats.find((chat) => {
        if (chat.chatMode !== chatMode) return false;
        if (chat.messages.length !== messages.length) return false;

        return chat.messages.every((savedMsg, idx) => {
          const currentMsg = messages[idx];
          return savedMsg.id === currentMsg.id && savedMsg.role === currentMsg.role && savedMsg.content === currentMsg.content;
        });
      }) || null
    );
  };

  const handleDeleteSavedChat = (id: string) => {
    setSavedChats((prev) => prev.filter((chat) => chat.id !== id));
    toast.success("Chat deleted");
  };

  const handleRenameSavedChat = (id: string, newTitle: string) => {
    setSavedChats((prev) => prev.map((chat) => (chat.id === id ? { ...chat, title: newTitle } : chat)));
    toast.success("Chat renamed");
  };

  const handleSaveChat = () => {
    if (messages.length <= 1) {
      toast.info("No conversation to save");
      return;
    }

    const existingChat = isCurrentChatSaved();
    if (existingChat) {
      handleDeleteSavedChat(existingChat.id);
      toast.success("Chat unsaved");
      return;
    }

    const title = `Chat - ${chatMode === "ask" ? "Ask" : chatMode === "review" ? "Review" : "Quiz"} - ${new Date().toLocaleDateString()}`;

    const newChat: SavedChat = {
      id: Date.now().toString(),
      title,
      messages: [...messages],
      chatMode,
      timestamp: new Date(),
    };

    setSavedChats((prev) => [newChat, ...prev]);
    setLeftPanelVisible(true);
    toast.success("Chat saved!");
  };

  const handleLoadChat = (savedChat: SavedChat) => {
    setChatMode(savedChat.chatMode);

    if (savedChat.chatMode === "ask") setAskMessages(savedChat.messages);
    else if (savedChat.chatMode === "review") setReviewMessages(savedChat.messages);
    else {
      setQuizMessages(savedChat.messages);
      setQuizState({ currentQuestion: 0, waitingForAnswer: false, showNextButton: false });
    }

    toast.success("Chat loaded!");
  };

  const handleClearConversation = (shouldSave: boolean = false) => {
    if (shouldSave) handleSaveChat();

    const initialMessages: Record<ChatMode, Message[]> = {
      ask: [
        {
          id: "1",
          role: "assistant",
          content:
            "👋 Hi! I'm Clare, your AI teaching assistant. I'm here to help you learn through personalized tutoring. Feel free to ask me anything about the course materials, or upload your documents to get started!",
          timestamp: new Date(),
        },
      ],
      review: [
        {
          id: "review-1",
          role: "assistant",
          content:
            "📚 Welcome to Review mode! I'll help you review and consolidate your learning. Let's go through what you've learned!",
          timestamp: new Date(),
        },
      ],
      quiz: [
        {
          id: "quiz-1",
          role: "assistant",
          content:
            "🎯 Welcome to Quiz mode! I'll test your understanding with personalized questions based on your learning history. Ready to start?",
          timestamp: new Date(),
        },
      ],
    };

    if (chatMode === "ask") setAskMessages(initialMessages.ask);
    else if (chatMode === "review") setReviewMessages(initialMessages.review);
    else {
      setQuizMessages(initialMessages.quiz);
      setQuizState({ currentQuestion: 0, waitingForAnswer: false, showNextButton: false });
    }
  };

  const handleSave = (
    content: string,
    type: "export" | "quiz" | "summary",
    saveAsChat: boolean = false,
    format: "pdf" | "text" = "text",
    workspaceId?: string
  ) => {
    if (!content.trim()) return;

    if (saveAsChat && type !== "summary") {
      const chatMessages: Message[] = [
        {
          id: "1",
          role: "assistant",
          content:
            "👋 Hi! I'm Clare, your AI teaching assistant. I'm here to help you learn through personalized tutoring. Feel free to ask me anything about the course materials, or upload your documents to get started!",
          timestamp: new Date(),
        },
        { id: Date.now().toString(), role: "assistant", content, timestamp: new Date() },
      ];

      const title = type === "export" ? "Exported Conversation" : "Micro-Quiz";
      const newChat: SavedChat = {
        id: Date.now().toString(),
        title: `${title} - ${new Date().toLocaleDateString()}`,
        messages: chatMessages,
        chatMode: "ask",
        timestamp: new Date(),
      };

      setSavedChats((prev) => [newChat, ...prev]);
      setLeftPanelVisible(true);
      toast.success("Chat saved!");
      return;
    }

    const existingItem = savedItems.find((item) => item.content === content && item.type === type);
    if (existingItem) {
      handleUnsave(existingItem.id);
      return;
    }

    const title = type === "export" ? "Exported Conversation" : type === "quiz" ? "Micro-Quiz" : "Summarization";
    const newItem: SavedItem = {
      id: Date.now().toString(),
      title: `${title} - ${new Date().toLocaleDateString()}`,
      content,
      type,
      timestamp: new Date(),
      isSaved: true,
      format,
      workspaceId: workspaceId || currentWorkspaceId,
    };

    setSavedItems((prev) => [newItem, ...prev]);
    setRecentlySavedId(newItem.id);
    setLeftPanelVisible(true);

    setTimeout(() => setRecentlySavedId(null), 2000);
    toast.success("Saved for later!");
  };

  const handleUnsave = (id: string) => {
    setSavedItems((prev) => prev.filter((item) => item.id !== id));
    toast.success("Removed from saved items");
  };

  const handleCreateWorkspace = (payload: { name: string; category: "course" | "personal"; courseId?: string; invites: string[] }) => {
    const id = `group-${Date.now()}`;
    const avatar = `https://api.dicebear.com/7.x/shapes/svg?seed=${encodeURIComponent(payload.name)}`;

    const creatorMember: GroupMember = user
      ? {
          id: user.email,
          name: user.name,
          email: user.email,
          avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(user.email)}`,
        }
      : { id: "unknown", name: "Unknown", email: "unknown@email.com" };

    const members: GroupMember[] = [
      creatorMember,
      ...payload.invites.map((email) => ({
        id: email,
        name: email.split("@")[0] || email,
        email,
      })),
    ];

    let newWorkspace: Workspace;

    if (payload.category === "course") {
      const courseInfo = availableCourses.find((c) => c.id === payload.courseId);
      newWorkspace = {
        id,
        name: payload.name,
        type: "group",
        avatar,
        members,
        category: "course",
        courseName: courseInfo?.name || "Untitled Course",
        courseInfo,
      };
    } else {
      newWorkspace = {
        id,
        name: payload.name,
        type: "group",
        avatar,
        members,
        category: "personal",
        isEditable: true,
      };
    }

    setWorkspaces((prev) => [...prev, newWorkspace]);
    setCurrentWorkspaceId(id);

    if (payload.category === "course" && payload.courseId) {
      setCurrentCourseId(payload.courseId);
    }

    toast.success("New group workspace created");
  };

  const handleReviewClick = () => {
    setChatMode("review");
    setShowReviewBanner(false);
    localStorage.setItem("reviewBannerDismissed", "true");
  };

  const handleDismissReviewBanner = () => {
    setShowReviewBanner(false);
    localStorage.setItem("reviewBannerDismissed", "true");
  };

  // ✅ login: hydrate profile and only show onboarding if not completed
  const handleLogin = (newUser: User) => {
    const hydrated = hydrateUserFromStorage(newUser);
    setUser(hydrated);
    setShowOnboarding(!hydrated.onboardingCompleted);
  };

  const handleOnboardingComplete = (updatedUser: User) => {
    handleUserSave({ ...updatedUser, onboardingCompleted: true });
    setShowOnboarding(false);
  };

  const handleOnboardingSkip = () => {
    updateUser({ onboardingCompleted: true });
    setShowOnboarding(false);
  };

  if (!user) return <LoginScreen onLogin={handleLogin} />;

  if (showOnboarding && user)
    return <Onboarding user={user} onComplete={handleOnboardingComplete} onSkip={handleOnboardingSkip} />;

  return (
    <div className="fixed inset-0 w-full bg-background overflow-hidden">
      <Toaster />

      <div className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden">
        <div className="flex-shrink-0">
          <Header
            user={user}
            onMenuClick={() => setLeftSidebarOpen(!leftSidebarOpen)}
            onUserClick={() => setShowProfileEditor(true)}
            isDarkMode={isDarkMode}
            onToggleDarkMode={() => setIsDarkMode(!isDarkMode)}
            language={language}
            onLanguageChange={setLanguage}
            workspaces={workspaces}
            currentWorkspace={currentWorkspace}
            onWorkspaceChange={setCurrentWorkspaceId}
            onCreateWorkspace={handleCreateWorkspace}
            onLogout={() => setUser(null)}
            availableCourses={availableCourses}
            onUserUpdate={handleUserSave}
            reviewStarOpacity={reviewStarOpacity}
            reviewEnergyPct={reviewEnergyPct}
            onStarClick={() => {
              setChatMode("review");
              setShowReviewBanner(false);
              localStorage.setItem("reviewBannerDismissed", "true");
            }}
          />
        </div>

        {showProfileEditor && user && (
          <ProfileEditor user={user} onSave={handleUserSave} onClose={() => setShowProfileEditor(false)} />
        )}

        {showReviewBanner && (
          <div className="flex-shrink-0 w-full bg-background border-b border-border relative z-50">
            <ReviewBanner onReview={handleReviewClick} onDismiss={handleDismissReviewBanner} />
          </div>
        )}

        <div className="flex flex-1 min-h-0 min-w-0 overflow-hidden relative">
          {!leftPanelVisible && (
            <Button
              variant="secondary"
              size="icon"
              onClick={() => setLeftPanelVisible(true)}
              className="hidden lg:flex absolute z-[100] h-8 w-5 shadow-lg rounded-full bg-card border border-border transition-all duration-200 ease-in-out hover:translate-x-[10px]"
              style={{ left: "-5px", top: "1rem" }}
              title="Open panel"
            >
              <ChevronRight className="h-3 w-3" />
            </Button>
          )}

          {leftSidebarOpen && (
            <div className="fixed inset-0 bg-black/50 z-40 lg:hidden" onClick={() => setLeftSidebarOpen(false)} />
          )}

          {leftPanelVisible ? (
            <aside className="hidden lg:flex w-80 h-full min-h-0 min-w-0 bg-card border-r border-border overflow-hidden relative flex-col">
              <Button
                variant="secondary"
                size="icon"
                onClick={() => setLeftPanelVisible(false)}
                className="absolute z-[70] h-8 w-5 shadow-lg rounded-full bg-card border border-border"
                style={{ right: "-10px", top: "1rem" }}
                title="Close panel"
              >
                <ChevronLeft className="h-3 w-3" />
              </Button>

              <div className="flex-1 min-h-0 min-w-0 overflow-hidden">
                <LeftSidebar
                  learningMode={learningMode}
                  language={language}
                  onLearningModeChange={setLearningMode}
                  onLanguageChange={setLanguage}
                  spaceType={sidebarSpaceType}
                  groupMembers={sidebarGroupMembers}
                  user={user}
                  onLogin={setUser}
                  onLogout={() => setUser(null)}
                  isLoggedIn={!!user}
                  onEditProfile={() => setShowProfileEditor(true)}
                  savedItems={savedItems}
                  recentlySavedId={recentlySavedId}
                  onUnsave={handleUnsave}
                  onSave={handleSave}
                  savedChats={savedChats}
                  onLoadChat={handleLoadChat}
                  onDeleteSavedChat={handleDeleteSavedChat}
                  onRenameSavedChat={handleRenameSavedChat}
                  currentWorkspaceId={currentWorkspaceId}
                  workspaces={sidebarWorkspaces}
                  selectedCourse={currentCourseId}
                  availableCourses={availableCourses}
                />
              </div>
            </aside>
          ) : null}

          <aside
            className={[
              "fixed lg:hidden z-50",
              "left-0 top-0 bottom-0",
              "w-80 bg-card border-r border-border",
              "transform transition-transform duration-300 ease-in-out",
              leftSidebarOpen ? "translate-x-0" : "-translate-x-full",
              "overflow-hidden flex flex-col",
            ].join(" ")}
          >
            <div className="p-4 border-b border-border flex justify-between items-center flex-shrink-0">
              <h3>Settings & Guide</h3>
              <Button variant="ghost" size="icon" onClick={() => setLeftSidebarOpen(false)}>
                <X className="h-5 w-5" />
              </Button>
            </div>

            <div className="flex-1 min-h-0 overflow-hidden">
              <LeftSidebar
                learningMode={learningMode}
                language={language}
                onLearningModeChange={setLearningMode}
                onLanguageChange={setLanguage}
                spaceType={sidebarSpaceType}
                groupMembers={sidebarGroupMembers}
                user={user}
                onLogin={setUser}
                onLogout={() => setUser(null)}
                isLoggedIn={!!user}
                onEditProfile={() => setShowProfileEditor(true)}
                savedItems={savedItems}
                recentlySavedId={recentlySavedId}
                onUnsave={handleUnsave}
                onSave={handleSave}
                savedChats={savedChats}
                onLoadChat={handleLoadChat}
                onDeleteSavedChat={handleDeleteSavedChat}
                onRenameSavedChat={handleRenameSavedChat}
                currentWorkspaceId={currentWorkspaceId}
                workspaces={sidebarWorkspaces}
                selectedCourse={currentCourseId}
                availableCourses={availableCourses}
              />
            </div>
          </aside>

          <main className="flex flex-1 min-w-0 min-h-0 overflow-hidden flex-col">
            <div className="flex-1 min-h-0 min-w-0 overflow-hidden">
              <ChatArea
                messages={messages}
                onSendMessage={handleSendMessage}
                uploadedFiles={uploadedFiles}
                onFileUpload={handleFileUpload}
                onRemoveFile={handleRemoveFile}
                onFileTypeChange={handleFileTypeChange}
                memoryProgress={memoryProgress}
                isLoggedIn={!!user}
                learningMode={learningMode}
                onClearConversation={() => setShowClearDialog(true)}
                onSaveChat={handleSaveChat}
                onLearningModeChange={setLearningMode}
                spaceType={spaceType}
                chatMode={chatMode}
                onChatModeChange={setChatMode}
                onNextQuestion={handleNextQuestion}
                onStartQuiz={handleStartQuiz}
                quizState={quizState}
                isTyping={isTyping}
                showClearDialog={showClearDialog}
                onConfirmClear={(shouldSave) => {
                  handleClearConversation(shouldSave);
                  setShowClearDialog(false);
                }}
                onCancelClear={() => setShowClearDialog(false)}
                savedChats={savedChats}
                workspaces={workspaces}
                currentWorkspaceId={currentWorkspaceId}
                onSaveFile={(content, type, _format, targetWorkspaceId) =>
                  handleSave(content, type, false, (_format ?? "text") as "pdf" | "text", targetWorkspaceId)
                }
                leftPanelVisible={leftPanelVisible}
                currentCourseId={currentCourseId}
                onCourseChange={handleCourseChange}
                availableCourses={availableCourses}
                showReviewBanner={showReviewBanner}
                onReviewActivity={handleReviewActivity}
                currentUserId={user?.email}
                docType={"Syllabus"}
                // ✅ bio is still allowed to be updated by chat/Clare
                onProfileBioUpdate={(bio) => updateUser({ bio })}
              />
            </div>
          </main>
        </div>
      </div>
    </div>
  );
}

export default App;
