import { randomUUID } from "node:crypto";
import { Injectable } from "@nestjs/common";
import { schedulingRepository } from "./scheduling.repository.js";
import { detectConflict, type ScheduleSlot } from "./schedule-conflict.js";
import {
  NotFoundError,
  ValidationError,
  ApplicationError,
  CoreEduErrorCode,
} from "../shared/errors/application-error.js";
import type { Course, Schedule } from "./scheduling.schema.js";

export interface CreateCourseInput {
  classId: string;
  subjectId: string;
  teacherId: string;
  schoolId: string;
  name: string;
  startDate: Date | string;
  endDate: Date | string;
}

export interface CreateScheduleInput {
  courseId: string;
  lessonId?: string;
  teacherId: string;
  classId: string;
  roomId?: string;
  startTime: Date | string;
  endTime: Date | string;
  schoolId: string;
}

@Injectable()
export class SchedulingService {
  // Course operations
  async createCourse(input: CreateCourseInput): Promise<{ id: string }> {
    if (!input.classId || !input.subjectId || !input.teacherId || !input.name) {
      throw new ValidationError(
        "classId, subjectId, teacherId, name are required",
      );
    }
    const id = randomUUID();
    await schedulingRepository.createCourse({
      id,
      classId: input.classId,
      subjectId: input.subjectId,
      teacherId: input.teacherId,
      schoolId: input.schoolId,
      name: input.name,
      startDate:
        input.startDate instanceof Date
          ? input.startDate
          : new Date(input.startDate),
      endDate:
        input.endDate instanceof Date ? input.endDate : new Date(input.endDate),
    });
    return { id };
  }

  async getCourse(id: string): Promise<Course> {
    const course = await schedulingRepository.findCourseById(id);
    if (!course) {
      throw new NotFoundError(`Course ${id} not found`);
    }
    return course;
  }

  async listCoursesByClass(classId: string): Promise<Course[]> {
    return schedulingRepository.findCoursesByClassId(classId);
  }

  async listCoursesByTeacher(teacherId: string): Promise<Course[]> {
    return schedulingRepository.findCoursesByTeacherId(teacherId);
  }

  // Schedule operations
  async createSchedule(input: CreateScheduleInput): Promise<{ id: string }> {
    if (
      !input.courseId ||
      !input.teacherId ||
      !input.classId ||
      !input.schoolId
    ) {
      throw new ValidationError(
        "courseId, teacherId, classId, schoolId are required",
      );
    }

    const startTime =
      input.startTime instanceof Date
        ? input.startTime
        : new Date(input.startTime);
    const endTime =
      input.endTime instanceof Date ? input.endTime : new Date(input.endTime);

    if (startTime >= endTime) {
      throw new ValidationError("startTime must be before endTime");
    }

    // Conflict detection
    const overlapping = await schedulingRepository.findOverlappingSchedules(
      input.teacherId,
      input.classId,
      startTime,
      endTime,
    );

    const newSlot: Omit<ScheduleSlot, "id"> = {
      teacherId: input.teacherId,
      classId: input.classId,
      startTime,
      endTime,
      roomId: input.roomId,
    };

    const conflict = detectConflict(newSlot, overlapping);
    if (conflict.hasConflict && conflict.conflictingSlot) {
      throw new ApplicationError(
        CoreEduErrorCode.SCHEDULE_CONFLICT,
        `Schedule conflict detected (${conflict.conflictType}): overlaps with existing schedule ${conflict.conflictingSlot.id}`,
        409,
        {
          conflictType: conflict.conflictType,
          conflictingScheduleId: conflict.conflictingSlot.id,
        },
      );
    }

    const id = randomUUID();
    await schedulingRepository.createSchedule({
      id,
      courseId: input.courseId,
      lessonId: input.lessonId,
      teacherId: input.teacherId,
      classId: input.classId,
      roomId: input.roomId,
      startTime,
      endTime,
      schoolId: input.schoolId,
    });
    return { id };
  }

  async getSchedule(id: string): Promise<Schedule> {
    const schedule = await schedulingRepository.findScheduleById(id);
    if (!schedule) {
      throw new NotFoundError(`Schedule ${id} not found`);
    }
    return schedule;
  }

  async listSchedulesByCourse(courseId: string): Promise<Schedule[]> {
    return schedulingRepository.findSchedulesByCourseId(courseId);
  }

  async listSchedulesByTeacher(teacherId: string): Promise<Schedule[]> {
    return schedulingRepository.findSchedulesByTeacherId(teacherId);
  }

  async listSchedulesByClass(classId: string): Promise<Schedule[]> {
    return schedulingRepository.findSchedulesByClassId(classId);
  }
}
