import "server-only"

import { and, asc, desc, eq, inArray, sql, type SQL } from "drizzle-orm"
import { createId } from "@paralleldrive/cuid2"

import { db } from "@/shared/db"
import { leaveRequests } from "@/shared/db/schema"
import { getClassNamesByIds } from "@/modules/classes/data-access"
import { getUserNamesByIds } from "@/modules/users/data-access"
import type { DataScope } from "@/shared/types/permissions"

import type {
  LeaveRequest,
  LeaveRequestListItem,
  LeaveStatus,
  LeaveType,
  PaginatedLeaveResult,
  LeaveQueryParams,
} from "./types"
import type { CreateLeaveRequestInput, ReviewLeaveRequestInput } from "./schema"

/**
 * 构建 dataScope 过滤条件。
 * - all：无过滤
 * - class_taught：仅所辖班级
 * - children：仅子女
 * - class_members：仅本人
 * - owned：仅本人提交
 * - grade_managed：暂不支持（返回 1=0）
 */
function buildScopeFilter(scope: DataScope): SQL | null {
  if (scope.type === "all") return null
  if (scope.type === "class_taught") {
    return scope.classIds.length > 0
      ? inArray(leaveRequests.classId, scope.classIds)
      : sql`1=0`
  }
  if (scope.type === "children") {
    return scope.childrenIds.length > 0
      ? inArray(leaveRequests.studentId, scope.childrenIds)
      : sql`1=0`
  }
  if (scope.type === "class_members") {
    // 学生只看自己的请假
    return sql`1=0`
  }
  if (scope.type === "owned") {
    return eq(leaveRequests.requesterId, scope.userId)
  }
  return sql`1=0`
}

const serializeDate = (d: Date | string | null): string =>
  d ? new Date(d).toISOString().slice(0, 10) : ""

function serializeRequest(row: typeof leaveRequests.$inferSelect): LeaveRequest {
  return {
    id: row.id,
    studentId: row.studentId,
    requesterId: row.requesterId,
    classId: row.classId,
    leaveType: row.leaveType as LeaveType,
    startDate: serializeDate(row.startDate),
    endDate: serializeDate(row.endDate),
    reason: row.reason,
    status: row.status as LeaveStatus,
    reviewerId: row.reviewerId ?? null,
    reviewComment: row.reviewComment ?? null,
    reviewedAt: row.reviewedAt ? row.reviewedAt.toISOString() : null,
    attendanceSynced: row.attendanceSynced,
    createdAt: row.createdAt.toISOString(),
    updatedAt: row.updatedAt.toISOString(),
  }
}

async function enrichListItems(
  rows: typeof leaveRequests.$inferSelect[],
  currentUserId: string,
): Promise<LeaveRequestListItem[]> {
  if (rows.length === 0) return []

  const studentIds = Array.from(new Set(rows.map((r) => r.studentId)))
  const requesterIds = Array.from(new Set(rows.map((r) => r.requesterId)))
  const reviewerIds = Array.from(
    new Set(rows.map((r) => r.reviewerId).filter((id): id is string => id !== null)),
  )
  const classIds = Array.from(new Set(rows.map((r) => r.classId)))

  const [studentMap, requesterMap, reviewerMap, classMap] = await Promise.all([
    getUserNamesByIds(studentIds),
    getUserNamesByIds(requesterIds),
    reviewerIds.length > 0 ? getUserNamesByIds(reviewerIds) : Promise.resolve(new Map()),
    getClassNamesByIds(classIds),
  ])

  // 学生视角下，class_members 的 currentUserId 是学生本人
  void currentUserId

  return rows.map((row) => {
    const base = serializeRequest(row)
    return {
      ...base,
      studentName: studentMap.get(row.studentId)?.name ?? "Unknown",
      className: classMap.get(row.classId) ?? "Unknown",
      requesterName: requesterMap.get(row.requesterId)?.name ?? "Unknown",
      reviewerName: row.reviewerId ? reviewerMap.get(row.reviewerId)?.name ?? null : null,
    }
  })
}

/**
 * 分页查询请假申请列表（按 dataScope 过滤）。
 */
export async function getLeaveRequests(
  params: LeaveQueryParams & { scope: DataScope; currentUserId?: string },
): Promise<PaginatedLeaveResult> {
  const page = Math.max(1, params.page ?? 1)
  const pageSize = Math.max(1, Math.min(50, params.pageSize ?? 20))
  const conditions: SQL[] = []

  const scopeFilter = buildScopeFilter(params.scope)
  if (scopeFilter) conditions.push(scopeFilter)

  // class_members 由调用方传入 currentUserId 时，过滤为本人提交
  if (params.scope.type === "class_members" && params.currentUserId) {
    conditions.push(eq(leaveRequests.requesterId, params.currentUserId))
  }

  if (params.classId) conditions.push(eq(leaveRequests.classId, params.classId))
  if (params.studentId) conditions.push(eq(leaveRequests.studentId, params.studentId))
  if (params.status) conditions.push(eq(leaveRequests.status, params.status))

  const where = conditions.length > 0 ? and(...conditions) : undefined

  const [totalRow, rows] = await Promise.all([
    db.select({ count: sql<number>`count(*)` }).from(leaveRequests).where(where),
    db
      .select()
      .from(leaveRequests)
      .where(where)
      .orderBy(desc(leaveRequests.createdAt))
      .limit(pageSize)
      .offset((page - 1) * pageSize),
  ])

  const total = Number(totalRow[0]?.count ?? 0)
  const items = await enrichListItems(rows, params.currentUserId ?? "")

  return {
    items,
    total,
    page,
    pageSize,
    totalPages: Math.ceil(total / pageSize),
  }
}

