"use server"

import { revalidatePath } from "next/cache"

import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"

import { getUserNamesByIds } from "@/modules/users/data-access"

import {
  getSchedulingRules,
  upsertSchedulingRules,
  getScheduleChanges,
  createScheduleChange,
  updateScheduleChangeStatus,
  getClassConflicts,
  getClassroomsForScheduling,
  getClassSubjectsForScheduling,
  replaceClassSchedule,
} from "./data-access"
import { autoSchedule, buildDefaultTimeSlots } from "./auto-scheduler"
import {
  SchedulingRuleSchema,
  ScheduleChangeSchema,
  AutoScheduleParamsSchema,
} from "./schema"
import type {
  AutoScheduleParams,
  AutoScheduleResult,
  ScheduleChangeListItem,
  ScheduleConflict,
  ScheduleChangeQueryParams,
} from "./types"

export async function saveSchedulingRulesAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  try {
    await requirePermission(Permissions.SCHEDULE_ADJUST)

    const parsed = SchedulingRuleSchema.safeParse({
      classId: formData.get("classId"),
      maxDailyHours: formData.get("maxDailyHours") || undefined,
      maxContinuousHours: formData.get("maxContinuousHours") || undefined,
      lunchBreakStart: formData.get("lunchBreakStart") || undefined,
      lunchBreakEnd: formData.get("lunchBreakEnd") || undefined,
      morningStart: formData.get("morningStart") || undefined,
      afternoonEnd: formData.get("afternoonEnd") || undefined,
      avoidBackToBack: formData.get("avoidBackToBack") === "true",
      balancedSubjects: formData.get("balancedSubjects") === "true",
    })

    if (!parsed.success) {
      return {
        success: false,
        message: "Invalid form data",
        errors: parsed.error.flatten().fieldErrors,
      }
    }

    const id = await upsertSchedulingRules(parsed.data)
    revalidatePath("/admin/scheduling/rules")
    return { success: true, message: "Scheduling rules saved", data: id }
  } catch (e) {
    if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
    if (e instanceof Error) return { success: false, message: e.message }
    return { success: false, message: "Unexpected error" }
  }
}

export async function autoScheduleAction(
  prevState: ActionState<AutoScheduleResult> | null,
  formData: FormData
): Promise<ActionState<AutoScheduleResult>> {
  try {
    await requirePermission(Permissions.SCHEDULE_AUTO)

    const classId = String(formData.get("classId") ?? "").trim()
    if (!classId) return { success: false, message: "Class is required" }

    // Load rules for class (or global fallback)
    const rulesRows = await getSchedulingRules(classId)
    const rules = rulesRows.find((r) => r.classId === classId) ?? rulesRows.find((r) => r.classId === null)
    if (!rules) return { success: false, message: "No scheduling rules found. Please configure rules first." }

    // Load subjects + teacher assignments for this class
    const subjectRows = await getClassSubjectsForScheduling(classId)
    if (subjectRows.length === 0) {
      return { success: false, message: "No subjects assigned to this class" }
    }

    // Default weekly hours: 5 per subject (configurable in future)
    const subjectsInput = subjectRows.map((r) => ({
      subjectId: r.subjectId,
      subjectName: r.subjectName,
      weeklyHours: 5,
      teacherId: r.teacherId ?? null,
    }))

    // Load teachers
    const teacherIds = Array.from(
      new Set(subjectRows.map((r) => r.teacherId).filter((v): v is string => v !== null))
    )
    const teacherMap = teacherIds.length > 0 ? await getUserNamesByIds(teacherIds) : new Map()
    const teachersInput = teacherIds.map((id) => ({ id, name: teacherMap.get(id)?.name ?? "Unknown" }))

    // Load classrooms
    const classroomRows = await getClassroomsForScheduling()
    const classroomsInput = classroomRows.map((c) => ({ id: c.id, name: c.name }))

    // Build default time slots: Mon-Fri, 4 morning + 4 afternoon sessions
    const timeSlots = buildDefaultTimeSlots(rules.morningStart, rules.afternoonEnd, rules.lunchBreakStart, rules.lunchBreakEnd)

    const params: AutoScheduleParams = {
      classId,
      rules,
      subjects: subjectsInput,
      teachers: teachersInput,
      classrooms: classroomsInput,
      timeSlots,
    }

    const parsed = AutoScheduleParamsSchema.safeParse(params)
    if (!parsed.success) {
      return { success: false, message: "Invalid scheduling parameters", errors: parsed.error.flatten().fieldErrors }
    }

    const result = autoSchedule(params)
    return { success: true, message: `Scheduled ${result.scheduledCount} sessions, ${result.conflictCount} conflicts`, data: result }
  } catch (e) {
    if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
    if (e instanceof Error) return { success: false, message: e.message }
    return { success: false, message: "Unexpected error" }
  }
}

