import { prisma } from "@/lib/prisma";
import { WEEK_DAYS } from "@/lib/constants";

export interface ScheduleBody {
  staffId?: number | string | null;
  sessionItemId?: number | string | null;
  departmentItemId?: number | string | null;
  classItemId?: number | string | null;
  sectionItemId?: number | string | null;
  groupItemId?: number | string | null;
  subjectItemId?: number | string | null;
  roomItemId?: number | string | null;
  days?: string[];
  startTime?: string;
  endTime?: string;
  note?: string;
  isActive?: boolean;
}

export interface NormalizedSchedule {
  staffId: number | null;
  sessionItemId: number | null;
  departmentItemId: number | null;
  classItemId: number | null;
  sectionItemId: number | null;
  groupItemId: number | null;
  subjectItemId: number | null;
  roomItemId: number | null;
  days: string[];
  startTime: string;
  endTime: string;
  note: string | null;
  isActive: boolean;
}

/** "HH:MM" strings compare correctly when zero-padded. */
function overlaps(aStart: string, aEnd: string, bStart: string, bEnd: string): boolean {
  return aStart < bEnd && bStart < aEnd;
}

function normalize(body: ScheduleBody): NormalizedSchedule {
  const num = (v: unknown) => {
    const n = Number(v);
    return Number.isFinite(n) && n > 0 ? n : null;
  };
  return {
    staffId: num(body.staffId),
    sessionItemId: num(body.sessionItemId),
    departmentItemId: num(body.departmentItemId),
    classItemId: num(body.classItemId),
    sectionItemId: num(body.sectionItemId),
    groupItemId: num(body.groupItemId),
    subjectItemId: num(body.subjectItemId),
    roomItemId: num(body.roomItemId),
    days: [...new Set((body.days ?? []).filter((d) => (WEEK_DAYS as readonly string[]).includes(d)))],
    startTime: String(body.startTime ?? ""),
    endTime: String(body.endTime ?? ""),
    note: String(body.note ?? "").trim() || null,
    isActive: body.isActive !== false,
  };
}

/** Validate + normalize a schedule payload; detects teacher/room/class clashes. */
export async function validateSchedule(
  institutionId: number,
  body: ScheduleBody,
  excludeId?: number
): Promise<{ error: string; status: number } | { data: NormalizedSchedule }> {
  const norm = normalize(body);

  if (!norm.staffId) return { error: "Teacher required", status: 400 };
  if (!norm.classItemId) return { error: "Class required", status: 400 };
  if (!norm.subjectItemId) return { error: "Subject required", status: 400 };
  if (norm.days.length === 0) return { error: "DAYS_REQUIRED", status: 400 };
  if (!/^\d{2}:\d{2}$/.test(norm.startTime) || !/^\d{2}:\d{2}$/.test(norm.endTime)) {
    return { error: "Invalid time", status: 400 };
  }
  if (norm.endTime <= norm.startTime) return { error: "TIME_INVALID", status: 400 };

  // Referenced academic items must be selected by this institution
  const itemIds = [
    norm.sessionItemId,
    norm.departmentItemId,
    norm.classItemId,
    norm.sectionItemId,
    norm.groupItemId,
    norm.subjectItemId,
    norm.roomItemId,
  ].filter((x): x is number => x !== null);
  if (itemIds.length > 0) {
    const selected = await prisma.academicSelection.count({
      where: { institutionId, academicItemId: { in: itemIds } },
    });
    if (selected !== itemIds.length) {
      return { error: "Item not selected by institution", status: 400 };
    }
  }

  const staff = await prisma.staff.findFirst({
    where: { id: norm.staffId, institutionId, status: "ACTIVE" },
  });
  if (!staff) return { error: "Teacher not found", status: 404 };

  // Clash detection against existing active schedules (shared day + overlapping time)
  const existing = await prisma.classSchedule.findMany({
    where: { institutionId, isActive: true, ...(excludeId ? { id: { not: excludeId } } : {}) },
  });
  for (const s of existing) {
    const sDays = (s.days as string[]) ?? [];
    if (!norm.days.some((d) => sDays.includes(d))) continue;
    if (!overlaps(norm.startTime, norm.endTime, s.startTime, s.endTime)) continue;

    if (s.staffId === norm.staffId) return { error: "CONFLICT_TEACHER", status: 409 };
    if (norm.roomItemId !== null && s.roomItemId === norm.roomItemId) {
      return { error: "CONFLICT_ROOM", status: 409 };
    }
    if (
      s.classItemId === norm.classItemId &&
      (s.sectionItemId ?? null) === norm.sectionItemId
    ) {
      return { error: "CONFLICT_CLASS", status: 409 };
    }
  }

  return { data: norm };
}
