import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';
import { Question } from './question.entity';
import { SubmitQuizDto } from './dto/submit-quiz.dto';

export interface QuestionResult {
  questionId: number;
  correct: boolean;
  score: number;
  maxScore: number;
  correctOptionIds: number[];
  explanation?: string;
}

export interface QuizResult {
  activityId: number;
  score: number;
  maxScore: number;
  percentage: number;
  correctCount: number;
  totalQuestions: number;
  results: QuestionResult[];
}

/**
 * Grading happens server-side so the answer key never ships to the device.
 * A question is correct only when the selected set matches the correct set
 * exactly - this covers single-answer, multiple-answer and true/false alike.
 */
@Injectable()
export class QuizService {
  constructor(
    @InjectRepository(Question)
    private readonly questionsRepo: Repository<Question>,
  ) {}

  async grade(dto: SubmitQuizDto): Promise<QuizResult> {
    const questionIds = dto.answers.map((a) => a.questionId);
    const questions = await this.questionsRepo.find({
      where: { id: In(questionIds), activityId: dto.activityId },
      relations: ['options'],
    });

    if (questions.length === 0) {
      throw new NotFoundException('No questions found for this activity');
    }

    const byId = new Map(questions.map((q) => [Number(q.id), q]));
    const results: QuestionResult[] = [];
    let score = 0;
    let maxScore = 0;

    for (const answer of dto.answers) {
      const question = byId.get(Number(answer.questionId));
      if (!question) continue;

      const correctOptionIds = (question.options || [])
        .filter((o) => !!o.isCorrect)
        .map((o) => Number(o.id));

      const selected = new Set(answer.optionIds.map(Number));
      const correct =
        correctOptionIds.length === selected.size &&
        correctOptionIds.every((id) => selected.has(id));

      const questionScore = correct ? question.score : 0;
      score += questionScore;
      maxScore += question.score;

      results.push({
        questionId: Number(question.id),
        correct,
        score: questionScore,
        maxScore: question.score,
        correctOptionIds,
        explanation: question.explanation,
      });
    }

    const correctCount = results.filter((r) => r.correct).length;

    return {
      activityId: dto.activityId,
      score,
      maxScore,
      percentage: maxScore > 0 ? Math.round((score / maxScore) * 100) : 0,
      correctCount,
      totalQuestions: results.length,
      results,
    };
  }
}
