import {
  Injectable,
  BadRequestException,
  NotFoundException,
  Logger,
} from '@nestjs/common';
import { PrismaService } from '../../database/prisma.service';
import { RewardEngine } from '../time-wallet/reward-engine.service';
import { AntiFarmingService } from '../time-wallet/anti-farming.service';
import { AssignmentStatus, TransactionType } from '@prisma/client';
import { v4 as uuidv4 } from 'uuid';

export interface SubmitAnswerDto {
  questionId: string;
  selectedOptionIds?: string[];
  textAnswer?: string;
  responseTimeSec?: number;
}

export interface StartAttemptResult {
  attemptId: string;
  questions: QuestionForChild[];
}

export interface QuestionForChild {
  id: string;
  type: string;
  stem: string;
  options: { id: string; text: string }[];
  timeEstimateSec: number;
  sortOrder: number;
}

export interface AttemptResult {
  attemptId: string;
  score: number;
  totalQuestions: number;
  correctAnswers: number;
  passed: boolean;
  rewardMinutes: number;
  feedback: QuestionFeedback[];
  nextRecommendation?: string;
}

export interface QuestionFeedback {
  questionId: string;
  isCorrect: boolean;
  explanation?: string;
  correctOptionIds: string[];
}

@Injectable()
export class QuizEngineService {
  private readonly logger = new Logger(QuizEngineService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly rewardEngine: RewardEngine,
    private readonly antiFarming: AntiFarmingService,
  ) {}

  async startAttempt(childId: string, assignmentId: string): Promise<StartAttemptResult> {
    const assignment = await this.prisma.missionAssignment.findFirst({
      where: { id: assignmentId, childId },
      include: {
        mission: {
          include: {
            lessons: {
              include: { questions: { include: { options: true } } },
              orderBy: { sortOrder: 'asc' },
            },
          },
        },
      },
    });

    if (!assignment) throw new NotFoundException('Missão não encontrada');

    if (assignment.status === AssignmentStatus.COMPLETED) {
      throw new BadRequestException('Esta missão já foi completada');
    }

    if (assignment.attemptsUsed >= assignment.mission.maxAttempts) {
      throw new BadRequestException('Número máximo de tentativas atingido');
    }

    // Criar tentativa
    const attempt = await this.prisma.attempt.create({
      data: {
        assignmentId,
        score: 0,
        totalQuestions: 0,
        correctAnswers: 0,
      },
    });

    // Atualizar status da missão
    await this.prisma.missionAssignment.update({
      where: { id: assignmentId },
      data: {
        status: AssignmentStatus.IN_PROGRESS,
        attemptsUsed: { increment: 1 },
      },
    });

    // Retornar questões sem as respostas corretas
    const questions: QuestionForChild[] = assignment.mission.lessons
      .flatMap((l) => l.questions)
      .map((q) => ({
        id: q.id,
        type: q.type,
        stem: q.stem,
        options: q.options.map((o) => ({ id: o.id, text: o.text })),
        timeEstimateSec: q.timeEstimateSec,
        sortOrder: q.sortOrder,
      }));

    return { attemptId: attempt.id, questions };
  }

