import { eq, and, lte, gte } from "drizzle-orm";
import { db } from "../config/database.js";
import {
  courses,
  schedules,
  type Course,
  type NewCourse,
  type Schedule,
  type NewSchedule,
} from "./scheduling.schema.js";

export class SchedulingRepository {
  // Courses
  async findCourseById(id: string): Promise<Course | undefined> {
    const [result] = await db
      .select()
      .from(courses)
      .where(eq(courses.id, id))
      .limit(1);
    return result;
  }

  async findCoursesByClassId(classId: string): Promise<Course[]> {
    return db.select().from(courses).where(eq(courses.classId, classId));
  }

  async findCoursesByTeacherId(teacherId: string): Promise<Course[]> {
    return db.select().from(courses).where(eq(courses.teacherId, teacherId));
  }

  async createCourse(course: NewCourse): Promise<void> {
    await db.insert(courses).values(course);
  }

  // Schedules
  async findScheduleById(id: string): Promise<Schedule | undefined> {
    const [result] = await db
      .select()
      .from(schedules)
      .where(eq(schedules.id, id))
      .limit(1);
    return result;
  }

  async findSchedulesByCourseId(courseId: string): Promise<Schedule[]> {
    return db.select().from(schedules).where(eq(schedules.courseId, courseId));
  }

  async findSchedulesByTeacherId(teacherId: string): Promise<Schedule[]> {
    return db
      .select()
      .from(schedules)
      .where(eq(schedules.teacherId, teacherId));
  }

  async findSchedulesByClassId(classId: string): Promise<Schedule[]> {
    return db.select().from(schedules).where(eq(schedules.classId, classId));
  }

  /**
   * Find overlapping schedules for conflict detection.
   * Returns schedules where [startTime, endTime) overlaps with the given range
   * for a specific teacher or class.
   */
  async findOverlappingSchedules(
    teacherId: string,
    classId: string,
    startTime: Date,
    endTime: Date,
    excludeId?: string,
  ): Promise<Schedule[]> {
    // Query both teacher and class conflicts
    const teacherSchedules = await db
      .select()
      .from(schedules)
      .where(
        and(
          eq(schedules.teacherId, teacherId),
          lte(schedules.startTime, endTime),
          gte(schedules.endTime, startTime),
        ),
      );
    const classSchedules = await db
      .select()
      .from(schedules)
      .where(
        and(
          eq(schedules.classId, classId),
          lte(schedules.startTime, endTime),
          gte(schedules.endTime, startTime),
        ),
      );
    const all = [...teacherSchedules, ...classSchedules];
    // Deduplicate and exclude self
    const seen = new Set<string>();
    return all.filter((s) => {
      if (excludeId && s.id === excludeId) return false;
      if (seen.has(s.id)) return false;
      seen.add(s.id);
      return true;
    });
  }

  async createSchedule(schedule: NewSchedule): Promise<void> {
    await db.insert(schedules).values(schedule);
  }

  async updateSchedule(id: string, data: Partial<NewSchedule>): Promise<void> {
    await db.update(schedules).set(data).where(eq(schedules.id, id));
  }
}

export const schedulingRepository = new SchedulingRepository();
