import "server-only"

import { cache } from "react"
import { and, asc, eq, inArray } from "drizzle-orm"

import { db } from "@/shared/db"
import { parentStudentRelations } from "@/shared/db/schema"
import {
  getStudentActiveClass,
  getStudentClasses,
  getStudentSchedule,
} from "@/modules/classes/data-access"
import {
  getStudentHomeworkAssignments,
  getStudentExamResults,
} from "@/modules/homework/data-access-student"
import { getStudentDashboardGrades } from "@/modules/homework/stats-service"
import { getStudentGradeSummary } from "@/modules/grades/data-access"
import { getGradeNameById } from "@/modules/school/data-access"
import { getUserBasicInfo, getUserNamesByIds } from "@/modules/users/data-access"
import type {
  ChildBasicInfo,
  ChildDashboardData,
  ChildHomeworkSummaryData,
  ChildScheduleItem,
  ChildWeeklyScheduleItem,
  ParentChildRelation,
  ParentDashboardData,
} from "./types"

type Weekday = 1 | 2 | 3 | 4 | 5 | 6 | 7

const isWeekday = (n: number): n is Weekday => n >= 1 && n <= 7

const toWeekday = (d: Date): Weekday => {
  const day = d.getDay()
  // getDay() returns 0 (Sun) - 6 (Sat); normalize Sunday (0) to 7
  const normalized = day === 0 ? 7 : day
  return isWeekday(normalized) ? normalized : 1
}

export const getChildren = cache(async (parentId: string): Promise<ParentChildRelation[]> => {
  const id = parentId.trim()
  if (!id) return []

  const rows = await db
    .select({
      id: parentStudentRelations.id,
      parentId: parentStudentRelations.parentId,
      studentId: parentStudentRelations.studentId,
      relation: parentStudentRelations.relation,
      createdAt: parentStudentRelations.createdAt,
    })
    .from(parentStudentRelations)
    .where(eq(parentStudentRelations.parentId, id))
    .orderBy(asc(parentStudentRelations.createdAt))

  return rows.map((r) => ({
    id: r.id,
    parentId: r.parentId,
    studentId: r.studentId,
    relation: r.relation,
    createdAt: r.createdAt.toISOString(),
  }))
})

/**
 * 校验家长与子女的关系是否存在，并返回关系类型。
 * 同时按 parentId 与 studentId 过滤，防止跨家庭信息泄露。
 */
export const verifyParentChildRelation = cache(
  async (studentId: string, parentId: string): Promise<string | null> => {
    const [row] = await db
      .select({ relation: parentStudentRelations.relation })
      .from(parentStudentRelations)
      .where(
        and(
          eq(parentStudentRelations.studentId, studentId),
          eq(parentStudentRelations.parentId, parentId),
        ),
      )
      .limit(1)
    return row?.relation ?? null
  },
)

export const getChildBasicInfo = cache(
  async (
    studentId: string,
    relation: string | null = null,
  ): Promise<ChildBasicInfo | null> => {
    const student = await getUserBasicInfo(studentId)

    if (!student) return null

    // gradeName 与 activeClass 相互独立，并行拉取
    const [gradeName, activeClass] = await Promise.all([
      student.gradeId ? getGradeNameById(student.gradeId) : Promise.resolve(null),
      getStudentActiveClass(studentId),
    ])

    return {
      id: student.id,
      name: student.name,
      email: student.email,
      image: student.image,
      gradeName,
      className: activeClass?.className ?? null,
      classId: activeClass?.classId ?? null,
      relation,
    }
  },
)

const buildHomeworkSummary = (
  assignments: Awaited<ReturnType<typeof getStudentHomeworkAssignments>>,
): ChildHomeworkSummaryData => {
  const now = new Date()

  let pendingCount = 0
  let submittedCount = 0
  let gradedCount = 0
  let overdueCount = 0

  for (const a of assignments) {
    if (a.progressStatus === "graded") {
      gradedCount += 1
      continue
    }
    if (a.progressStatus === "submitted") {
      submittedCount += 1
    }
    if (a.progressStatus === "not_started" || a.progressStatus === "in_progress") {
      pendingCount += 1
    }
    if (a.dueAt) {
      const due = new Date(a.dueAt)
      if (due < now && a.progressStatus !== "submitted") {
        overdueCount += 1
      }
    }
  }

  const recentAssignments = assignments
    .toSorted((a, b) => {
      const aDue = a.dueAt ? new Date(a.dueAt).getTime() : Number.POSITIVE_INFINITY
      const bDue = b.dueAt ? new Date(b.dueAt).getTime() : Number.POSITIVE_INFINITY
      return aDue - bDue
    })
    .slice(0, 5)

  return {
    pendingCount,
    submittedCount,
    gradedCount,
    overdueCount,
    recentAssignments,
  }
}

