import * as THREE from 'three';

/**
 * Registry of the innermost local-frame group of each model in the scene.
 * Measurements are stored in this local frame so they follow the model
 * when the user repositions/rotates it via the "Posicionar" tool.
 */
const localGroups = new Map<string, THREE.Object3D>();

export function registerModelLocalGroup(modelId: string, obj: THREE.Object3D) {
  localGroups.set(modelId, obj);
}

export function unregisterModelLocalGroup(modelId: string, obj?: THREE.Object3D) {
  // Only delete if the registered object matches (avoid race between unmount/mount of the same id)
  const current = localGroups.get(modelId);
  if (!obj || current === obj) localGroups.delete(modelId);
}

export function getModelLocalGroup(modelId: string | null | undefined): THREE.Object3D | undefined {
  if (!modelId) return undefined;
  return localGroups.get(modelId);
}

export function getAllModelLocalGroups(): THREE.Object3D[] {
  return Array.from(localGroups.values());
}

export function worldToModelLocal(
  modelId: string | null | undefined,
  p: { x: number; y: number; z: number },
): { x: number; y: number; z: number } | null {
  const g = getModelLocalGroup(modelId);
  if (!g) return null;
  g.updateWorldMatrix(true, false);
  const v = new THREE.Vector3(p.x, p.y, p.z);
  g.worldToLocal(v);
  return { x: v.x, y: v.y, z: v.z };
}

export function modelLocalToWorld(
  modelId: string | null | undefined,
  p: { x: number; y: number; z: number },
): { x: number; y: number; z: number } | null {
  const g = getModelLocalGroup(modelId);
  if (!g) return null;
  g.updateWorldMatrix(true, false);
  const v = new THREE.Vector3(p.x, p.y, p.z);
  g.localToWorld(v);
  return { x: v.x, y: v.y, z: v.z };
}

export function getModelWorldScaleFactor(modelId: string | null | undefined): number {
  const g = getModelLocalGroup(modelId);
  if (!g) return 1;
  g.updateWorldMatrix(true, false);
  const s = new THREE.Vector3();
  g.getWorldScale(s);
  const factor = (Math.abs(s.x) + Math.abs(s.y) + Math.abs(s.z)) / 3;
  return Number.isFinite(factor) && factor > 1e-6 ? factor : 1;
}
