import { useState, useEffect, useMemo, useRef, type TouchEventHandler } from 'react';
import { Card, Button, Modal, App } from 'antd';
import { useNavigate, useLocation } from 'react-router-dom';
import { useUser, useQuiz } from '../contexts';
import type { QuizQuestion } from '../contexts';
import { quizAPI } from '../services/api';
import { questionTypeMap } from '../utils/validation';
import { detectHorizontalSwipe } from '../utils/swipe';
import { UserLayout } from '../layouts/UserLayout';
import { QuizHeader } from './quiz/components/QuizHeader';
import { QuizProgress } from './quiz/components/QuizProgress';
import { OptionList } from './quiz/components/OptionList';
import { QuizFooter } from './quiz/components/QuizFooter';

type Question = QuizQuestion;

interface LocationState {
  questions?: QuizQuestion[];
  totalScore?: number;
  timeLimit?: number;
  subjectId?: string;
  taskId?: string;
  taskName?: string;
}

const QuizPage = () => {
  const navigate = useNavigate();
  const { message } = App.useApp();
  const location = useLocation();
  const { user } = useUser();
  const { questions, setQuestions, currentQuestionIndex, setCurrentQuestionIndex, answers, setAnswer, setAnswers, clearQuiz } = useQuiz();
  const [loading, setLoading] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [answerSheetOpen, setAnswerSheetOpen] = useState(false);
  const [timeLeft, setTimeLeft] = useState<number | null>(null);
  const [timeLimit, setTimeLimit] = useState<number | null>(null);
  const [subjectId, setSubjectId] = useState<string>('');
  const [taskId, setTaskId] = useState<string>('');
  const [taskName, setTaskName] = useState<string>('');
  const lastTickSavedAtMsRef = useRef<number>(0);
  const saveDebounceTimerRef = useRef<number | null>(null);
  const [navDirection, setNavDirection] = useState<'next' | 'prev'>('next');
  const [enterAnimationOn, setEnterAnimationOn] = useState(false);
  const touchStartRef = useRef<{ x: number; y: number; time: number } | null>(null);
  const questionHeadingRef = useRef<HTMLHeadingElement | null>(null);

  useEffect(() => {
    const el = questionHeadingRef.current;
    if (!el) return;
    el.focus();
  }, [currentQuestionIndex]);

  useEffect(() => {
    const onKeyDown = (event: KeyboardEvent) => {
      if (event.defaultPrevented) return;

      const active = document.activeElement as HTMLElement | null;
      const activeTag = (active?.tagName || '').toLowerCase();

      const inputType =
        activeTag === 'input' ? ((active as HTMLInputElement | null)?.type || '').toLowerCase() : '';
      const isTextualInputType =
        activeTag === 'input'
          ? inputType === '' ||
            inputType === 'text' ||
            inputType === 'search' ||
            inputType === 'tel' ||
            inputType === 'url' ||
            inputType === 'email' ||
            inputType === 'password' ||
            inputType === 'number'
          : false;
      const inTextInput =
        activeTag === 'textarea' ||
        active?.getAttribute('role') === 'textbox' ||
        active?.classList.contains('ant-input') ||
        active?.closest?.('.ant-input') ||
        isTextualInputType ||
        active?.isContentEditable;

      if (inTextInput) return;

      if (event.key === 'ArrowLeft') {
        event.preventDefault();
        handlePrevious();
        return;
      }

      if (event.key === 'ArrowRight') {
        event.preventDefault();
        handleNext();
        return;
      }

      if (event.key === 'Escape') {
        if (answerSheetOpen) {
          event.preventDefault();
          setAnswerSheetOpen(false);
        }
      }
    };

    window.addEventListener('keydown', onKeyDown);
    return () => window.removeEventListener('keydown', onKeyDown);
  }, [answerSheetOpen, currentQuestionIndex, questions.length]);

  useEffect(() => {
    if (!user) {
      message.warning('请先填写个人信息');
      navigate('/');
      return;
    }

    const state = location.state as LocationState;

    clearQuiz();

    if (state?.questions) {
      const nextSubjectId = state.subjectId || '';
      const nextTaskId = state.taskId || '';
      const nextTaskName = state.taskName || '';
      const nextTimeLimit = state.timeLimit || 60;

      setQuestions(state.questions);
      setAnswers({});
      setCurrentQuestionIndex(0);
      setTimeLimit(nextTimeLimit);
      setTimeLeft(nextTimeLimit * 60);
      setSubjectId(nextSubjectId);
      setTaskId(nextTaskId);
      setTaskName(nextTaskName);

      const progressKey = buildProgressKey(user.id, nextSubjectId, nextTaskId);
      setActiveProgress(user.id, progressKey);
      saveProgress(progressKey, {
        questions: state.questions,
        answers: {},
        currentQuestionIndex: 0,
        timeLeftSeconds: nextTimeLimit * 60,
        timeLimitMinutes: nextTimeLimit,
        subjectId: nextSubjectId,
        taskId: nextTaskId,
      });
      return;
    }

    const restored = restoreActiveProgress(user.id);
    if (restored) {
      setQuestions(restored.questions);
      setAnswers(restored.answers);
      setCurrentQuestionIndex(restored.currentQuestionIndex);
      setTimeLimit(restored.timeLimitMinutes);
      setTimeLeft(restored.timeLeftSeconds);
      setSubjectId(restored.subjectId);
      setTaskId(restored.taskId);
      return;
    }

    generateQuiz();
  }, [user, navigate, location]);

  // 倒计时逻辑
  useEffect(() => {
    if (timeLeft === null || timeLeft <= 0) return;

    const timer = setInterval(() => {
      setTimeLeft(prev => {
        if (prev === null || prev <= 1) {
          clearInterval(timer);
          handleTimeUp();
          return 0;
        }
        return prev - 1;
      });
    }, 1000);

    return () => clearInterval(timer);
  }, [timeLeft]);

  const generateQuiz = async () => {
    try {
      setLoading(true);
      const response = await quizAPI.generateQuiz(user!.id);
      setQuestions(response.data.questions);
      setCurrentQuestionIndex(0);
      setAnswers({});
      setTimeLimit(60);
      setTimeLeft(60 * 60);
      setSubjectId('');
      setTaskId('');

      const progressKey = buildProgressKey(user!.id, '', '');
      setActiveProgress(user!.id, progressKey);
      saveProgress(progressKey, {
        questions: response.data.questions,
        answers: {},
        currentQuestionIndex: 0,
        timeLeftSeconds: 60 * 60,
        timeLimitMinutes: 60,
        subjectId: '',
        taskId: '',
      });
    } catch (error: any) {
      message.error(error.message || '生成试卷失败');
    } finally {
      setLoading(false);
    }
  };

  const handleTimeUp = () => {
    message.warning('考试时间已到，将自动提交答案');
    setTimeout(() => {
      handleSubmit(true);
    }, 1000);
  };

  const getTagColor = (type: string) => {
    switch (type) {
      case 'single': return 'bg-orange-100 text-orange-800 border-orange-200';
      case 'multiple': return 'bg-blue-100 text-blue-800 border-blue-200';
      case 'judgment': return 'bg-purple-100 text-purple-800 border-purple-200';
      case 'text': return 'bg-green-100 text-green-800 border-green-200';
      default: return 'bg-gray-100 text-gray-800 border-gray-200';
    }
  };

  const handleAnswerChange = (questionId: string, value: string | string[]) => {
    setAnswer(questionId, value);
  };

  useEffect(() => {
    if (!user) return;
    if (!questions.length) return;
    if (timeLeft === null) return;

    if (saveDebounceTimerRef.current !== null) {
      window.clearTimeout(saveDebounceTimerRef.current);
    }

    saveDebounceTimerRef.current = window.setTimeout(() => {
      const progressKey = getActiveProgressKey(user.id);
      if (!progressKey) return;
      saveProgress(progressKey, {
        questions,
        answers,
        currentQuestionIndex,
        timeLeftSeconds: timeLeft,
        timeLimitMinutes: timeLimit || 60,
        subjectId,
        taskId,
      });
      saveDebounceTimerRef.current = null;
    }, 300);

    return () => {
      if (saveDebounceTimerRef.current !== null) {
        window.clearTimeout(saveDebounceTimerRef.current);
        saveDebounceTimerRef.current = null;
      }
    };
  }, [user, questions, answers, currentQuestionIndex, subjectId, taskId, timeLeft, timeLimit]);

  useEffect(() => {
    if (!user) return;
    if (!questions.length) return;
    if (timeLeft === null) return;

    const now = Date.now();
    if (now - lastTickSavedAtMsRef.current < 5000) return;
    lastTickSavedAtMsRef.current = now;

    const progressKey = getActiveProgressKey(user.id);
    if (!progressKey) return;

    saveProgress(progressKey, {
      questions,
      answers,
      currentQuestionIndex,
      timeLeftSeconds: timeLeft,
      timeLimitMinutes: timeLimit || 60,
      subjectId,
      taskId,
    });
  }, [user, questions.length, timeLeft]);

  const handleNext = () => {
    if (currentQuestionIndex < questions.length - 1) {
      setNavDirection('next');
      setCurrentQuestionIndex(currentQuestionIndex + 1);
    }
  };

  const handlePrevious = () => {
    if (currentQuestionIndex > 0) {
      setNavDirection('prev');
      setCurrentQuestionIndex(currentQuestionIndex - 1);
    }
  };

  const handleJumpTo = (index: number) => {
    if (index < 0 || index >= questions.length) return;
    if (index === currentQuestionIndex) return;
    setNavDirection(index > currentQuestionIndex ? 'next' : 'prev');
    setCurrentQuestionIndex(index);
  };

  const handleJumpToFirstUnanswered = () => {
    const firstUnansweredIndex = questions.findIndex(q => Number(q.score) > 0 && !answers[q.id]);
    if (firstUnansweredIndex !== -1) {
      handleJumpTo(firstUnansweredIndex);
    }
    setAnswerSheetOpen(false);
  };

  const handleSubmit = async (forceSubmit = false) => {
    try {
      setSubmitting(true);

      if (!forceSubmit) {
        const unansweredQuestions = questions.filter(q => Number(q.score) > 0 && !answers[q.id]);
        if (unansweredQuestions.length > 0) {
          message.warning(`还有 ${unansweredQuestions.length} 道题未作答`);
          return;
        }
      }

      const answersData = questions.map(question => {
        const userAnswer = (answers[question.id] ?? '') as any;
        const isCorrect = checkAnswer(question, userAnswer);
        return {
          questionId: question.id,
          userAnswer,
          score: isCorrect ? question.score : 0,
          isCorrect
        };
      });

      const response = await quizAPI.submitQuiz({
        userId: user!.id,
        subjectId: subjectId || undefined,
        taskId: taskId || undefined,
        answers: answersData
      }) as any;

      const payload = response?.data ?? response;
      const recordId = payload?.recordId;
      if (!recordId) {
        throw new Error('提交成功但未返回记录ID');
      }

      message.success('答题提交成功！');
      clearActiveProgress(user!.id);
      navigate(`/result/${recordId}`);
    } catch (error: any) {
      message.error(error.message || '提交失败');
    } finally {
      setSubmitting(false);
    }
  };

  const handleGiveUp = () => {
    if (!user) return;
    Modal.confirm({
      title: '确认弃考？',
      content: '弃考将清空本次答题进度与计时信息，且不计入考试次数。',
      okText: '确认弃考',
      cancelText: '继续答题',
      okButtonProps: { danger: true },
      onOk: () => {
        clearActiveProgress(user.id);
        clearQuiz();
        navigate('/tasks');
      },
    });
  };

  const checkAnswer = (question: Question, userAnswer: string | string[]): boolean => {
    if (Number(question.score) === 0) return true;
    if (!userAnswer) return false;

    if (question.type === 'multiple') {
      const correctAnswers = Array.isArray(question.answer) ? question.answer : [question.answer];
      const userAnswers = Array.isArray(userAnswer) ? userAnswer : [userAnswer];
      return correctAnswers.length === userAnswers.length && 
             correctAnswers.every(answer => userAnswers.includes(answer));
    } else {
      return userAnswer === question.answer;
    }
  };

  const answeredCount = useMemo(() => {
    return questions.reduce((count, q) => (answers[q.id] || Number(q.score) === 0 ? count + 1 : count), 0);
  }, [questions, answers]);

  useEffect(() => {
    if (!questions.length) return;
    setEnterAnimationOn(false);
    const rafId = requestAnimationFrame(() => setEnterAnimationOn(true));
    return () => cancelAnimationFrame(rafId);
  }, [currentQuestionIndex, questions.length]);

  if (loading || !questions.length) {
    return (
      <UserLayout>
        <div className="flex items-center justify-center h-full min-h-[500px]">
          <div className="text-center">
            <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-mars-600 mx-auto mb-4"></div>
            <p className="text-gray-700">正在生成试卷...</p>
          </div>
        </div>
      </UserLayout>
    );
  }

  const currentQuestion = questions[currentQuestionIndex];

  const handleTouchStart: TouchEventHandler = (e) => {
    if (e.touches.length !== 1) return;
    const target = e.target as HTMLElement | null;
    if (target?.closest('textarea,input,[role="textbox"],.ant-input,.ant-checkbox-wrapper,.ant-radio-wrapper')) return;
    const touch = e.touches[0];
    touchStartRef.current = { x: touch.clientX, y: touch.clientY, time: Date.now() };
  };

  const handleTouchEnd: TouchEventHandler = (e) => {
    const start = touchStartRef.current;
    touchStartRef.current = null;
    if (!start) return;

    const touch = e.changedTouches[0];
    if (!touch) return;

    const direction = detectHorizontalSwipe({
      startX: start.x,
      startY: start.y,
      endX: touch.clientX,
      endY: touch.clientY,
      elapsedMs: Date.now() - start.time,
    });

    if (direction === 'left') {
      handleNext();
      return;
    }
    if (direction === 'right') {
      handlePrevious();
    }
  };

  return (
    <div className="min-h-screen bg-gray-50 flex flex-col font-sans">
      <QuizHeader 
        current={currentQuestionIndex + 1}
        total={questions.length}
        timeLeft={timeLeft}
        onGiveUp={handleGiveUp}
        taskName={taskName}
      />
      
      {/* Mobile Progress Bar */}
      <div className="lg:hidden">
        <QuizProgress 
          current={currentQuestionIndex + 1}
          total={questions.length}
        />
      </div>

      <main className="flex-1 w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 lg:py-8">
        <div className="grid grid-cols-1 lg:grid-cols-12 gap-8 h-full items-start">
          {/* Left Column: Question Area */}
          <div className="lg:col-span-9 flex flex-col h-full">
            <div 
              onTouchStart={handleTouchStart}
              onTouchEnd={handleTouchEnd}
              className={`
                bg-white rounded-2xl shadow-sm border border-gray-200 p-6 sm:p-8 lg:p-10 flex-1 flex flex-col min-h-[60vh] transition-all duration-300 ease-out
                ${enterAnimationOn 
                  ? 'opacity-100 translate-x-0' 
                  : navDirection === 'next' 
                    ? 'opacity-0 translate-x-4' 
                    : 'opacity-0 -translate-x-4'
                }
              `}
            >
              {/* Question Meta */}
              <div className="flex items-center gap-3 mb-6">
                <span className="text-3xl font-bold text-gray-200 select-none">
                  {(currentQuestionIndex + 1).toString().padStart(2, '0')}
                </span>
                <span className={`px-3 py-1 rounded-full text-sm font-medium border ${getTagColor(currentQuestion.type)}`}>
                  {questionTypeMap[currentQuestion.type]}
                </span>
                <span className="text-gray-400 text-sm">
                  / 共 {questions.length} 题
                </span>
                {currentQuestion.category && (
                  <span className="ml-auto px-2 py-1 bg-gray-50 text-gray-500 text-xs rounded border border-gray-100">
                    {currentQuestion.category}
                  </span>
                )}
              </div>

              {/* Question Content */}
              <h2
                ref={questionHeadingRef}
                tabIndex={-1}
                className="text-xl sm:text-2xl font-medium text-gray-900 leading-relaxed mb-8 outline-none"
              >
                {currentQuestion.content}
              </h2>

              {/* Options */}
              <div className="flex-1">
                <OptionList 
                  type={currentQuestion.type}
                  options={currentQuestion.options}
                  value={answers[currentQuestion.id]}
                  onChange={(val) => handleAnswerChange(currentQuestion.id, val)}
                />
              </div>

              {/* Desktop Navigation Buttons */}
              <div className="hidden lg:flex items-center justify-between mt-12 pt-8 border-t border-gray-100">
                <Button 
                  size="large"
                  onClick={handlePrevious}
                  disabled={currentQuestionIndex === 0}
                  className="px-8 h-12 text-base rounded-xl"
                >
                  上一题
                </Button>
                
                {currentQuestionIndex === questions.length - 1 ? (
                   <Button 
                    type="primary"
                    size="large"
                    onClick={() => handleSubmit()}
                    loading={submitting}
                    className="px-8 h-12 text-base rounded-xl bg-[#008C8C] hover:bg-[#00796B]"
                  >
                    提交试卷
                  </Button>
                ) : (
                  <Button 
                    type="primary"
                    size="large"
                    onClick={handleNext}
                    className="px-8 h-12 text-base rounded-xl bg-[#008C8C] hover:bg-[#00796B]"
                  >
                    下一题
                  </Button>
                )}
              </div>
            </div>
          </div>

          {/* Right Column: Sidebar (Desktop Only) */}
          <div className="hidden lg:flex lg:col-span-3 flex-col gap-6 sticky top-24">
            {/* User Info Card */}
            <div className="bg-white rounded-2xl shadow-sm border border-gray-200 p-6">
              <div className="flex items-center gap-4 mb-4">
                <div className="w-12 h-12 rounded-full bg-[#E0F7FA] flex items-center justify-center text-[#008C8C] text-xl font-bold">
                  {user?.name?.[0] || 'U'}
                </div>
                <div>
                  <div className="font-bold text-gray-900">{user?.name}</div>
                  <div className="text-xs text-gray-500">考生</div>
                </div>
              </div>
              <div className="flex justify-between items-center text-sm text-gray-600 bg-gray-50 px-4 py-2 rounded-lg">
                <span>已完成</span>
                <span className="font-bold text-[#008C8C]">{answeredCount} / {questions.length}</span>
              </div>
            </div>

            {/* Answer Sheet Card */}
            <div className="bg-white rounded-2xl shadow-sm border border-gray-200 p-6 flex-1">
              <h3 className="font-bold text-gray-900 mb-4">答题卡</h3>
              <div className="grid grid-cols-5 gap-2 max-h-[400px] overflow-y-auto pr-2 custom-scrollbar">
                {questions.map((q, idx) => {
                  const isCurrent = idx === currentQuestionIndex;
                  const isAnswered = !!answers[q.id];
                  let className = 'h-10 w-10 rounded-lg flex items-center justify-center text-sm font-medium border transition-all duration-200 ';
                  
                  if (isCurrent) {
                    className += 'border-[#008C8C] bg-[#E0F7FA] text-[#006064] ring-2 ring-[#008C8C] ring-offset-2';
                  } else if (isAnswered) {
                    className += 'border-[#008C8C] bg-[#008C8C] text-white';
                  } else {
                    className += 'border-gray-200 text-gray-600 bg-gray-50 hover:border-[#008C8C] hover:bg-white';
                  }

                  return (
                    <button
                      key={q.id}
                      onClick={() => handleJumpTo(idx)}
                      className={className}
                    >
                      {idx + 1}
                    </button>
                  );
                })}
              </div>
              
              <div className="mt-6 pt-6 border-t border-gray-100 grid grid-cols-2 gap-3 text-xs text-gray-500">
                 <div className="flex items-center gap-2">
                   <div className="w-3 h-3 rounded bg-[#008C8C]"></div>
                   <span>已作答</span>
                 </div>
                 <div className="flex items-center gap-2">
                   <div className="w-3 h-3 rounded bg-[#E0F7FA] border border-[#008C8C]"></div>
                   <span>当前</span>
                 </div>
                 <div className="flex items-center gap-2">
                   <div className="w-3 h-3 rounded bg-gray-50 border border-gray-200"></div>
                   <span>未作答</span>
                 </div>
              </div>

              <Button 
                type="primary" 
                block 
                size="large"
                className="mt-6 bg-[#008C8C] hover:bg-[#00796B] h-12 rounded-xl font-medium"
                onClick={() => handleSubmit()}
              >
                交卷
              </Button>
            </div>
          </div>
        </div>
      </main>

      {/* Mobile Footer */}
      <div className="lg:hidden">
        <QuizFooter 
          current={currentQuestionIndex}
          total={questions.length}
          onPrev={handlePrevious}
          onNext={handleNext}
          onSubmit={() => handleSubmit()}
          onOpenSheet={() => setAnswerSheetOpen(true)}
          answeredCount={answeredCount}
        />
      </div>

      {/* Mobile Answer Sheet Modal */}
      <Modal
        title="答题卡"
        open={answerSheetOpen}
        onCancel={() => setAnswerSheetOpen(false)}
        footer={null}
        centered
        width={340}
        destroyOnClose
        className="mobile-sheet-modal"
      >
        <div className="flex items-center justify-between mb-4">
          <div className="text-sm text-gray-700">
            进度：<span className="text-[#008C8C] font-bold">{answeredCount}</span> / {questions.length}
          </div>
          <Button
            type="primary"
            onClick={handleJumpToFirstUnanswered}
            className="bg-[#008C8C] hover:bg-[#00796B] text-xs h-8 px-4 rounded-lg"
          >
            跳转未答
          </Button>
        </div>

        <div className="grid grid-cols-5 gap-3">
          {questions.map((q, idx) => {
            const isCurrent = idx === currentQuestionIndex;
            const isAnswered = !!answers[q.id];

            let className = 'h-10 w-10 rounded-lg flex items-center justify-center text-sm font-medium border transition-colors ';
            if (isCurrent) {
              className += 'border-[#008C8C] bg-[#E0F7FA] text-[#006064]';
            } else if (isAnswered) {
              className += 'border-[#008C8C] bg-[#008C8C] text-white';
            } else {
              className += 'border-gray-200 text-gray-600 bg-white hover:border-[#008C8C]';
            }

            return (
              <button
                key={q.id}
                type="button"
                className={className}
                onClick={() => {
                  setAnswerSheetOpen(false);
                  handleJumpTo(idx);
                }}
                aria-label={`跳转到第 ${idx + 1} 题`}
              >
                {idx + 1}
              </button>
            );
          })}
        </div>
      </Modal>
    </div>
  );
};

