import { randomUUID } from "node:crypto";
import { eq } from "drizzle-orm";
import { Injectable } from "@nestjs/common";
import { db } from "../config/database.js";
import { grades } from "./grades.schema.js";
import { outboxRepository } from "../shared/outbox/outbox.repository.js";
import type { Grade, NewGrade } from "./grades.schema.js";
import {
  NotFoundError,
  ValidationError,
} from "../shared/errors/application-error.js";

export interface RecordGradeInput {
  studentId: string;
  examId?: string;
  homeworkId?: string;
  score: string;
  feedback?: string;
  gradedBy: string;
}

@Injectable()
export class GradesService {
  async recordGrade(input: RecordGradeInput): Promise<{ id: string }> {
    if (!input.studentId || !input.score || !input.gradedBy) {
      throw new ValidationError("studentId, score, gradedBy are required");
    }
    if (!input.examId && !input.homeworkId) {
      throw new ValidationError("Either examId or homeworkId must be provided");
    }

    const id = randomUUID();
    const record: NewGrade = {
      id,
      studentId: input.studentId,
      examId: input.examId,
      homeworkId: input.homeworkId,
      score: input.score,
      feedback: input.feedback,
      gradedBy: input.gradedBy,
    };

    await db.transaction(async (tx) => {
      await tx.insert(grades).values(record);
      await outboxRepository.create(
        {
          id: randomUUID(),
          aggregateId: id,
          aggregateType: "grade",
          eventType: "grade.recorded",
          payload: JSON.stringify({
            id,
            studentId: input.studentId,
            score: input.score,
            examId: input.examId,
            homeworkId: input.homeworkId,
          }),
          status: "pending",
        },
        tx,
      );
    });

    return { id };
  }

  async getGrade(id: string): Promise<Grade> {
    const [record] = await db
      .select()
      .from(grades)
      .where(eq(grades.id, id))
      .limit(1);
    if (!record) {
      throw new NotFoundError(`Grade ${id} not found`);
    }
    return record;
  }

  async listByStudent(studentId: string): Promise<Grade[]> {
    return db.select().from(grades).where(eq(grades.studentId, studentId));
  }

  async listByExam(examId: string): Promise<Grade[]> {
    return db.select().from(grades).where(eq(grades.examId, examId));
  }

  async listByHomework(homeworkId: string): Promise<Grade[]> {
    return db.select().from(grades).where(eq(grades.homeworkId, homeworkId));
  }
}
