import { useLayoutEffect, useMemo, useRef } from 'react';
import * as THREE from 'three';
import { PivotControls } from '@react-three/drei';
import type { Vec3 } from '../types';
import { snapValue, useBuilder } from '../store/builderStore';

/**
 * SelectionGizmo — a translate-only pivot gizmo at the selection centroid.
 * Dragging an axis arrow / plane slider moves every selected part rigidly in
 * all three dimensions (grid-snapped, floor-clamped), as one undo step.
 */
export function SelectionGizmo() {
  const selectedIds = useBuilder((s) => s.selectedIds);
  const parts = useBuilder((s) => s.parts);
  const draggingId = useBuilder((s) => s.draggingId);
  const placingType = useBuilder((s) => s.placingType);
  const tool = useBuilder((s) => s.tool);

  const matrix = useMemo(() => new THREE.Matrix4(), []);
  const draggingRef = useRef(false);
  const startRef = useRef<{ centroid: Vec3; positions: Record<string, Vec3> } | null>(null);

  const selected = selectedIds.map((id) => parts[id]).filter(Boolean);
  const centroid: Vec3 = [0, 0, 0];
  for (const p of selected) {
    centroid[0] += p.position[0] / selected.length;
    centroid[1] += p.position[1] / selected.length;
    centroid[2] += p.position[2] / selected.length;
  }

  // Keep the gizmo parked at the selection centroid whenever it isn't the
  // thing doing the moving (selection changes, undo, inspector edits, ...).
  useLayoutEffect(() => {
    if (!draggingRef.current) matrix.setPosition(centroid[0], centroid[1], centroid[2]);
  });

  if (!selected.length || draggingId || placingType || tool === 'measure') return null;

  const onDragStart = () => {
    draggingRef.current = true;
    const s = useBuilder.getState();
    s.pushHistory();
    s.setGizmoDragging(true);
    const positions: Record<string, Vec3> = {};
    for (const id of s.selectedIds) {
      const p = s.parts[id];
      if (p) positions[id] = [...p.position] as Vec3;
    }
    startRef.current = { centroid: [...centroid] as Vec3, positions };
  };

  const onDrag = (l: THREE.Matrix4) => {
    const start = startRef.current;
    if (!start) return;
    const s = useBuilder.getState();
    matrix.copy(l); // gizmo follows the pointer
    const v = new THREE.Vector3().setFromMatrixPosition(l);
    const snap = (n: number) => (s.snapToGrid ? snapValue(n, s.gridSize) : n);
    const delta: Vec3 = [
      snap(v.x - start.centroid[0]),
      snap(v.y - start.centroid[1]),
      snap(v.z - start.centroid[2]),
    ];
    // Don't let any part of the group sink below the floor.
    let minY = Infinity;
    for (const p of Object.values(start.positions)) minY = Math.min(minY, p[1]);
    if (Number.isFinite(minY)) delta[1] = Math.max(delta[1], -minY);

    const updates: Record<string, Vec3> = {};
    for (const [id, sp] of Object.entries(start.positions)) {
      updates[id] = [sp[0] + delta[0], sp[1] + delta[1], sp[2] + delta[2]];
    }
    s.setPositions(updates);
  };

  const onDragEnd = () => {
    draggingRef.current = false;
    startRef.current = null;
    useBuilder.getState().setGizmoDragging(false);
  };

  return (
    <PivotControls
      matrix={matrix}
      autoTransform={false}
      fixed
      scale={72}
      lineWidth={3}
      depthTest={false}
      disableRotations
      disableScaling
      axisColors={['#f87171', '#4ade80', '#60a5fa']}
      hoveredColor="#fbbf24"
      onDragStart={onDragStart}
      onDrag={onDrag}
      onDragEnd={onDragEnd}
    />
  );
}
