/**
 * 트리 구조 기반 권한 해석(Resolution) 유틸.
 *
 * 2차원 모델: 리소스 트리(상속) × 오퍼레이션(RCUD, 플랫)
 *
 * 규칙:
 * 1. 부모 리소스의 READ가 N → 자식의 모든 오퍼레이션 강제 N
 * 2. 해당 (RSRC_CD, OPER_CD) 명시적 레코드 있으면 → 그 값 사용
 * 3. 명시적 레코드 없으면 → 부모의 같은 OPER_CD 상속
 * 4. 최상위까지 없으면 → 기본 N (거부)
 *
 * 키 형식: "rsrcCode::operCd" (더블콜론으로 리소스와 오퍼레이션 구분)
 */

export const OPERATIONS = ['READ', 'CREATE', 'UPDATE', 'DELETE'] as const
export type OperationCode = (typeof OPERATIONS)[number]

export interface PermTreeNode {
  code: string
  parentCode: string | null
  name: string
  description: string | null
  icon: string | null
  level: number
  sortOrder: number
}

/** 리소스::오퍼레이션 키 생성 */
export function makePermKey(rsrcCode: string, operCd: string): string {
  return `${rsrcCode}::${operCd}`
}

/** 키에서 리소스 코드와 오퍼레이션 코드 분리 */
export function parsePermKey(key: string): { rsrcCode: string; operCd: string } {
  const idx = key.indexOf('::')
  return {
    rsrcCode: key.substring(0, idx),
    operCd: key.substring(idx + 2),
  }
}

/**
 * 트리 노드 + 역할별 명시적 권한 → granted된 "rsrc::oper" Set 반환.
 * 다중 역할: 역할별 resolve 후 OR (하나라도 Y면 Y).
 */
export function resolvePermissions(
  treeNodes: PermTreeNode[],
  explicitPermsPerRole: Map<number, Map<string, boolean>>,
): Set<string> {
  const granted = new Set<string>()

  const nodeMap = new Map<string, PermTreeNode>()
  for (const node of treeNodes) {
    nodeMap.set(node.code, node)
  }

  for (const [, explicitPerms] of explicitPermsPerRole) {
    const roleResolved = resolveForSingleRole(treeNodes, nodeMap, explicitPerms)
    for (const key of roleResolved) {
      granted.add(key)
    }
  }

  return granted
}

/**
 * 단일 역할에 대한 권한 해석.
 */
function resolveForSingleRole(
  treeNodes: PermTreeNode[],
  nodeMap: Map<string, PermTreeNode>,
  explicitPerms: Map<string, boolean>,
): Set<string> {
  const effective = new Map<string, boolean>()

  // 레벨 순(0→1→2→...)으로 처리하여 부모 → 자식 순서 보장
  const sorted = [...treeNodes].sort((a, b) => a.level - b.level || a.sortOrder - b.sortOrder)

  for (const node of sorted) {
    // READ 먼저 resolve (CUD는 READ 결과에 의존)
    resolveNodeOper(node, 'READ', explicitPerms, effective)

    // CUD resolve
    for (const oper of OPERATIONS) {
      if (oper === 'READ') continue
      resolveNodeOper(node, oper, explicitPerms, effective)
    }
  }

  const granted = new Set<string>()
  for (const [key, value] of effective) {
    if (value) granted.add(key)
  }
  return granted
}

/**
 * 개별 노드 × 오퍼레이션의 effective 값 계산.
 */
function resolveNodeOper(
  node: PermTreeNode,
  operCd: string,
  explicitPerms: Map<string, boolean>,
  effective: Map<string, boolean>,
): void {
  const key = makePermKey(node.code, operCd)
  if (effective.has(key)) return

  const explicit = explicitPerms.get(key)

  if (node.parentCode === null) {
    // 최상위: 명시적 값 또는 기본 거부
    effective.set(key, explicit ?? false)
    return
  }

  // 부모의 READ 확인 (접근 게이트)
  const parentReadKey = makePermKey(node.parentCode, 'READ')
  const parentReadEffective = effective.get(parentReadKey)

  if (parentReadEffective === false) {
    // 부모 READ 차단 → 모든 오퍼레이션 강제 차단
    effective.set(key, false)
    return
  }

  // 명시적 값 있으면 사용
  if (explicit !== undefined) {
    effective.set(key, explicit)
    return
  }

  // 부모의 같은 오퍼레이션 상속
  const parentOperKey = makePermKey(node.parentCode, operCd)
  const parentOperEffective = effective.get(parentOperKey)
  effective.set(key, parentOperEffective ?? false)
}

/**
 * resolved Set → Record<rsrcCode, operCd[]> 변환 (API 반환용).
 */
export function grantedSetToRecord(granted: Set<string>): Record<string, string[]> {
  const result: Record<string, string[]> = {}
  for (const key of granted) {
    const { rsrcCode, operCd } = parsePermKey(key)
    if (!result[rsrcCode]) result[rsrcCode] = []
    result[rsrcCode].push(operCd)
  }
  return result
}

/**
 * 플랫 노드 배열 → 트리 구조 변환 (프론트엔드 UI용).
 */
export interface PermTreeResponse {
  code: string
  parentCode: string | null
  name: string
  description: string | null
  icon: string | null
  level: number
  sortOrder: number
  children: PermTreeResponse[]
}

export function buildPermTree(nodes: PermTreeNode[]): PermTreeResponse[] {
  const nodeMap = new Map<string, PermTreeResponse>()
  const roots: PermTreeResponse[] = []

  const sorted = [...nodes].sort((a, b) => a.level - b.level || a.sortOrder - b.sortOrder)

  for (const node of sorted) {
    const treeNode: PermTreeResponse = {
      code: node.code,
      parentCode: node.parentCode,
      name: node.name,
      description: node.description,
      icon: node.icon,
      level: node.level,
      sortOrder: node.sortOrder,
      children: [],
    }
    nodeMap.set(node.code, treeNode)

    if (node.parentCode === null) {
      roots.push(treeNode)
    } else {
      const parent = nodeMap.get(node.parentCode)
      if (parent) {
        parent.children.push(treeNode)
      }
    }
  }

  return roots
}