const buildTodaySchedule = (
  schedule: Awaited<ReturnType<typeof getStudentSchedule>>,
): ChildScheduleItem[] => {
  const todayWeekday = toWeekday(new Date())
  return schedule
    .filter((s) => s.weekday === todayWeekday)
    .map((s) => ({
      id: s.id,
      classId: s.classId,
      className: s.className,
      course: s.course,
      startTime: s.startTime,
      endTime: s.endTime,
      location: s.location ?? null,
    }))
    .sort((a, b) => a.startTime.localeCompare(b.startTime))
}

const buildWeeklySchedule = (
  schedule: Awaited<ReturnType<typeof getStudentSchedule>>,
): ChildWeeklyScheduleItem[] => {
  return schedule
    .map((s) => ({
      id: s.id,
      classId: s.classId,
      className: s.className,
      course: s.course,
      startTime: s.startTime,
      endTime: s.endTime,
      location: s.location ?? null,
      weekday: s.weekday,
    }))
    .sort((a, b) =>
      a.weekday === b.weekday
        ? a.startTime.localeCompare(b.startTime)
        : a.weekday - b.weekday,
    )
}

export const getChildDashboardData = cache(
  async (studentId: string, relation: string | null = null): Promise<ChildDashboardData | null> => {
    const basicInfo = await getChildBasicInfo(studentId, relation)
    if (!basicInfo) return null

    const [enrolledClasses, schedule, assignments, gradeTrend, gradeSummary, examResults] = await Promise.all([
      getStudentClasses(studentId),
      getStudentSchedule(studentId),
      getStudentHomeworkAssignments(studentId),
      getStudentDashboardGrades(studentId),
      getStudentGradeSummary(studentId),
      getStudentExamResults(studentId),
    ])

    return {
      basicInfo,
      enrolledClasses,
      todaySchedule: buildTodaySchedule(schedule),
      weeklySchedule: buildWeeklySchedule(schedule),
      homeworkSummary: buildHomeworkSummary(assignments),
      gradeTrend,
      gradeSummary,
      examResults,
    }
  },
)

export const getParentDashboardData = cache(
  async (parentId: string): Promise<ParentDashboardData> => {
    const id = parentId.trim()
    if (!id) return { parentName: null, children: [] }

    const [parentInfo, relations] = await Promise.all([
      getUserNamesByIds([id]),
      getChildren(id),
    ])

    const parentName = parentInfo.get(id)?.name ?? null

    if (relations.length === 0) {
      return { parentName, children: [] }
    }

    const children = await Promise.all(
      relations.map((r) => getChildDashboardData(r.studentId, r.relation)),
    )

    return {
      parentName,
      children: children.filter((c): c is ChildDashboardData => c !== null),
    }
  },
)

/**
 * 获取家长所有子女的轻量列表（id + name），用于详情页头部多子女切换器。
 * 一次批量查询，避免 N+1。
 */
export const getChildNameList = cache(
  async (parentId: string): Promise<Array<{ id: string; name: string | null }>> => {
    const relations = await getChildren(parentId)
    if (relations.length === 0) return []

    const nameMap = await getUserNamesByIds(relations.map((r) => r.studentId))
    return relations.map((r) => ({
      id: r.studentId,
      name: nameMap.get(r.studentId)?.name ?? null,
    }))
  },
)

/**
 * v4-P1-5: 批量查询多个学生的家长 userId 列表。
 * 用于通知场景：报告/成绩发布时同步通知所有相关家长。
 * 返回去重后的 parentId 数组。
 */
export const getParentIdsByStudentIds = cache(
  async (studentIds: string[]): Promise<string[]> => {
    if (studentIds.length === 0) return []
    const rows = await db
      .select({ parentId: parentStudentRelations.parentId })
      .from(parentStudentRelations)
      .where(inArray(parentStudentRelations.studentId, studentIds))
    return Array.from(new Set(rows.map((r) => r.parentId)))
  },
)

/**
 * L-2: 批量查询学生→家长映射（保留关联关系）。
 * 用于考勤通知等需要按家长-学生分组发送的场景，避免 N+1 查询。
 * 返回数组形如 [{ parentId, studentId }]，同一家长多个学生会出现多条。
 */
export const getParentStudentMapByStudentIds = cache(
  async (studentIds: string[]): Promise<Array<{ parentId: string; studentId: string }>> => {
    if (studentIds.length === 0) return []
    const rows = await db
      .select({
        parentId: parentStudentRelations.parentId,
        studentId: parentStudentRelations.studentId,
      })
      .from(parentStudentRelations)
      .where(inArray(parentStudentRelations.studentId, studentIds))
    return rows.map((r) => ({ parentId: r.parentId, studentId: r.studentId }))
  },
)