export async function applyAutoScheduleAction(
  classId: string,
  schedules: Array<{
    weekday: number
    startTime: string
    endTime: string
    course: string
    location: string | null
  }>
): Promise<ActionState<number>> {
  try {
    await requirePermission(Permissions.SCHEDULE_AUTO)

    if (!classId) return { success: false, message: "Class is required" }
    if (!Array.isArray(schedules) || schedules.length === 0) {
      return { success: false, message: "No schedules to apply" }
    }

    // Replace existing schedule for the class via unified write entry point
    await replaceClassSchedule(
      classId,
      schedules.map((s) => ({
        classId,
        weekday: s.weekday,
        startTime: s.startTime,
        endTime: s.endTime,
        course: s.course,
        location: s.location ?? null,
      }))
    )

    revalidatePath("/admin/scheduling/auto")
    revalidatePath("/teacher/classes/schedule")
    return { success: true, message: `Applied ${schedules.length} schedule items`, data: schedules.length }
  } catch (e) {
    if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
    if (e instanceof Error) return { success: false, message: e.message }
    return { success: false, message: "Unexpected error" }
  }
}

export async function requestScheduleChangeAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  try {
    const ctx = await requirePermission(Permissions.SCHEDULE_ADJUST)

    const parsed = ScheduleChangeSchema.safeParse({
      classId: formData.get("classId"),
      originalScheduleId: formData.get("originalScheduleId") || undefined,
      originalTeacherId: formData.get("originalTeacherId") || undefined,
      substituteTeacherId: formData.get("substituteTeacherId") || undefined,
      originalDate: formData.get("originalDate") || undefined,
      newDate: formData.get("newDate") || undefined,
      newStartTime: formData.get("newStartTime") || undefined,
      newEndTime: formData.get("newEndTime") || undefined,
      reason: formData.get("reason"),
    })

    if (!parsed.success) {
      return {
        success: false,
        message: "Invalid form data",
        errors: parsed.error.flatten().fieldErrors,
      }
    }

    const id = await createScheduleChange(parsed.data, ctx.userId)
    revalidatePath("/teacher/schedule-changes")
    revalidatePath("/admin/scheduling/changes")
    return { success: true, message: "Schedule change request submitted", data: id }
  } catch (e) {
    if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
    if (e instanceof Error) return { success: false, message: e.message }
    return { success: false, message: "Unexpected error" }
  }
}

export async function approveScheduleChangeAction(changeId: string): Promise<ActionState> {
  try {
    const ctx = await requirePermission(Permissions.SCHEDULE_AUTO)
    if (!changeId) return { success: false, message: "Missing change id" }

    await updateScheduleChangeStatus(changeId, "approved", ctx.userId)
    revalidatePath("/admin/scheduling/changes")
    revalidatePath("/teacher/schedule-changes")
    return { success: true, message: "Schedule change approved" }
  } catch (e) {
    if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
    if (e instanceof Error) return { success: false, message: e.message }
    return { success: false, message: "Unexpected error" }
  }
}

export async function rejectScheduleChangeAction(
  changeId: string,
  reason?: string
): Promise<ActionState> {
  try {
    const ctx = await requirePermission(Permissions.SCHEDULE_AUTO)
    if (!changeId) return { success: false, message: "Missing change id" }

    await updateScheduleChangeStatus(changeId, "rejected", ctx.userId)
    revalidatePath("/admin/scheduling/changes")
    revalidatePath("/teacher/schedule-changes")
    return { success: true, message: reason ? `Rejected: ${reason}` : "Schedule change rejected" }
  } catch (e) {
    if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
    if (e instanceof Error) return { success: false, message: e.message }
    return { success: false, message: "Unexpected error" }
  }
}

export async function getScheduleChangesAction(
  params: ScheduleChangeQueryParams
): Promise<ActionState<ScheduleChangeListItem[]>> {
  try {
    await requirePermission(Permissions.SCHEDULE_ADJUST)
    const items = await getScheduleChanges(params)
    return { success: true, data: items }
  } catch (e) {
    if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
    if (e instanceof Error) return { success: false, message: e.message }
    return { success: false, message: "Unexpected error" }
  }
}

export async function getClassConflictsAction(
  classId: string
): Promise<ActionState<ScheduleConflict[]>> {
  try {
    await requirePermission(Permissions.SCHEDULE_ADJUST)
    if (!classId) return { success: false, message: "Class is required" }
    const conflicts = await getClassConflicts(classId)
    return { success: true, data: conflicts }
  } catch (e) {
    if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
    if (e instanceof Error) return { success: false, message: e.message }
    return { success: false, message: "Unexpected error" }
  }
}
