/**
 * 트리 기반 RBAC 권한 해석기 (백엔드 PermResolver.java의 TypeScript 미러).
 * - PermissionsPanel UI에서 셀 상태를 미리 계산해 표시하기 위함.
 * - 백엔드와 동일한 알고리즘이어야 사용자 혼란 없음.
 */

export interface TreeNode {
  rsrcCd: string;
  parentCd: string | null;
  rsrcNm: string;
  rsrcLevel: number;
  sortOrd: number;
  useYn: string;
}

export interface PermRow {
  rsrcCd: string;
  operCd: string;
  grantYn: 'Y' | 'N';
}

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

export type CellState =
  | 'explicit-granted' // ✓ 파란 - 명시적 Y
  | 'explicit-denied'  // — 빨간 - 명시적 N
  | 'inherited-granted' // ✓ 연파랑 - 부모로부터 상속
  | 'forced-denied';   // 회색 비활성 - 부모 READ가 N이라 강제 거부

export function makePermKey(rsrcCd: string, operCd: string) {
  return `${rsrcCd}::${operCd}`;
}

/**
 * 단일 (rsrcCd, operCd) 셀의 상태를 계산.
 *
 * @param rsrcCd 대상 리소스
 * @param operCd 대상 오퍼레이션
 * @param explicit 명시 권한 맵 (key → 'Y'/'N')
 * @param parentEffective 부모 노드의 effective 권한 셋 (resolved 결과)
 * @returns CellState
 */
export function resolveCellState(
  rsrcCd: string,
  operCd: Operation,
  explicit: Map<string, 'Y' | 'N'>,
  parentEffective: Set<string> | null,
): CellState {
  const key = makePermKey(rsrcCd, operCd);
  const explicitVal = explicit.get(key);

  // 부모의 READ가 deny면 모든 작업 강제 deny
  const parentReadDenied = parentEffective !== null && !parentEffective.has('READ');

  if (parentEffective && parentReadDenied && operCd !== 'READ') {
    return 'forced-denied';
  }

  // READ도 부모가 deny면 강제 거부 (부모가 정의되었고 READ가 없다면)
  if (operCd === 'READ' && parentEffective && parentReadDenied) {
    return 'forced-denied';
  }

  if (explicitVal === 'Y') return 'explicit-granted';
  if (explicitVal === 'N') return 'explicit-denied';

  // 명시값 없음 → 부모로부터 상속
  if (parentEffective?.has(operCd)) return 'inherited-granted';

  // 부모가 없거나 부모도 권한 없음
  return 'explicit-denied'; // 미정 = 거부 (시각적으로는 빨간 — 보다는 회색이 적합)
}

/**
 * 단일 역할의 effective 권한 맵을 계산 (resource → granted operations).
 * 백엔드 PermResolver.resolveSingleRole과 동일 로직.
 */
export function resolveSingleRoleEffective(
  treeNodes: TreeNode[],
  perms: PermRow[],
): Map<string, Set<Operation>> {
  const explicit = new Map<string, 'Y' | 'N'>();
  for (const p of perms) {
    explicit.set(makePermKey(p.rsrcCd, p.operCd), p.grantYn);
  }

  // 트리 인덱싱
  const childrenMap = new Map<string | null, TreeNode[]>();
  for (const n of treeNodes) {
    if (n.useYn !== 'Y') continue;
    const arr = childrenMap.get(n.parentCd) ?? [];
    arr.push(n);
    childrenMap.set(n.parentCd, arr);
  }

  const resolved = new Map<string, Set<Operation>>();

  function walk(node: TreeNode, parentEffective: Set<Operation> | null) {
    const effective = new Set<Operation>();

    // READ
    const readState = resolveCellState(node.rsrcCd, 'READ', explicit, parentEffective as Set<string> | null);
    if (readState === 'explicit-granted' || readState === 'inherited-granted') {
      effective.add('READ');
    }

    // 다른 작업: READ가 부여된 경우만
    if (effective.has('READ')) {
      for (const op of ['CREATE', 'UPDATE', 'DELETE', 'EXPORT'] as Operation[]) {
        const st = resolveCellState(node.rsrcCd, op, explicit, parentEffective as Set<string> | null);
        if (st === 'explicit-granted' || st === 'inherited-granted') {
          effective.add(op);
        }
      }
    }

    if (effective.size > 0) resolved.set(node.rsrcCd, effective);

    const children = childrenMap.get(node.rsrcCd) ?? [];
    for (const child of children) walk(child, effective);
  }

  const roots = childrenMap.get(null) ?? [];
  for (const root of roots) walk(root, null);

  return resolved;
}