export default QuizPage;

const QUIZ_PROGRESS_ACTIVE_PREFIX = 'quiz_progress_active_v1:';
const QUIZ_PROGRESS_PREFIX = 'quiz_progress_v1:';

type QuizProgressV1 = {
  questions: Question[];
  answers: Record<string, string | string[]>;
  currentQuestionIndex: number;
  timeLeftSeconds: number;
  timeLimitMinutes: number;
  subjectId: string;
  taskId: string;
  savedAt: string;
};

const buildProgressKey = (userId: string, subjectId: string, taskId: string) => {
  const scope = taskId ? `task:${taskId}` : `subject:${subjectId || 'none'}`;
  return `${QUIZ_PROGRESS_PREFIX}${userId}:${scope}`;
};

const setActiveProgress = (userId: string, progressKey: string) => {
  localStorage.setItem(`${QUIZ_PROGRESS_ACTIVE_PREFIX}${userId}`, progressKey);
};

const removeActiveProgress = (userId: string) => {
  localStorage.removeItem(`${QUIZ_PROGRESS_ACTIVE_PREFIX}${userId}`);
};

const getActiveProgressKey = (userId: string) => {
  return localStorage.getItem(`${QUIZ_PROGRESS_ACTIVE_PREFIX}${userId}`) || '';
};

