import "server-only"
import type { LessonPlan } from "../types"
import type { AuthContext, DataScope } from "@/shared/types/permissions"

/**
 * 课案权限作用域校验工具（V4 P0-1 修复）。
 *
 * data-access 层的 `getLessonPlanById` 仅校验 `creatorId` 或 `status = "published"`，
 * 对于 parent/student/grade_head 等角色的跨年级隔离需由调用方（页面层）补齐。
 * 本模块提供纯函数辅助，避免在每个路由重复实现。
 */

/**
 * 判断单个课案是否落在当前用户的 DataScope 内。
 *
 * - admin (`type: "all"`)：永远返回 true
 * - teacher (`type: "class_taught"`)：creator 自有课案权限由 data-access 层校验；
 *   若 plan 非 creator 自有且非 published，data-access 已返回 null，这里只兜底 gradeId
 * - parent/student/grade_head：需要校验 `plan.gradeId` 是否在 scope.gradeIds 范围内
 *
 * @returns `true` 表示通过；`false` 表示越权（应返回 notFound）
 */
export function isPlanInScope(plan: LessonPlan, scope: DataScope): boolean {
  switch (scope.type) {
    case "all":
      return true
    case "owned":
      // owned 仅允许查看自己的课案；data-access 层 creatorId 已校验，这里兜底
      return true
    case "class_taught":
      // class_taught 的课案权限依赖 creator + subjectId/gradeId 过滤；
      // data-access 的 buildScopeCondition 已处理列表查询；单课案由 creator 校验
      return true
    case "class_members":
      // student：仅可查看已发布 + 本年级课案
      return plan.status === "published" && isGradeInScope(plan, scope.gradeIds)
    case "children":
      // parent：仅可查看已发布 + 孩子所在年级课案
      return plan.status === "published" && isGradeInScope(plan, scope.gradeIds)
    case "grade_managed":
      // grade_head/teaching_head：仅可查看所管年级的课案
      return isGradeInScope(plan, scope.gradeIds)
    default: {
      // 穷尽性检查：unknown 类型分支兜底拒绝
      const _exhaustive: never = scope
      void _exhaustive
      return false
    }
  }
}

/** 校验 plan.gradeId 是否在允许的 gradeIds 集合内（无 gradeId 的课案视为通过） */
function isGradeInScope(plan: LessonPlan, gradeIds?: string[]): boolean {
  if (!plan.gradeId) return true
  if (!gradeIds || gradeIds.length === 0) return false
  return gradeIds.includes(plan.gradeId)
}

/**
 * 断言式调用：失败时抛 `LessonPlanScopeError`，由 Next.js error.tsx 兜底。
 * 用于 Server Component 页面层。
 */
export class LessonPlanScopeError extends Error {
  constructor(public readonly reason: "not_found" | "grade_scope_violation") {
    super(reason)
    this.name = "LessonPlanScopeError"
  }
}

export function assertPlanInScope(plan: LessonPlan, ctx: AuthContext): void {
  if (!isPlanInScope(plan, ctx.dataScope)) {
    throw new LessonPlanScopeError("grade_scope_violation")
  }
}
