import "server-only"

import type { DataScope, Role } from "@/shared/types/permissions"

import { getGradesForStaff } from "@/modules/school/data-access"
import {
  getTeacherScopeData,
  getStudentScopeData,
  getGradeIdsForStudentIds,
} from "@/modules/classes/data-access"
import { getChildren } from "@/modules/parent/data-access"

import type { DataScopeRule } from "./data-scope-config"

/**
 * Configuration-driven DataScope rules (P1-5/P1-6 audit fix).
 *
 * Order matters: the first rule whose `resolve` returns a non-null
 * DataScope wins. Rules that query module data-access functions return
 * null when the user has no managed resources, allowing fall-through to
 * the next rule (e.g. a grade_head with no grades falls back to "owned").
 *
 * To add a new role or change scope behaviour, edit this array — no need
 * to touch the resolver logic in shared/lib/auth-guard.ts.
 */
export const DATA_SCOPE_RULES: DataScopeRule[] = [
  // Admin sees everything
  {
    roles: ["admin"],
    resolve: async () => ({ type: "all" }),
  },
  // Grade head / teaching head: can manage their grades
  {
    roles: ["grade_head", "teaching_head"],
    resolve: async (userId) => {
      const managedGrades = await getGradesForStaff(userId)
      if (managedGrades.length === 0) return null
      return {
        type: "grade_managed",
        gradeIds: managedGrades.map((g) => g.id),
      }
    },
  },
  // Teacher: can see their own classes
  {
    roles: ["teacher"],
    resolve: async (userId) => {
      const { classIds, subjectIds } = await getTeacherScopeData(userId)
      return {
        type: "class_taught",
        classIds,
        subjectIds: subjectIds.length > 0 ? subjectIds : undefined,
      }
    },
  },
  // Student: can see data from their enrolled classes
  {
    roles: ["student"],
    resolve: async (userId) => {
      const { classIds, gradeIds } = await getStudentScopeData(userId)
      return {
        type: "class_members",
        classIds,
        gradeIds: gradeIds.length > 0 ? gradeIds : undefined,
      }
    },
  },
  // Parent: can see their children's data
  {
    roles: ["parent"],
    resolve: async (userId) => {
      const children = await getChildren(userId)
      const childrenIds = children.map((c) => c.studentId)
      const gradeIds = await getGradeIdsForStudentIds(childrenIds)
      return {
        type: "children",
        childrenIds,
        gradeIds: gradeIds.length > 0 ? gradeIds : undefined,
      }
    },
  },
]

/**
 * Resolve the DataScope for a user based on their roles.
 *
 * Iterates DATA_SCOPE_RULES in priority order. The first rule whose
 * `resolve` returns a non-null DataScope wins. Falls back to "owned"
 * if no rule matches or all matching rules return null.
 *
 * This function is called by shared/lib/auth-guard.ts via dynamic import,
 * so the shared layer never statically depends on modules/*.
 */
export async function resolveDataScopeFromConfig(
  userId: string,
  roleNames: Role[],
): Promise<DataScope> {
  const roleSet = new Set(roleNames)

  for (const rule of DATA_SCOPE_RULES) {
    const hasRole = rule.roles.some((r) => roleSet.has(r))
    if (!hasRole) continue

    const scope = await rule.resolve(userId)
    if (scope !== null) return scope
  }

  // Fallback: only own data
  return { type: "owned", userId }
}