  async submitAttempt(
    childId: string,
    attemptId: string,
    answers: SubmitAnswerDto[],
    durationSec: number,
  ): Promise<AttemptResult> {
    const attempt = await this.prisma.attempt.findFirst({
      where: { id: attemptId, assignment: { childId } },
      include: {
        assignment: {
          include: { mission: true },
        },
      },
    });

    if (!attempt) throw new NotFoundException('Tentativa não encontrada');
    if (attempt.completedAt) throw new BadRequestException('Tentativa já finalizada');

    // Buscar questões com respostas corretas
    const questions = await this.prisma.question.findMany({
      where: { id: { in: answers.map((a) => a.questionId) } },
      include: { options: true, skills: { include: { skill: true } } },
    });

    const feedback: QuestionFeedback[] = [];
    let correctCount = 0;

    // Salvar e avaliar respostas
    for (const answer of answers) {
      const question = questions.find((q) => q.id === answer.questionId);
      if (!question) continue;

      const correctOptionIds = question.options
        .filter((o) => o.isCorrect)
        .map((o) => o.id);

      let isCorrect = false;

      if (answer.selectedOptionIds) {
        const selected = new Set(answer.selectedOptionIds);
        const correct = new Set(correctOptionIds);
        isCorrect =
          selected.size === correct.size &&
          [...selected].every((id) => correct.has(id));
      } else if (answer.textAnswer) {
        // Para questões abertas: validação simples (pode ser expandido)
        isCorrect = correctOptionIds.length === 0; // sem opções = sempre correto
      }

      if (isCorrect) correctCount++;

      await this.prisma.answer.create({
        data: {
          attemptId,
          questionId: answer.questionId,
          selectedOptions: answer.selectedOptionIds || [],
          textAnswer: answer.textAnswer,
          isCorrect,
          responseTimeSec: answer.responseTimeSec,
        },
      });

      feedback.push({
        questionId: answer.questionId,
        isCorrect,
        explanation: question.explanation || undefined,
        correctOptionIds,
      });
    }

    const totalQuestions = answers.length;
    const score = totalQuestions > 0 ? correctCount / totalQuestions : 0;
    const passed = score >= attempt.assignment.mission.minPassRate;

    // Completar tentativa
    await this.prisma.attempt.update({
      where: { id: attemptId },
      data: {
        score,
        totalQuestions,
        correctAnswers: correctCount,
        completedAt: new Date(),
        durationSec,
      },
    });

    let rewardMinutes = 0;

    if (passed) {
      // Verificar anti-farming
      const farmCheck = await this.antiFarming.checkMissionAttempt(
        childId,
        attempt.assignment.missionId,
        durationSec,
      );

      if (farmCheck.allowed && !attempt.assignment.rewardCredited) {
        rewardMinutes = await this.antiFarming.calculateEffectiveReward(
          childId,
          attempt.assignment.missionId,
          attempt.assignment.mission.rewardMinutes,
          score,
          durationSec,
        );

        // Creditar recompensa
        await this.rewardEngine.credit({
          childId,
          minutes: rewardMinutes,
          type: TransactionType.MISSION_REWARD,
          description: `Missão concluída: ${attempt.assignment.mission.title}`,
          referenceId: attempt.assignment.id,
          idempotencyKey: `mission-reward-${attempt.assignment.id}`,
        });

        // Marcar como completado e recompensa creditada
        await this.prisma.missionAssignment.update({
          where: { id: attempt.assignment.id },
          data: {
            status: AssignmentStatus.COMPLETED,
            bestScore: score,
            completedAt: new Date(),
            rewardCredited: true,
          },
        });

        // Atualizar progresso de habilidades
        await this.updateSkillProgress(childId, questions, correctCount, totalQuestions);
      } else if (!farmCheck.allowed) {
        this.logger.warn(`[QuizEngine] Anti-farming bloqueou recompensa: ${farmCheck.reason}`);
      }
    }

    return {
      attemptId,
      score,
      totalQuestions,
      correctAnswers: correctCount,
      passed,
      rewardMinutes,
      feedback,
    };
  }

  private async updateSkillProgress(
    childId: string,
    questions: any[],
    correctCount: number,
    totalCount: number,
  ) {
    const skillMap = new Map<string, { correct: number; total: number }>();

    for (const question of questions) {
      for (const qs of question.skills || []) {
        const skillId = qs.skillId;
        if (!skillMap.has(skillId)) skillMap.set(skillId, { correct: 0, total: 0 });
        const entry = skillMap.get(skillId)!;
        entry.total++;
      }
    }

    for (const [skillId, data] of skillMap.entries()) {
      const existing = await this.prisma.skillProgress.findUnique({
        where: { childId_skillId: { childId, skillId } },
      });

      const newTotal = (existing?.totalAnswers || 0) + data.total;
      const newCorrect = (existing?.correctAnswers || 0) + correctCount;
      const domain = newTotal > 0 ? (newCorrect / newTotal) * 100 : 0;

      await this.prisma.skillProgress.upsert({
        where: { childId_skillId: { childId, skillId } },
        create: {
          childId,
          skillId,
          domainPercent: domain,
          totalAnswers: data.total,
          correctAnswers: correctCount,
          lastPracticed: new Date(),
        },
        update: {
          domainPercent: domain,
          totalAnswers: newTotal,
          correctAnswers: newCorrect,
          lastPracticed: new Date(),
        },
      });
    }
  }
}
