import Quiz from "../models/quiz";
import QuizResult from "../models/quizResult";
import User from "../models/users";
import { IQuestionDocument } from "../models/question";
import { notifyQuizResult } from "./notificationService";
// Removed old reward logic (migrating to Rewards Store)

const err = (msg: string, code = 400): Error & { statusCode: number } =>
  Object.assign(new Error(msg), { statusCode: code });

interface AnswerInput {
  question_id: string;
  selected_option: string;
}

// ─── User: Submit Quiz ────────────────────────────────────────────────────────

import QuizAttempt from "../models/quizAttempt";

export const submitQuiz = async (
  quizId: string,
  userId: string,
  answers: AnswerInput[],
) => {
  const quiz = await Quiz.findById(quizId).populate(
    "questions",
    "question_text correct_option points",
  );
  if (!quiz) throw err("Quiz not found", 404);
  if (quiz.status !== "active") throw err("This quiz is not active");

  const now = new Date();
  if (now < quiz.start_datetime) throw err("This quiz has not started yet");
  if (now > quiz.end_datetime) throw err("This quiz has already ended");

  // Enforce attempt-based timer and status
  const attempt = await QuizAttempt.findOne({ quiz: quizId, user: userId });
  if (!attempt)
    throw err("No active quiz attempt found. Please start the quiz first.");

  // Check attempt status
  if (attempt.status === "submitted")
    throw err("You have already submitted this quiz");
  if (attempt.status === "expired") throw err("This quiz attempt has expired");

  // Check timer with 2-second grace period for network delay
  const GRACE_MS = 2000;
  if (now.getTime() > attempt.expiresAt.getTime() + GRACE_MS) {
    // Auto-expire attempt
    attempt.status = "expired";
    await attempt.save();
    throw err("Time is up! This quiz attempt has expired");
  }
  if (attempt.status !== "in_progress")
    throw err("Quiz attempt is not in progress");

  // Only allow one submission per attempt
  const alreadySubmitted = await QuizResult.findOne({
    quiz: quizId,
    user: userId,
  });
  if (alreadySubmitted) {
    // Defensive: mark attempt as submitted if not already
    if (attempt.status === "in_progress") {
      attempt.status = "submitted";
      await attempt.save();
    }
    throw err("You have already submitted this quiz");
  }

  const questions = quiz.questions as unknown as IQuestionDocument[];
  // Allow partial/incomplete answers: no length check
  const questionMap = new Map(questions.map((q) => [q._id.toString(), q]));
  const total_points = questions.reduce((sum, q) => sum + q.points, 0);

  const answersResult = [];
  let score = 0;

  for (const { question_id, selected_option } of answers) {
    const question = questionMap.get(question_id);
    if (!question)
      throw err(`Question ${question_id} does not belong to this quiz`);
    const is_correct =
      selected_option.trim() === question.correct_option.trim();
    const points_earned = is_correct ? question.points : 0;
    score += points_earned;
    answersResult.push({
      question: question._id,
      selected_option: selected_option.trim(),
      is_correct,
      points_earned,
    });
  }

  const result = await QuizResult.create({
    quiz: quizId,
    user: userId,
    answers: answersResult,
    score,
    total_points,
  });

  // Mark attempt as submitted
  attempt.status = "submitted";
  await attempt.save();

  // Add quiz points to user's earned points (migrating to Rewards Store later)
  if (score > 0) {
    await User.findByIdAndUpdate(userId, {
      $inc: { quiz_points: score, earned_points: score },
    });
  }

  await notifyQuizResult(userId, quizId, quiz.title, score, total_points);

  // Add totalQuestions to the result for response
  const totalQuestions = questions.length;
  return { ...result.toObject(), totalQuestions };
};

// ...existing code...

export const getMyResults = async (userId: string) => {
  const results = await QuizResult.find({ user: userId })
    .populate("quiz", "title questions")
    .select("quiz answers score total_points submitted_at")
    .sort({ submitted_at: -1 })
    .lean();

  return results
    .filter((r) => r.quiz !== null)
    .map((r) => {
      const quiz = r.quiz as unknown as {
        _id: unknown;
        title: string;
        questions?: any[];
      };
      const total_questions = Array.isArray(quiz.questions)
        ? quiz.questions.length
        : r.answers.length;
      const correct_answers = r.answers.filter((a) => a.is_correct).length;
      return {
        _id: r._id,
        quiz_id: quiz._id,
        quiz_title: quiz.title,
        correct_answers,
        total_questions,
        scored_points: r.score,
        total_points: r.total_points,
        percentage:
          total_questions > 0
            ? Math.round((correct_answers / total_questions) * 100)
            : 0,
        submitted_at: r.submitted_at,
      };
    });
};

// ─── Admin: Get Results ───────────────────────────────────────────────────────

export const getQuizResults = async (quizId: string) => {
  const quiz = await Quiz.findById(quizId, "title");
  if (!quiz) throw err("Quiz not found", 404);
  const results = await QuizResult.find({ quiz: quizId })
    .populate("user", "name email")
    .populate("answers.question", "question_text correct_option points")
    .sort({ score: -1 });
  return { quiz, results };
};

// ─── Admin: Get Single User Submission ───────────────────────────────────────

export const getSubmission = async (quizId: string, userId: string) => {
  const submission = await QuizResult.findOne({ quiz: quizId, user: userId })
    .populate("user", "name email")
    .populate(
      "answers.question",
      "question_text option_a option_b option_c option_d correct_option points",
    );

  if (!submission) throw err("Submission not found", 404);
  return submission;
};

// ─── Admin: Export Results as CSV ─────────────────────────────────────────────

export const exportResultsCSV = async (quizId: string) => {
  const { quiz, results } = await getQuizResults(quizId);
  if (results.length === 0) throw err("No results found for this quiz", 404);

  const rows: (string | number)[][] = [
    ["Name", "Email", "Score", "Total Points", "Percentage", "Submitted At"],
  ];

  for (const r of results) {
    const user = r.user as unknown as { name?: string; email?: string } | null;
    const percentage = ((r.score / r.total_points) * 100).toFixed(2);
    rows.push([
      user?.name ?? "N/A",
      user?.email ?? "N/A",
      r.score,
      r.total_points,
      `${percentage}%`,
      new Date(r.submitted_at).toISOString(),
    ]);
  }

  const csv = rows
    .map((row) => row.map((cell) => `"${cell}"`).join(","))
    .join("\n");
  const filename = `${quiz.title.replace(/\s+/g, "_")}_results.csv`;
  return { csv, filename };
};
