/**
 * 공간적 종속성 검증 유틸리티
 * 
 * 하위 영역이 상위 영역 내부에 배치되는지 검증
 */

export interface SpatialObject {
  id: number;
  position: { x: number; y: number; z: number };
  size: { x: number; y: number; z: number };
  hierarchyLevel: number;
  parentId?: number;
  parentKey?: string; // 외부 DB 키 (데이터 바인딩용)
}

/**
 * 객체 A가 객체 B 안에 포함되는지 확인 (AABB)
 */
export function isContainedIn(child: SpatialObject, parent: SpatialObject): boolean {
  // AABB (Axis-Aligned Bounding Box) 계산
  const childMin = {
    x: child.position.x - child.size.x / 2,
    z: child.position.z - child.size.z / 2,
  };
  const childMax = {
    x: child.position.x + child.size.x / 2,
    z: child.position.z + child.size.z / 2,
  };

  const parentMin = {
    x: parent.position.x - parent.size.x / 2,
    z: parent.position.z - parent.size.z / 2,
  };
  const parentMax = {
    x: parent.position.x + parent.size.x / 2,
    z: parent.position.z + parent.size.z / 2,
  };

  // 자식 객체의 모든 모서리가 부모 객체 내부에 있어야 함 (XZ 평면에서)
  return (
    childMin.x >= parentMin.x &&
    childMax.x <= parentMax.x &&
    childMin.z >= parentMin.z &&
    childMax.z <= parentMax.z
  );
}

/**
 * 드래그 시 부모 영역을 찾아서 검증
 * @param child 드래그 중인 자식 객체
 * @param allObjects 모든 배치된 객체들
 * @param hierarchyLevels 계층 레벨 설정 (1, 2, 3, ...)
 * @returns 유효한 부모 객체 또는 null
 */
export function findValidParent(
  child: SpatialObject,
  allObjects: SpatialObject[],
  hierarchyLevels: number
): SpatialObject | null {
  // 최상위 레벨(레벨 1)은 부모가 없음
  if (child.hierarchyLevel === 1) {
    return null;
  }

  // 부모 레벨 (자신보다 1단계 위)
  const parentLevel = child.hierarchyLevel - 1;

  // 부모 레벨의 모든 객체 중에서 포함하는 객체 찾기
  const possibleParents = allObjects.filter(
    (obj) => obj.hierarchyLevel === parentLevel
  );

  for (const parent of possibleParents) {
    if (isContainedIn(child, parent)) {
      return parent;
    }
  }

  // 포함하는 부모가 없으면 null
  return null;
}

/**
 * 드래그 종료 시 공간적 종속성 검증
 * @param child 드래그 종료된 자식 객체
 * @param allObjects 모든 배치된 객체들
 * @returns { valid: boolean, parent: SpatialObject | null }
 */
export function validateSpatialContainment(
  child: SpatialObject,
  allObjects: SpatialObject[]
): { valid: boolean; parent: SpatialObject | null } {
  // 최상위 레벨은 항상 유효
  if (child.hierarchyLevel === 1) {
    return { valid: true, parent: null };
  }

  const parent = findValidParent(child, allObjects, child.hierarchyLevel);

  return {
    valid: parent !== null,
    parent: parent,
  };
}

/**
 * 부모 객체 이동 시 모든 자식 객체의 위치 재계산
 * @param parent 이동한 부모 객체
 * @param oldPosition 이전 위치
 * @param newPosition 새 위치
 * @param allObjects 모든 배치된 객체들
 * @returns 업데이트된 자식 객체 배열
 */
export function updateChildrenPositions(
  parent: SpatialObject,
  oldPosition: { x: number; y: number; z: number },
  newPosition: { x: number; y: number; z: number },
  allObjects: SpatialObject[]
): SpatialObject[] {
  const delta = {
    x: newPosition.x - oldPosition.x,
    y: newPosition.y - oldPosition.y,
    z: newPosition.z - oldPosition.z,
  };

  // 직계 자식 (부모 ID가 일치하는 객체)
  const directChildren = allObjects.filter(
    (obj) => obj.parentId === parent.id
  );

  // 자식들의 위치 업데이트
  return directChildren.map((child) => ({
    ...child,
    position: {
      x: child.position.x + delta.x,
      y: child.position.y + delta.y,
      z: child.position.z + delta.z,
    },
  }));
}

/**
 * 특정 객체의 모든 하위 자손 찾기 (재귀)
 * @param parentId 부모 객체 ID
 * @param allObjects 모든 배치된 객체들
 * @returns 모든 하위 자손 객체 배열
 */
export function getAllDescendants(
  parentId: number,
  allObjects: SpatialObject[]
): SpatialObject[] {
  const directChildren = allObjects.filter((obj) => obj.parentId === parentId);
  
  let descendants = [...directChildren];
  
  // 재귀적으로 손자, 증손자... 찾기
  for (const child of directChildren) {
    const grandChildren = getAllDescendants(child.id, allObjects);
    descendants = [...descendants, ...grandChildren];
  }
  
  return descendants;
}