const saveProgress = (progressKey: string, input: Omit<QuizProgressV1, 'savedAt'>) => {
  const payload: QuizProgressV1 = {
    ...input,
    savedAt: new Date().toISOString(),
  };
  localStorage.setItem(progressKey, JSON.stringify(payload));
};

const restoreActiveProgress = (userId: string): QuizProgressV1 | null => {
  const progressKey = getActiveProgressKey(userId);
  if (!progressKey) return null;

  const raw = localStorage.getItem(progressKey);
  if (!raw) return null;

  try {
    const parsed = JSON.parse(raw) as QuizProgressV1;
    if (!parsed || !Array.isArray(parsed.questions)) return null;
    if (!parsed.answers || typeof parsed.answers !== 'object') return null;
    if (typeof parsed.currentQuestionIndex !== 'number') return null;
    if (typeof parsed.timeLeftSeconds !== 'number') return null;
    if (typeof parsed.timeLimitMinutes !== 'number') return null;
    if (typeof parsed.subjectId !== 'string') return null;
    if (typeof parsed.taskId !== 'string') return null;
    return parsed;
  } catch {
    return null;
  }
};

const clearActiveProgress = (userId: string) => {
  const progressKey = getActiveProgressKey(userId);
  if (progressKey) {
    localStorage.removeItem(progressKey);
  }
  removeActiveProgress(userId);
};
