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

export interface CreateExamInput {
  classId: string;
  title: string;
  description?: string;
  examDate: Date | string;
  duration: string;
  totalScore: string;
  createdBy: string;
}

export interface UpdateExamInput {
  title?: string;
  description?: string;
  examDate?: Date;
  duration?: string;
  totalScore?: string;
  status?: string;
}

@Injectable()
export class ExamsService {
  async createExam(input: CreateExamInput): Promise<{ id: string }> {
    if (!input.classId || !input.title || !input.createdBy) {
      throw new ValidationError("classId, title, createdBy are required");
    }

    const id = randomUUID();
    const exam: NewExam = {
      id,
      classId: input.classId,
      title: input.title,
      description: input.description,
      // Drizzle datetime 列需要 Date 对象（调用 toISOString）。HTTP 请求体里的
      // examDate 是 ISO 字符串，这里统一转成 Date，避免 "toISOString is not a function"。
      examDate:
        input.examDate instanceof Date
          ? input.examDate
          : new Date(input.examDate),
      duration: input.duration,
      totalScore: input.totalScore,
      status: "draft",
      createdBy: input.createdBy,
    };

    await db.transaction(async (tx) => {
      await tx.insert(exams).values(exam);
      await outboxRepository.create(
        {
          id: randomUUID(),
          aggregateId: id,
          aggregateType: "exam",
          eventType: "exam.created",
          payload: JSON.stringify({
            id,
            classId: input.classId,
            title: input.title,
          }),
          status: "pending",
        },
        tx,
      );
    });

    return { id };
  }

  async getExam(id: string): Promise<Exam> {
    const exam = await examsRepository.findById(id);
    if (!exam) {
      throw new NotFoundError(`Exam ${id} not found`);
    }
    return exam;
  }

  async listExamsByClass(classId: string): Promise<Exam[]> {
    return examsRepository.findByClassId(classId);
  }

  async updateExam(id: string, data: UpdateExamInput): Promise<void> {
    const existing = await examsRepository.findById(id);
    if (!existing) {
      throw new NotFoundError(`Exam ${id} not found`);
    }

    await db.transaction(async (tx) => {
      await tx.update(exams).set(data).where(eq(exams.id, id));
      await outboxRepository.create(
        {
          id: randomUUID(),
          aggregateId: id,
          aggregateType: "exam",
          eventType: "exam.updated",
          payload: JSON.stringify({ id, changes: data }),
          status: "pending",
        },
        tx,
      );
    });
  }

  async deleteExam(id: string): Promise<void> {
    const existing = await examsRepository.findById(id);
    if (!existing) {
      throw new NotFoundError(`Exam ${id} not found`);
    }

    await db.transaction(async (tx) => {
      await tx.delete(exams).where(eq(exams.id, id));
      await outboxRepository.create(
        {
          id: randomUUID(),
          aggregateId: id,
          aggregateType: "exam",
          eventType: "exam.deleted",
          payload: JSON.stringify({ id }),
          status: "pending",
        },
        tx,
      );
    });
  }
}