/**
 * 获取单条请假申请详情（按 dataScope 校验访问权限）。
 */
export async function getLeaveRequest(
  id: string,
  scope: DataScope,
  currentUserId?: string,
): Promise<LeaveRequestListItem | null> {
  const conditions: SQL[] = [eq(leaveRequests.id, id)]
  const scopeFilter = buildScopeFilter(scope)
  if (scopeFilter) conditions.push(scopeFilter)
  if (scope.type === "class_members" && currentUserId) {
    conditions.push(eq(leaveRequests.requesterId, currentUserId))
  }

  const [row] = await db
    .select()
    .from(leaveRequests)
    .where(and(...conditions))
    .limit(1)

  if (!row) return null
  const items = await enrichListItems([row], currentUserId ?? "")
  return items[0] ?? null
}

/**
 * 校验同一学生在日期范围内是否已有未结束的请假（pending/approved）。
 * 用于提交前校验，避免重复请假。
 */
export async function hasOverlappingLeave(
  studentId: string,
  startDate: string,
  endDate: string,
  excludeId?: string,
): Promise<boolean> {
  const conditions: SQL[] = [
    eq(leaveRequests.studentId, studentId),
    inArray(leaveRequests.status, ["pending", "approved"]),
    sql`${leaveRequests.startDate} <= ${endDate}`,
    sql`${leaveRequests.endDate} >= ${startDate}`,
  ]
  if (excludeId) conditions.push(eq(leaveRequests.id, excludeId))
  const existing = await db
    .select({ id: leaveRequests.id })
    .from(leaveRequests)
    .where(and(...conditions))
    .limit(1)
  return existing.length > 0
}

/**
 * 创建请假申请。
 * 调用方需先校验：1) requester 与 student 关系（家长-子女 或 学生本人）；
 *                  2) classId 确为学生当前所在班级；3) 无日期重叠的未结束请假。
 */
export async function createLeaveRequest(
  data: CreateLeaveRequestInput,
  requesterId: string,
): Promise<string> {
  const id = createId()
  await db.insert(leaveRequests).values({
    id,
    studentId: data.studentId,
    requesterId,
    classId: data.classId,
    leaveType: data.leaveType,
    startDate: new Date(data.startDate),
    endDate: new Date(data.endDate),
    reason: data.reason,
    status: "pending",
  })
  return id
}

/**
 * 审批请假申请（pending → approved/rejected）。
 * 调用方需先校验审批人对该班级的归属（class_taught / all）。
 */
export async function reviewLeaveRequest(
  id: string,
  review: ReviewLeaveRequestInput,
  reviewerId: string,
): Promise<LeaveRequest | null> {
  const result = await db
    .update(leaveRequests)
    .set({
      status: review.status,
      reviewerId,
      reviewComment: review.reviewComment ?? null,
      reviewedAt: new Date(),
    })
    .where(and(eq(leaveRequests.id, id), eq(leaveRequests.status, "pending")))

  if (result[0].affectedRows === 0) return null
  const [row] = await db
    .select()
    .from(leaveRequests)
    .where(eq(leaveRequests.id, id))
    .limit(1)
  if (!row) return null
  return serializeRequest(row)
}

/**
 * 撤销请假申请（仅 pending 状态可撤销，且只有 requester 本人可撤销）。
 */
export async function cancelLeaveRequest(
  id: string,
  requesterId: string,
): Promise<LeaveRequest | null> {
  const result = await db
    .update(leaveRequests)
    .set({ status: "cancelled" })
    .where(
      and(
        eq(leaveRequests.id, id),
        eq(leaveRequests.requesterId, requesterId),
        eq(leaveRequests.status, "pending"),
      ),
    )

  if (result[0].affectedRows === 0) return null
  const [row] = await db
    .select()
    .from(leaveRequests)
    .where(eq(leaveRequests.id, id))
    .limit(1)
  if (!row) return null
  return serializeRequest(row)
}

/**
 * L-5 审批通过后，标记考勤已同步。
 * 由考勤同步逻辑调用，确保不重复同步。
 */
export async function markAttendanceSynced(id: string): Promise<void> {
  await db
    .update(leaveRequests)
    .set({ attendanceSynced: true })
    .where(and(eq(leaveRequests.id, id), eq(leaveRequests.attendanceSynced, false)))
}

/**
 * 获取指定状态下的请假申请（用于审批通过后的考勤同步）。
 */
export async function getLeaveRequestsByIds(
  ids: string[],
): Promise<LeaveRequest[]> {
  if (ids.length === 0) return []
  const rows = await db
    .select()
    .from(leaveRequests)
    .where(inArray(leaveRequests.id, ids))
    .orderBy(asc(leaveRequests.startDate))
  return rows.map(serializeRequest)
}

/**
 * 教师审批待办：按 classIds 统计 pending 数量。
 */
export async function countPendingForClasses(
  classIds: string[],
): Promise<number> {
  if (classIds.length === 0) return 0
  const [row] = await db
    .select({ count: sql<number>`count(*)` })
    .from(leaveRequests)
    .where(
      and(
        inArray(leaveRequests.classId, classIds),
        eq(leaveRequests.status, "pending"),
      ),
    )
  return Number(row?.count ?? 0)
}
