"use server";

import { revalidatePath } from "next/cache";
import {
  ClassSchema,
  ExamSchema,
  StudentSchema,
  SubjectSchema,
  TeacherSchema,
  LessonSchema,
  AssignmentSchema,
  ResultSchema,
  EventSchema,
  AnnouncementSchema,
  TermSchema,
  HolidaySchema,
  SchoolTimetableSlotSchema,
  TimetableTemplateSchema,
  TimetableEntrySchema,
} from "./formValidationSchemas";
import { getSupabaseClient } from "./supabase";
import { clerkClient, auth } from "@clerk/nextjs/server";

type CurrentState = { success: boolean; error: boolean };

export const createSubject = async (
  currentState: CurrentState,
  data: SubjectSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const { data: newSubject, error: subjectError } = await supabase
      .from("Subject")
      .insert([{ name: data.name, schoolId: data.schoolId }])
      .select()
      .single();

    if (subjectError) throw subjectError;

    if (data.teachers && data.teachers.length > 0) {
      const teacherConnections = data.teachers.map((tId) => ({
        subjectId: newSubject.id as number,
        teacherId: tId,
      }));
      const { error: relError } = await supabase
        .from("TeacherSubject")
        .insert(teacherConnections);
      if (relError) throw relError;
    }

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateSubject = async (
  currentState: CurrentState,
  data: SubjectSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };

    const supabase = await getSupabaseClient();
    const { error: subjectError } = await supabase
      .from("Subject")
      .update({ name: data.name, schoolId: data.schoolId })
      .eq("id", data.id);

    if (subjectError) throw subjectError;

    // To mimic prisma `set` behaviour for m2m, first delete existing relations then insert new
    await supabase.from("TeacherSubject").delete().eq("subjectId", data.id);

    if (data.teachers && data.teachers.length > 0) {
      const subjectId = data.id as number;
      const teacherConnections = data.teachers.map((tId) => ({
        subjectId: subjectId,
        teacherId: tId,
      }));
      const { error: relError } = await supabase
        .from("TeacherSubject")
        .insert(teacherConnections);
      if (relError) throw relError;
    }

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteSubject = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    // Assuming cascading delete is setup in Postgres for m2m, else we need to delete from join table first.
    const { error } = await supabase.from("Subject").delete().eq("id", parseInt(id));
    if (error) throw error;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const createClass = async (
  currentState: CurrentState,
  data: ClassSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Class").insert([{
      name: data.name,
      capacity: data.capacity,
      supervisorId: data.supervisorId || null,
      gradeId: data.gradeId,
      schoolId: data.schoolId,
    }]);

    if (error) throw error;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateClass = async (
  currentState: CurrentState,
  data: ClassSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };
    const supabase = await getSupabaseClient();
    const { error } = await supabase
      .from("Class")
      .update({
        name: data.name,
        capacity: data.capacity,
        supervisorId: data.supervisorId || null,
        gradeId: data.gradeId,
        schoolId: data.schoolId,
      })
      .eq("id", data.id);

    if (error) throw error;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteClass = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Class").delete().eq("id", parseInt(id));
    if (error) throw error;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const createTeacher = async (
  currentState: CurrentState,
  data: TeacherSchema
) => {
  try {
    const user = await clerkClient.users.createUser({
      username: data.username,
      password: data.password,
      firstName: data.name,
      lastName: data.surname,
      publicMetadata: { role: "teacher" }
    });

    const supabase = await getSupabaseClient();
    const { error: teacherError } = await supabase.from("Teacher").insert([{
      id: user.id,
      username: data.username,
      name: data.name,
      surname: data.surname,
      email: data.email || null,
      phone: data.phone || null,
      address: data.address,
      img: data.img || null,
      bloodType: data.bloodType,
      sex: data.sex,
      birthday: data.birthday.toISOString(),
    }]);

    if (teacherError) throw teacherError;

    if (data.subjects && data.subjects.length > 0) {
      const subjectConnections = data.subjects.map((sId: string) => ({
        subjectId: parseInt(sId),
        teacherId: user.id,
      }));
      const { error: relError } = await supabase
        .from("TeacherSubject")
        .insert(subjectConnections);
      if (relError) throw relError;
    }

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const ensureTeacherOnboarding = async () => {
  try {
    const { userId, sessionClaims } = auth();

    if (!userId) {
      return { status: "anonymous" as const };
    }

    const metadata = (sessionClaims?.metadata || {}) as {
      role?: string;
      teacherType?: "INDEPENDENT" | "AGENCY" | string;
    };

    if (metadata.role !== "teacher") {
      return { status: "not-teacher" as const };
    }

    const teacherType =
      (metadata.teacherType as "INDEPENDENT" | "AGENCY" | undefined) ||
      "INDEPENDENT";

    const supabase = await getSupabaseClient();

    // Ensure a Teacher row exists for this Clerk user
    const { data: existingTeacher, error: teacherLookupError } = await supabase
      .from("Teacher")
      .select("id")
      .eq("id", userId)
      .maybeSingle();

    if (teacherLookupError) {
      throw teacherLookupError;
    }

    if (!existingTeacher) {
      const user = await clerkClient.users.getUser(userId);
      const primaryEmail = user.emailAddresses[0]?.emailAddress || null;

      await supabase.from("Teacher").insert({
        id: userId,
        username: user.username || userId,
        name: user.firstName || "Teacher",
        surname: user.lastName || "",
        email: primaryEmail,
        phone: null,
        address: "",
        bloodType: "A+",
        sex: "MALE",
        birthday: new Date().toISOString(),
      });
    }

    // Check if this teacher is already linked to a school
    const { data: mappings, error: mappingError } = await supabase
      .from("TeacherSchool")
      .select("id, schoolId")
      .eq("teacherId", userId)
      .limit(1);

    if (mappingError) {
      throw mappingError;
    }

    if (mappings && mappings.length > 0) {
      return { status: "existing", schoolId: mappings[0].schoolId as string };
    }

    // Create a dedicated School for this independent/agency teacher
    const schoolType = teacherType === "AGENCY" ? "AGENCY" : "INDEPENDENT";
    const schoolId = `school-${schoolType.toLowerCase()}-${userId}`;

    const { error: schoolError } = await supabase.from("School").insert({
      id: schoolId,
      name:
        schoolType === "AGENCY"
          ? "My Agency"
          : "My Independent School",
      type: schoolType,
      adminId: userId,
    });

    if (schoolError) {
      throw schoolError;
    }

    const { error: mappingInsertError } = await supabase
      .from("TeacherSchool")
      .insert({
        teacherId: userId,
        schoolId,
        isManaged: false,
      });

    if (mappingInsertError) {
      throw mappingInsertError;
    }

    return { status: "created", schoolId, schoolType };
  } catch (err) {
    console.log("ensureTeacherOnboarding error:", err);
    return { status: "error" as const };
  }
};

export const setActiveSchool = async (data: FormData): Promise<void> => {
  try {
    const { userId, sessionClaims } = auth();

    if (!userId) return;

    const schoolId = data.get("schoolId") as string;
    if (!schoolId) return;

    const role =
      (sessionClaims?.metadata as { role?: string })?.role || "teacher";

    const supabase = await getSupabaseClient();

    if (role === "admin") {
      const { data: school, error } = await supabase
        .from("School")
        .select("id")
        .eq("id", schoolId)
        .eq("adminId", userId)
        .maybeSingle();

      if (error || !school) return;
    } else if (role === "teacher") {
      const { data: mapping, error } = await supabase
        .from("TeacherSchool")
        .select("id")
        .eq("teacherId", userId)
        .eq("schoolId", schoolId)
        .maybeSingle();

      if (error || !mapping) return;
    } else {
      return;
    }

    const user = await clerkClient.users.getUser(userId);
    const publicMetadata = user.publicMetadata || {};

    await clerkClient.users.updateUser(userId, {
      publicMetadata: {
        ...publicMetadata,
        schoolId,
      },
    });
  } catch (err) {
    console.log("setActiveSchool error:", err);
  }
};

export const linkTeacherToSchool = async (data: FormData): Promise<void> => {
  try {
    const { userId, sessionClaims } = auth();

    if (!userId) return;

    const role =
      (sessionClaims?.metadata as { role?: string })?.role || "teacher";
    if (role !== "teacher") return;

    const schoolId = data.get("schoolId") as string;
    if (!schoolId) return;

    const supabase = await getSupabaseClient();

    // Ensure mapping exists (idempotent)
    const { data: existing, error: existingError } = await supabase
      .from("TeacherSchool")
      .select("id")
      .eq("teacherId", userId)
      .eq("schoolId", schoolId)
      .maybeSingle();

    if (existingError) {
      console.error("linkTeacherToSchool existing check:", existingError);
      return;
    }
    if (!existing) {
      const { error: insertError } = await supabase.from("TeacherSchool").insert({
        teacherId: userId,
        schoolId,
        isManaged: false,
      });
      if (insertError) {
        console.error("linkTeacherToSchool insert:", insertError);
        return;
      }
    }
    revalidatePath("/list/my-schools");
  } catch (err) {
    console.error("linkTeacherToSchool error:", err);
  }
};

export const updateTeacher = async (
  currentState: CurrentState,
  data: TeacherSchema
) => {
  if (!data.id) {
    return { success: false, error: true };
  }
  try {
    await clerkClient.users.updateUser(data.id, {
      username: data.username,
      ...(data.password !== "" && { password: data.password }),
      firstName: data.name,
      lastName: data.surname,
    });

    const supabase = await getSupabaseClient();
    const { error: teacherError } = await supabase.from("Teacher").update({
      ...(data.password !== "" && { password: data.password }),
      username: data.username,
      name: data.name,
      surname: data.surname,
      email: data.email || null,
      phone: data.phone || null,
      address: data.address,
      img: data.img || null,
      bloodType: data.bloodType,
      sex: data.sex,
      birthday: data.birthday.toISOString(),
    }).eq("id", data.id);

    if (teacherError) throw teacherError;

    await supabase.from("TeacherSubject").delete().eq("teacherId", data.id);

    if (data.subjects && data.subjects.length > 0) {
      const subjectConnections = data.subjects.map((sId: string) => ({
        subjectId: parseInt(sId),
        teacherId: data.id as string,
      }));
      const { error: relError } = await supabase
        .from("TeacherSubject")
        .insert(subjectConnections);
      if (relError) throw relError;
    }

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteTeacher = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    await clerkClient.users.deleteUser(id);

    const supabase = await getSupabaseClient();
    const { error: teacherError } = await supabase.from("Teacher").delete().eq("id", id);
    if (teacherError) throw teacherError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const createStudent = async (
  currentState: CurrentState,
  data: StudentSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    for (const classId of data.classIds) {
      const { data: classRows } = await supabase.from("Class").select("capacity").eq("id", classId).single();
      const { count } = await supabase.from("StudentClass").select("studentId", { count: "exact", head: true }).eq("classId", classId);
      if (classRows && (count ?? 0) >= classRows.capacity) {
        return { success: false, error: true };
      }
    }

    const user = await clerkClient.users.createUser({
      username: data.username,
      password: data.password,
      firstName: data.name,
      lastName: data.surname,
      publicMetadata: { role: "student" },
    });

    const { error: studentError } = await supabase.from("Student").insert([{
      id: user.id,
      username: data.username,
      name: data.name,
      surname: data.surname,
      email: data.email || null,
      phone: data.phone || null,
      address: data.address,
      img: data.img || null,
      bloodType: data.bloodType,
      sex: data.sex,
      birthday: data.birthday.toISOString(),
      gradeId: data.gradeId,
      parentId: data.parentId,
      schoolId: data.schoolId,
    }]);

    if (studentError) throw studentError;

    if (data.classIds.length) {
      const { error: linkError } = await supabase.from("StudentClass").insert(
        data.classIds.map((classId) => ({ studentId: user.id, classId }))
      );
      if (linkError) throw linkError;
    }

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateStudent = async (
  currentState: CurrentState,
  data: StudentSchema
) => {
  if (!data.id) {
    return { success: false, error: true };
  }
  try {
    await clerkClient.users.updateUser(data.id, {
      username: data.username,
      ...(data.password !== "" && { password: data.password }),
      firstName: data.name,
      lastName: data.surname,
    });

    const supabase = await getSupabaseClient();

    for (const classId of data.classIds) {
      const { data: classRows } = await supabase.from("Class").select("capacity").eq("id", classId).single();
      const { count } = await supabase.from("StudentClass").select("studentId", { count: "exact", head: true }).eq("classId", classId);
      const alreadyIn = await supabase.from("StudentClass").select("studentId").eq("studentId", data.id).eq("classId", classId).maybeSingle();
      if (classRows && !alreadyIn.data && (count ?? 0) >= classRows.capacity) {
        return { success: false, error: true };
      }
    }

    const { error: studentError } = await supabase.from("Student").update({
      ...(data.password !== "" && { password: data.password }),
      username: data.username,
      name: data.name,
      surname: data.surname,
      email: data.email || null,
      phone: data.phone || null,
      address: data.address,
      img: data.img || null,
      bloodType: data.bloodType,
      sex: data.sex,
      birthday: data.birthday.toISOString(),
      gradeId: data.gradeId,
      parentId: data.parentId,
      schoolId: data.schoolId,
    }).eq("id", data.id);

    if (studentError) throw studentError;

    await supabase.from("StudentClass").delete().eq("studentId", data.id);
    if (data.classIds.length) {
      const { error: linkError } = await supabase.from("StudentClass").insert(
        data.classIds.map((classId) => ({ studentId: data.id!, classId }))
      );
      if (linkError) throw linkError;
    }

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteStudent = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    await clerkClient.users.deleteUser(id);

    const supabase = await getSupabaseClient();
    const { error: studentError } = await supabase.from("Student").delete().eq("id", id);
    if (studentError) throw studentError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const createExam = async (
  currentState: CurrentState,
  data: ExamSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Exam").insert([{
      title: data.title,
      startTime: data.startTime.toISOString(),
      endTime: data.endTime.toISOString(),
      lessonId: data.lessonId,
      schoolId: data.schoolId,
    }]);

    if (error) throw error;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateExam = async (
  currentState: CurrentState,
  data: ExamSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Exam").update({
      title: data.title,
      startTime: data.startTime.toISOString(),
      endTime: data.endTime.toISOString(),
      lessonId: data.lessonId,
      schoolId: data.schoolId,
    }).eq("id", data.id);

    if (error) throw error;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteExam = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;

  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Exam").delete().eq("id", parseInt(id));
    if (error) throw error;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const createLesson = async (
  currentState: CurrentState,
  data: LessonSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const { data: newLesson, error: lessonError } = await supabase
      .from("Lesson")
      .insert([
        {
          name: data.name,
          startTime: data.startTime.toISOString(),
          endTime: data.endTime.toISOString(),
          subjectId: data.subjectId,
          classId: data.classId,
          teacherId: data.teacherId,
          schoolId: data.schoolId,
        },
      ])
      .select()
      .single();

    if (lessonError) throw lessonError;

    // Create an associated LessonWhiteboard entry
    const { error: whiteboardError } = await supabase
      .from("LessonWhiteboard")
      .insert([
        {
          lessonId: newLesson.id,
        },
      ]);

    if (whiteboardError) throw whiteboardError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateLesson = async (
  currentState: CurrentState,
  data: LessonSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };

    const supabase = await getSupabaseClient();
    const { error: lessonError } = await supabase
      .from("Lesson")
      .update({
        name: data.name,
        startTime: data.startTime.toISOString(),
        endTime: data.endTime.toISOString(),
        subjectId: data.subjectId,
        classId: data.classId,
        teacherId: data.teacherId,
        schoolId: data.schoolId,
      })
      .eq("id", data.id);

    if (lessonError) throw lessonError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteLesson = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Lesson").delete().eq("id", parseInt(id));
    if (error) throw error;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const createAssignment = async (
  currentState: CurrentState,
  data: AssignmentSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const { error: assignmentError } = await supabase
      .from("Assignment")
      .insert([
        {
          title: data.title,
          startDate: data.startDate.toISOString(),
          dueDate: data.dueDate.toISOString(),
          lessonId: data.lessonId,
          schoolId: data.schoolId,
        },
      ]);

    if (assignmentError) throw assignmentError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateAssignment = async (
  currentState: CurrentState,
  data: AssignmentSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };

    const supabase = await getSupabaseClient();
    const { error: assignmentError } = await supabase
      .from("Assignment")
      .update({
        title: data.title,
        startDate: data.startDate.toISOString(),
        dueDate: data.dueDate.toISOString(),
        lessonId: data.lessonId,
        schoolId: data.schoolId,
      })
      .eq("id", data.id);

    if (assignmentError) throw assignmentError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteAssignment = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Assignment").delete().eq("id", parseInt(id));
    if (error) throw error;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const createResult = async (
  currentState: CurrentState,
  data: ResultSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const { error: resultError } = await supabase
      .from("Result")
      .insert([
        {
          score: data.score,
          studentId: data.studentId,
          ...(data.examId ? { examId: data.examId } : {}),
          ...(data.assignmentId ? { assignmentId: data.assignmentId } : {}),
          schoolId: data.schoolId,
        },
      ]);

    if (resultError) throw resultError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateResult = async (
  currentState: CurrentState,
  data: ResultSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };

    const supabase = await getSupabaseClient();
    const { error: resultError } = await supabase
      .from("Result")
      .update({
        score: data.score,
        studentId: data.studentId,
        ...(data.examId ? { examId: data.examId } : {}),
        ...(data.assignmentId ? { assignmentId: data.assignmentId } : {}),
        schoolId: data.schoolId,
      })
      .eq("id", data.id);

    if (resultError) throw resultError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteResult = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Result").delete().eq("id", parseInt(id));
    if (error) throw error;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const createEvent = async (
  currentState: CurrentState,
  data: EventSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const { error: eventError } = await supabase
      .from("Event")
      .insert([
        {
          title: data.title,
          description: data.description,
          startTime: data.startTime.toISOString(),
          endTime: data.endTime.toISOString(),
          ...(data.classId ? { classId: data.classId } : {}),
          schoolId: data.schoolId,
        },
      ]);

    if (eventError) throw eventError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateEvent = async (
  currentState: CurrentState,
  data: EventSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };

    const supabase = await getSupabaseClient();
    const { error: eventError } = await supabase
      .from("Event")
      .update({
        title: data.title,
        description: data.description,
        startTime: data.startTime.toISOString(),
        endTime: data.endTime.toISOString(),
        ...(data.classId ? { classId: data.classId } : {}),
        schoolId: data.schoolId,
      })
      .eq("id", data.id);

    if (eventError) throw eventError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteEvent = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Event").delete().eq("id", parseInt(id));
    if (error) throw error;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const createAnnouncement = async (
  currentState: CurrentState,
  data: AnnouncementSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const { error: announcementError } = await supabase
      .from("Announcement")
      .insert([
        {
          title: data.title,
          description: data.description,
          date: data.date.toISOString(),
          ...(data.classId ? { classId: data.classId } : {}),
          schoolId: data.schoolId,
        },
      ]);

    if (announcementError) throw announcementError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateAnnouncement = async (
  currentState: CurrentState,
  data: AnnouncementSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };

    const supabase = await getSupabaseClient();
    const { error: announcementError } = await supabase
      .from("Announcement")
      .update({
        title: data.title,
        description: data.description,
        date: data.date.toISOString(),
        ...(data.classId ? { classId: data.classId } : {}),
        schoolId: data.schoolId,
      })
      .eq("id", data.id);

    if (announcementError) throw announcementError;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteAnnouncement = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Announcement").delete().eq("id", parseInt(id));
    if (error) throw error;

    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

// --- TERM ACTIONS ---

export const createTerm = async (
  currentState: CurrentState,
  data: TermSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const academicYearId =
      data.academicYearId ??
      (await supabase
        .from("AcademicYear")
        .select("id")
        .eq("schoolId", data.schoolId)
        .order("id", { ascending: true })
        .limit(1)
        .single()
        .then((r) => r.data?.id));
    if (!academicYearId) throw new Error("No academic year found for this school.");

    const { error: termError } = await supabase
      .from("Term")
      .insert([
        {
          name: data.name,
          startDate: data.startDate.toISOString(),
          endDate: data.endDate.toISOString(),
          schoolId: data.schoolId,
          academicYearId,
        },
      ]);

    if (termError) throw termError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateTerm = async (
  currentState: CurrentState,
  data: TermSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };
    const supabase = await getSupabaseClient();
    const payload: Record<string, unknown> = {
      name: data.name,
      startDate: data.startDate.toISOString(),
      endDate: data.endDate.toISOString(),
      schoolId: data.schoolId,
    };
    if (data.academicYearId != null) payload.academicYearId = data.academicYearId;
    const { error: termError } = await supabase
      .from("Term")
      .update(payload)
      .eq("id", data.id);

    if (termError) throw termError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteTerm = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Term").delete().eq("id", parseInt(id));
    if (error) throw error;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

// --- HOLIDAY ACTIONS ---

export const createHoliday = async (
  currentState: CurrentState,
  data: HolidaySchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const academicYearId =
      data.academicYearId ??
      (await supabase
        .from("AcademicYear")
        .select("id")
        .eq("schoolId", data.schoolId)
        .order("id", { ascending: true })
        .limit(1)
        .single()
        .then((r) => r.data?.id));

    const { error: holidayError } = await supabase
      .from("Holiday")
      .insert([
        {
          name: data.name,
          startDate: data.startDate.toISOString(),
          endDate: data.endDate.toISOString(),
          schoolId: data.schoolId,
          ...(academicYearId != null && { academicYearId }),
        },
      ]);

    if (holidayError) throw holidayError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateHoliday = async (
  currentState: CurrentState,
  data: HolidaySchema
) => {
  try {
    if (!data.id) return { success: false, error: true };
    const supabase = await getSupabaseClient();
    const payload: Record<string, unknown> = {
      name: data.name,
      startDate: data.startDate.toISOString(),
      endDate: data.endDate.toISOString(),
      schoolId: data.schoolId,
    };
    if (data.academicYearId !== undefined) payload.academicYearId = data.academicYearId;
    const { error: holidayError } = await supabase
      .from("Holiday")
      .update(payload)
      .eq("id", data.id);

    if (holidayError) throw holidayError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteHoliday = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("Holiday").delete().eq("id", parseInt(id));
    if (error) throw error;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

// --- SCHOOL TIMETABLE SLOT ACTIONS ---

export const createSchoolTimetableSlot = async (
  currentState: CurrentState,
  data: SchoolTimetableSlotSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const schoolTimetableId =
      data.schoolTimetableId ??
      (await supabase
        .from("SchoolTimetable")
        .select("id")
        .eq("schoolId", data.schoolId)
        .order("id", { ascending: true })
        .limit(1)
        .single()
        .then((r) => r.data?.id));
    if (!schoolTimetableId) throw new Error("No school timetable found for this school.");

    const { data: maxSlot } = await supabase
      .from("SchoolTimetableSlot")
      .select("position")
      .eq("schoolTimetableId", schoolTimetableId)
      .order("position", { ascending: false })
      .limit(1)
      .single();
    const position = (maxSlot?.position ?? 0) + 1;

    const { error: slotError } = await supabase
      .from("SchoolTimetableSlot")
      .insert([
        {
          name: data.name,
          startTime: data.startTime,
          endTime: data.endTime,
          isTeachingSlot: data.isTeachingSlot,
          schoolId: data.schoolId,
          schoolTimetableId,
          position,
        },
      ]);

    if (slotError) throw slotError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateSchoolTimetableSlot = async (
  currentState: CurrentState,
  data: SchoolTimetableSlotSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };
    const supabase = await getSupabaseClient();
    const payload: Record<string, unknown> = {
      name: data.name,
      startTime: data.startTime,
      endTime: data.endTime,
      isTeachingSlot: data.isTeachingSlot,
      schoolId: data.schoolId,
    };
    if (data.position != null) payload.position = data.position;
    if (data.schoolTimetableId != null) payload.schoolTimetableId = data.schoolTimetableId;
    const { error: slotError } = await supabase
      .from("SchoolTimetableSlot")
      .update(payload)
      .eq("id", data.id);

    if (slotError) throw slotError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteSchoolTimetableSlot = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase.from("SchoolTimetableSlot").delete().eq("id", parseInt(id));
    if (error) throw error;
    return { success: true, error: false };

  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

// --- TIMETABLE TEMPLATE ACTIONS ---

export const createTimetableTemplate = async (
  currentState: CurrentState,
  data: TimetableTemplateSchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const schoolTimetableId =
      data.schoolTimetableId ??
      (await supabase
        .from("SchoolTimetable")
        .select("id")
        .eq("schoolId", data.schoolId)
        .order("id", { ascending: true })
        .limit(1)
        .single()
        .then((r) => r.data?.id));
    if (!schoolTimetableId) throw new Error("No school timetable found for this school.");

    const { error: templateError } = await supabase
      .from("TeacherTimetableTemplate")
      .insert([
        {
          name: data.name,
          teacherId: data.teacherId,
          schoolId: data.schoolId,
          schoolTimetableId,
        },
      ]);

    if (templateError) throw templateError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateTimetableTemplate = async (
  currentState: CurrentState,
  data: TimetableTemplateSchema
) => {
  try {
    if (!data.id) return { success: false, error: true };
    const supabase = await getSupabaseClient();
    const payload: Record<string, unknown> = {
      name: data.name,
      teacherId: data.teacherId,
      schoolId: data.schoolId,
    };
    if (data.schoolTimetableId != null) payload.schoolTimetableId = data.schoolTimetableId;
    const { error: templateError } = await supabase
      .from("TeacherTimetableTemplate")
      .update(payload)
      .eq("id", data.id);

    if (templateError) throw templateError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteTimetableTemplate = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase
      .from("TeacherTimetableTemplate")
      .delete()
      .eq("id", parseInt(id));
    if (error) throw error;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

// --- TIMETABLE ENTRY ACTIONS ---

export const createTimetableEntry = async (
  currentState: CurrentState,
  data: TimetableEntrySchema
) => {
  try {
    const supabase = await getSupabaseClient();
    const templateId = data.teacherTimetableTemplateId ?? data.timetableTemplateId;
    if (!templateId) throw new Error("Template is required.");
    const { error: entryError } = await supabase
      .from("TeacherTimetableEntry")
      .insert([
        {
          teacherTimetableTemplateId: templateId,
          schoolTimetableSlotId: data.schoolTimetableSlotId,
          classId: data.classId,
          subjectId: data.subjectId,
          dayOfWeek: data.dayOfWeek,
        },
      ]);

    if (entryError) throw entryError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const updateTimetableEntry = async (
  currentState: CurrentState,
  data: TimetableEntrySchema
) => {
  try {
    if (!data.id) return { success: false, error: true };
    const supabase = await getSupabaseClient();
    const templateId = data.teacherTimetableTemplateId ?? data.timetableTemplateId;
    if (!templateId) throw new Error("Template is required.");
    const { error: entryError } = await supabase
      .from("TeacherTimetableEntry")
      .update({
        teacherTimetableTemplateId: templateId,
        schoolTimetableSlotId: data.schoolTimetableSlotId,
        classId: data.classId,
        subjectId: data.subjectId,
        dayOfWeek: data.dayOfWeek,
      })
      .eq("id", data.id);

    if (entryError) throw entryError;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

export const deleteTimetableEntry = async (
  currentState: CurrentState,
  data: FormData
) => {
  const id = data.get("id") as string;
  try {
    const supabase = await getSupabaseClient();
    const { error } = await supabase
      .from("TeacherTimetableEntry")
      .delete()
      .eq("id", parseInt(id));
    if (error) throw error;
    return { success: true, error: false };
  } catch (err) {
    console.log(err);
    return { success: false, error: true };
  }
};

// --- TIMETABLE GENERATOR ACTIONS ---

export const generateTimetableLessons = async (
  currentState: CurrentState,
  data: FormData
) => {
  const termId = parseInt(data.get("termId") as string);
  const schoolId = data.get("schoolId") as string;

  if (!termId || !schoolId) {
    return { success: false, error: true, message: "Missing term or school ID." };
  }

  try {
    const supabase = await getSupabaseClient();

    // 1. Fetch the term
    const { data: term, error: termError } = await supabase
      .from("Term")
      .select("*")
      .eq("id", termId)
      .eq("schoolId", schoolId)
      .single();

    if (termError || !term) {
      return { success: false, error: true, message: "Term not found." };
    }

    // 2. Fetch holidays for the school overlapping the term
    const { data: holidays, error: holidayError } = await supabase
      .from("Holiday")
      .select("*")
      .eq("schoolId", schoolId)
      .gte("endDate", term.startDate)
      .lte("startDate", term.endDate);

    if (holidayError) {
      return { success: false, error: true, message: "Error fetching holidays." };
    }

    // 3. Fetch all timetable entries for the school
    const { data: templates } = await supabase
      .from("TeacherTimetableTemplate")
      .select("id")
      .eq("schoolId", schoolId);

    if (!templates || templates.length === 0) {
      return { success: true, error: false, message: "No templates found for this school, skipping entry generation." };
    }

    const templateIds = templates.map((t) => t.id);

    const { data: entries, error: entryError } = await supabase
      .from("TeacherTimetableEntry")
      .select("*, slot:SchoolTimetableSlot(*)")
      .in("teacherTimetableTemplateId", templateIds);

    if (entryError || !entries) {
      return { success: false, error: true, message: "Error fetching timetable entries." };
    }

    const termStart = new Date(term.startDate);
    const termEnd = new Date(term.endDate);

    const { data: loadedTemplates } = await supabase
      .from("TeacherTimetableTemplate")
      .select("*");

    const getTeacherId = (templateId: number) => {
      const t = loadedTemplates?.find((pt) => pt.id === templateId);
      return t?.teacherId;
    };

    const isHoliday = (date: Date) => {
      const checkTime = date.getTime();
      return holidays && holidays.some((h) => {
        const hStart = new Date(h.startDate).getTime();
        const hEnd = new Date(h.endDate).getTime();
        return checkTime >= hStart && checkTime <= hEnd;
      });
    };

    const mappedLessons: any[] = [];
    const currentDate = new Date(termStart);

    while (currentDate <= termEnd) {
      // 0 = Sunday, 6 = Saturday in JS
      const jsDay = currentDate.getDay();
      const dbDay = jsDay === 0 ? 7 : jsDay; // Convert to 1-7 for DB logic

      if (!isHoliday(currentDate)) {
        const dayEntries = entries
          .filter((e) => e.dayOfWeek === dbDay)
          .sort((a, b) => ((a.slot as { position?: number })?.position ?? 0) - ((b.slot as { position?: number })?.position ?? 0));

        for (const entry of dayEntries) {
          if (!entry.slot) continue;

          const [startHour, startMin] = (entry.slot as any).startTime.split(":").map(Number);
          const [endHour, endMin] = (entry.slot as any).endTime.split(":").map(Number);

          const lessonStart = new Date(currentDate);
          lessonStart.setUTCHours(startHour, startMin, 0, 0);

          const lessonEnd = new Date(currentDate);
          lessonEnd.setUTCHours(endHour, endMin, 0, 0);

          const teacherId = getTeacherId(entry.teacherTimetableTemplateId);

          if (teacherId) {
            mappedLessons.push({
              name: `${(entry.slot as any).name}`,
              startTime: lessonStart.toISOString(),
              endTime: lessonEnd.toISOString(),
              subjectId: entry.subjectId,
              classId: entry.classId,
              teacherId: teacherId,
              schoolId: schoolId,
            });
          }
        }
      }

      currentDate.setUTCDate(currentDate.getUTCDate() + 1);
    }

    if (mappedLessons.length === 0) {
      return { success: true, error: false, message: "No lessons generated (no valid dates/entries found)." };
    }

    // Avoid duplicates: delete existing lessons in this date range for the same school and teachers we're generating for
    const teacherIdsToReplace = Array.from(new Set(mappedLessons.map((l) => l.teacherId)));
    const termStartISO = termStart.toISOString();
    const termEndISO = termEnd.toISOString();
    for (const tid of teacherIdsToReplace) {
      const { error: delError } = await supabase
        .from("Lesson")
        .delete()
        .eq("schoolId", schoolId)
        .eq("teacherId", tid)
        .gte("startTime", termStartISO)
        .lte("startTime", termEndISO);
      if (delError) {
        console.error("Delete existing lessons error", delError);
        return { success: false, error: true, message: "Failed clearing existing lessons in range." };
      }
    }

    // Ensure Lesson id sequence is past max(id) so inserts get unique ids (avoids duplicate key after seed_schedule etc.)
    const { error: syncErr } = await (supabase as any).rpc("sync_lesson_id_sequence");
    if (syncErr) {
      console.error("generateTimetableLessons sync sequence error:", syncErr);
    }

    const CHUNK_SIZE = 1000;
    for (let i = 0; i < mappedLessons.length; i += CHUNK_SIZE) {
      const chunk = mappedLessons.slice(i, i + CHUNK_SIZE);
      const { error: insertError } = await supabase.from("Lesson").insert(chunk);
      if (insertError) {
        const errMsg = insertError.message || insertError.code || "Unknown error";
        console.error("generateTimetableLessons insert error:", insertError.code, insertError.message, insertError.details);
        return { success: false, error: true, message: `Failed inserting lessons: ${errMsg}` };
      }
    }

    return { success: true, error: false, message: `Successfully generated ${mappedLessons.length} lessons!` };

  } catch (err) {
    const message = err instanceof Error ? err.message : "An unexpected error occurred.";
    console.error("generateTimetableLessons unexpected error:", err);
    return { success: false, error: true, message };
  }
};

// --- GENERATE LESSONS FROM A SINGLE TEMPLATE (teacher populates own calendar) ---

export const generateLessonsFromTemplate = async (
  currentState: CurrentState,
  data: FormData
) => {
  const templateId = parseInt(data.get("templateId") as string);
  const schoolId = data.get("schoolId") as string;
  const termIdParam = data.get("termId") as string | null;
  const startDateParam = data.get("startDate") as string | null;
  const endDateParam = data.get("endDate") as string | null;

  if (!templateId || !schoolId) {
    return { success: false, error: true, message: "Missing template or school ID." };
  }

  const useTerm = termIdParam && termIdParam !== "";
  const useCustomRange = startDateParam && endDateParam && startDateParam !== "" && endDateParam !== "";

  if (!useTerm && !useCustomRange) {
    return { success: false, error: true, message: "Select a term or enter start and end dates." };
  }

  try {
    const supabase = await getSupabaseClient();

    const { data: template, error: templateError } = await supabase
      .from("TeacherTimetableTemplate")
      .select("id, teacherId, schoolId")
      .eq("id", templateId)
      .eq("schoolId", schoolId)
      .single();

    if (templateError || !template) {
      return { success: false, error: true, message: "Template not found or access denied." };
    }

    let rangeStart: Date;
    let rangeEnd: Date;

    if (useTerm) {
      const termId = parseInt(termIdParam!);
      const { data: term, error: termError } = await supabase
        .from("Term")
        .select("startDate, endDate")
        .eq("id", termId)
        .eq("schoolId", schoolId)
        .single();
      if (termError || !term) {
        return { success: false, error: true, message: "Term not found." };
      }
      rangeStart = new Date(term.startDate);
      rangeEnd = new Date(term.endDate);
    } else {
      rangeStart = new Date(startDateParam!);
      rangeEnd = new Date(endDateParam!);
      if (Number.isNaN(rangeStart.getTime()) || Number.isNaN(rangeEnd.getTime())) {
        return { success: false, error: true, message: "Invalid date format." };
      }
      if (rangeStart > rangeEnd) {
        return { success: false, error: true, message: "Start date must be before end date." };
      }
    }

    const { data: holidays } = await supabase
      .from("Holiday")
      .select("*")
      .eq("schoolId", schoolId)
      .gte("endDate", rangeStart.toISOString())
      .lte("startDate", rangeEnd.toISOString());

    const { data: entries, error: entryError } = await supabase
      .from("TeacherTimetableEntry")
      .select("*, slot:SchoolTimetableSlot(*)")
      .eq("teacherTimetableTemplateId", templateId);

    if (entryError || !entries || entries.length === 0) {
      return { success: true, error: false, message: "No timetable entries in this template. Add entries first." };
    }

    const isHoliday = (date: Date) => {
      const t = date.getTime();
      return holidays?.some((h) => {
        const hStart = new Date(h.startDate).getTime();
        const hEnd = new Date(h.endDate).getTime();
        return t >= hStart && t <= hEnd;
      }) ?? false;
    };

    const mappedLessons: Array<{
      name: string;
      startTime: string;
      endTime: string;
      subjectId: number;
      classId: number;
      teacherId: string;
      schoolId: string;
    }> = [];
    const currentDate = new Date(rangeStart);

    while (currentDate <= rangeEnd) {
      const jsDay = currentDate.getDay();
      const dbDay = jsDay === 0 ? 7 : jsDay;
      if (!isHoliday(currentDate)) {
        const dayEntries = entries
          .filter((e) => e.dayOfWeek === dbDay)
          .sort((a, b) => ((a.slot as { position?: number })?.position ?? 0) - ((b.slot as { position?: number })?.position ?? 0));
        for (const entry of dayEntries) {
          if (!entry.slot) continue;
          const [startHour, startMin] = (entry.slot as { startTime: string }).startTime.split(":").map(Number);
          const [endHour, endMin] = (entry.slot as { endTime: string }).endTime.split(":").map(Number);
          const lessonStart = new Date(currentDate);
          lessonStart.setUTCHours(startHour, startMin, 0, 0);
          const lessonEnd = new Date(currentDate);
          lessonEnd.setUTCHours(endHour, endMin, 0, 0);
          mappedLessons.push({
            name: (entry.slot as { name: string }).name,
            startTime: lessonStart.toISOString(),
            endTime: lessonEnd.toISOString(),
            subjectId: entry.subjectId,
            classId: entry.classId,
            teacherId: template.teacherId,
            schoolId: template.schoolId,
          });
        }
      }
      currentDate.setUTCDate(currentDate.getUTCDate() + 1);
    }

    if (mappedLessons.length === 0) {
      return { success: true, error: false, message: "No lessons in range (no weekdays or all holidays)." };
    }

    const rangeStartISO = rangeStart.toISOString();
    const rangeEndISO = rangeEnd.toISOString();
    const { error: delError } = await supabase
      .from("Lesson")
      .delete()
      .eq("schoolId", template.schoolId)
      .eq("teacherId", template.teacherId)
      .gte("startTime", rangeStartISO)
      .lte("startTime", rangeEndISO);

    if (delError) {
      const errMsg = delError.message || delError.code || "Unknown error";
      console.error("generateLessonsFromTemplate delete error:", delError.code, delError.message, delError.details);
      return { success: false, error: true, message: `Failed clearing existing lessons: ${errMsg}` };
    }

    // Ensure Lesson id sequence is past max(id) so inserts get unique ids (avoids duplicate key after seed_schedule etc.)
    const { error: syncErr } = await (supabase as any).rpc("sync_lesson_id_sequence");
    if (syncErr) {
      console.error("generateLessonsFromTemplate sync sequence error:", syncErr);
      // Continue anyway; insert might still work if sequence is already fine
    }

    const CHUNK_SIZE = 1000;
    for (let i = 0; i < mappedLessons.length; i += CHUNK_SIZE) {
      const chunk = mappedLessons.slice(i, i + CHUNK_SIZE);
      const { error: insertError } = await supabase.from("Lesson").insert(chunk);
      if (insertError) {
        const errMsg = insertError.message || insertError.code || "Unknown error";
        console.error("generateLessonsFromTemplate insert error:", insertError.code, insertError.message, insertError.details);
        return { success: false, error: true, message: `Failed inserting lessons: ${errMsg}` };
      }
    }

    return { success: true, error: false, message: `Generated ${mappedLessons.length} lessons. Existing lessons in this range were replaced.` };
  } catch (err) {
    const message = err instanceof Error ? err.message : "An unexpected error occurred.";
    console.error("generateLessonsFromTemplate unexpected error:", err);
    return { success: false, error: true, message };
  }
};
