"use client";

import React, {
  createContext,
  useContext,
  useState,
  useEffect,
  ReactNode,
} from "react";

import { Exam, ExamAnswer, ExamAttempt, ExamContextType } from "@/types/exam";
import { getFromStorage, removeFromStorage, setToStorage } from "@/lib/utils";

const ExamContext = createContext<ExamContextType | undefined>(undefined);

const STORAGE_KEYS = {
  CURRENT_EXAM: "current-exam",
  CURRENT_ATTEMPT: "current-attempt",
} as const;

export const ExamProvider: React.FC<{ children: ReactNode }> = ({
  children,
}) => {
  const [currentExam, setCurrentExamState] = useState<Exam | null>(null);
  const [currentAttempt, setCurrentAttemptState] = useState<ExamAttempt | null>(
    null
  );
  const [isHydrated, setIsHydrated] = useState(false);
  const [isInitialized, setIsInitialized] = useState(false);

  // Hydrate from session storage on mount
  useEffect(() => {
    const savedExam = getFromStorage<Exam>(STORAGE_KEYS.CURRENT_EXAM);
    const savedAttempt = getFromStorage<ExamAttempt>(
      STORAGE_KEYS.CURRENT_ATTEMPT
    );

    if (savedExam) {
      setCurrentExamState(savedExam);
    }

    if (savedAttempt) {
      // Convert date strings back to Date objects
      const hydratedAttempt = {
        ...savedAttempt,
        startTime: new Date(savedAttempt.startTime),
        endTime: savedAttempt.endTime
          ? new Date(savedAttempt.endTime)
          : undefined,
        answers: savedAttempt.answers.map((answer) => ({
          ...answer,
          timestamp: new Date(answer.timestamp),
        })),
      };
      setCurrentAttemptState(hydratedAttempt);
    }

    setIsHydrated(true);
  }, []);

  // Persist to session storage whenever state changes
  useEffect(() => {
    if (!isHydrated) return;

    if (currentExam) {
      setToStorage(STORAGE_KEYS.CURRENT_EXAM, currentExam);
    } else {
      removeFromStorage(STORAGE_KEYS.CURRENT_EXAM);
    }
  }, [currentExam, isHydrated]);

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

    if (currentAttempt) {
      setToStorage(STORAGE_KEYS.CURRENT_ATTEMPT, currentAttempt);
    } else {
      removeFromStorage(STORAGE_KEYS.CURRENT_ATTEMPT);
    }
  }, [currentAttempt, isHydrated]);

  const setCurrentExam = (exam: Exam) => {
    setCurrentExamState(exam);

    setCurrentAttemptState(null);
  };

  const startExam = () => {
    if (!currentExam) {
      throw new Error("No exam selected");
    }

    const attempt: ExamAttempt = {
      examId: currentExam.id,
      exam: currentExam,
      answers: [],
      startTime: new Date(),
    };

    setCurrentAttemptState(attempt);
    setIsInitialized(true);
  };

  const setAnswer = (questionId: string, answer: any) => {
    if (!currentAttempt) {
      throw new Error("No exam attempt started");
    }

    setCurrentAttemptState((prev) => {
      if (!prev) return null;

      const existingAnswerIndex = prev.answers.findIndex(
        (a) => a.questionId === questionId
      );

      const newAnswer: ExamAnswer = {
        questionId,
        answer,
        timestamp: new Date(),
      };

      let newAnswers: ExamAnswer[];
      if (existingAnswerIndex >= 0) {
        // Update existing answer
        newAnswers = [...prev.answers];
        newAnswers[existingAnswerIndex] = newAnswer;
      } else {
        // Add new answer
        newAnswers = [...prev.answers, newAnswer];
      }

      return {
        ...prev,
        answers: newAnswers,
      };
    });
  };

  const setApiResponse = (response: any) => {
    if (!currentAttempt) {
      throw new Error("No exam attempt started");
    }

    setCurrentAttemptState((prev) => {
      if (!prev) return null;
      return {
        ...prev,
        apiResponse: response,
      };
    });
  };

  const submitExam = (): ExamAttempt => {
    if (!currentAttempt) {
      throw new Error("No exam attempt to submit");
    }

    // Calculate score (simple example - you can customize this)
    const attemptQuestions = currentAttempt.exam.questions;
    const totalQuestions = attemptQuestions.length;
    const answeredQuestions = currentAttempt.answers.filter(
      (a) => a.questionId !== "__api_response__"
    ).length;
    const score = Math.round((answeredQuestions / totalQuestions) * 100);

    const completedAttempt: ExamAttempt = {
      ...currentAttempt,
      endTime: new Date(),
      score,
      totalQuestions,
      passed: currentAttempt.exam.passingScore
        ? score >= currentAttempt.exam.passingScore
        : undefined,
    };

    setCurrentAttemptState(completedAttempt);
    return completedAttempt;
  };

  const clearExam = (): void => {
    setCurrentExamState(null);
    setCurrentAttemptState(null);
  };

  const getAnswer = (questionId: string): any => {
    if (!currentAttempt) return undefined;

    const answer = currentAttempt.answers.find(
      (a) => a.questionId === questionId
    );
    return answer?.answer;
  };

  const getApiResponse = (): any => {
    return currentAttempt?.apiResponse;
  };

  const getProgress = (): number => {
    if (!currentAttempt || !currentAttempt.exam) return 0;

    const totalQuestions = currentAttempt.exam.questions.length;
    const answeredQuestions = currentAttempt.answers.filter(
      (a) => a.questionId !== "__api_response__"
    ).length;

    return totalQuestions > 0 ? (answeredQuestions / totalQuestions) * 100 : 0;
  };

  const isExamStarted = () => !!currentExam && !!currentAttempt;

  const isExamCompleted = (): boolean => {
    if (!isHydrated) return false; // ⛔ wait for hydration
    return currentAttempt !== null && currentAttempt.endTime !== undefined;
  };

  const contextValue: ExamContextType = {
    currentExam,
    currentAttempt,
    setCurrentExam,
    startExam,
    setAnswer,
    submitExam,
    clearExam,
    setApiResponse,
    getAnswer,
    getProgress,
    isExamStarted,
    isExamCompleted,
    getApiResponse,
    isHydrated,
    isInitialized,
  };

  return (
    <ExamContext.Provider value={contextValue}>{children}</ExamContext.Provider>
  );
};

export const useExam = (): ExamContextType => {
  const context = useContext(ExamContext);

  if (context === undefined) {
    throw new Error("useExam must be used within an ExamProvider");
  }

  return context;
};

// Hook for exam results (only when exam is completed)
export const useExamResults = (): ExamAttempt => {
  const { currentAttempt, isExamCompleted } = useExam();

  if (!isExamCompleted() || !currentAttempt) {
    throw new Error("No completed exam attempt found");
  }

  return currentAttempt;
};
