import { useRef, useState, useCallback, useEffect } from 'react';
import { useFrame } from '@react-three/fiber';
import { useXRHitTest, useXRRequestHitTest, useXRPlanes } from '@react-three/xr';
import { XRSpace } from '@react-three/xr';
import { XRPlaneModel } from '@react-three/xr';
import * as THREE from 'three';

const matrixHelper = new THREE.Matrix4();
const positionHelper = new THREE.Vector3();
const quaternionHelper = new THREE.Quaternion();

interface XRHitTestPlacementProps {
  placementMode: boolean;
  onPlace: (position: THREE.Vector3, quaternion: THREE.Quaternion) => void;
  children: React.ReactNode;
  snapToPlanes?: boolean;
}

/**
 * Uses @react-three/xr useXRHitTest to show a reticle on real-world surfaces.
 * When snapToPlanes is true, snaps to detected planes (floor, table, wall).
 * Tap (select) to place the model at the reticle position.
 */
export function XRHitTestPlacement({ placementMode, onPlace, children, snapToPlanes = true }: XRHitTestPlacementProps) {
  const reticleRef = useRef<THREE.Mesh>(null);
  const [reticlePos, setReticlePos] = useState<THREE.Vector3 | null>(null);
  const [reticleQuat, setReticleQuat] = useState<THREE.Quaternion | null>(null);
  const [placed, setPlaced] = useState(false);
  const [snappedPlaneLabel, setSnappedPlaneLabel] = useState<string | null>(null);
  const prevPlacementMode = useRef(placementMode);
  const placedPosition = useRef(new THREE.Vector3(0, 0, -1.5));
  const placedQuaternion = useRef(new THREE.Quaternion());

  // Get detected planes for snap
  const allPlanes = useXRPlanes();

  // Continuous hit-test from the viewer (camera center)
  useXRHitTest(
    placementMode && !placed
      ? (results, getWorldMatrix) => {
          if (results.length === 0) {
            setReticlePos(null);
            setSnappedPlaneLabel(null);
            return;
          }
          getWorldMatrix(matrixHelper, results[0]);
          positionHelper.setFromMatrixPosition(matrixHelper);
          quaternionHelper.setFromRotationMatrix(matrixHelper);

          // Snap to nearest detected plane if enabled
          if (snapToPlanes && allPlanes.length > 0) {
            const snapResult = findSnapPlane(allPlanes, positionHelper, 0.3);
            if (snapResult) {
              // Project hit point onto detected plane
              const projected = projectOntoPlane(positionHelper, snapResult);
              setReticlePos(projected);
              // Align quaternion with plane normal
              const alignedQuat = getPlaneAlignedQuaternion(snapResult);
              setReticleQuat(alignedQuat);
              setSnappedPlaneLabel(snapResult.semanticLabel ?? snapResult.orientation ?? 'surface');
              return;
            }
          }

          setReticlePos(positionHelper.clone());
          setReticleQuat(quaternionHelper.clone());
          setSnappedPlaneLabel(null);
        }
      : undefined,
    'viewer'
  );

  // Request-based hit test for tap-to-place
  const requestHitTest = useXRRequestHitTest();

  const handleSelect = useCallback(async () => {
    if (!placementMode) return;

    if (reticlePos) {
      placedPosition.current.copy(reticlePos);
      if (reticleQuat) placedQuaternion.current.copy(reticleQuat);
      setPlaced(true);
      onPlace(reticlePos.clone(), reticleQuat?.clone() ?? new THREE.Quaternion());
      const label = snappedPlaneLabel ? ` (${snappedPlaneLabel})` : '';
      console.log(`[HitTest] 📍 Modelo posicionado${label}:`, reticlePos.toArray());
      return;
    }

    // Fallback: on-demand hit test
    if (!requestHitTest) return;
    const result = await requestHitTest('viewer', ['plane', 'mesh']);
    if (!result || result.results.length === 0) return;
    result.getWorldMatrix(matrixHelper, result.results[0]);
    const pos = new THREE.Vector3().setFromMatrixPosition(matrixHelper);
    const quat = new THREE.Quaternion().setFromRotationMatrix(matrixHelper);
    placedPosition.current.copy(pos);
    placedQuaternion.current.copy(quat);
    setPlaced(true);
    onPlace(pos, quat);
    console.log('[HitTest] 📍 Modelo posicionado via requestHitTest:', pos.toArray());
  }, [placementMode, reticlePos, reticleQuat, onPlace, requestHitTest, snappedPlaneLabel]);

  // Update reticle mesh position each frame
  useFrame(() => {
    if (reticleRef.current) {
      if (reticlePos && placementMode && !placed) {
        reticleRef.current.visible = true;
        reticleRef.current.position.copy(reticlePos);
        if (reticleQuat) reticleRef.current.quaternion.copy(reticleQuat);
      } else {
        reticleRef.current.visible = false;
      }
    }
  });

  // Reset placement when user re-enters placement mode
  useEffect(() => {
    if (placementMode && !prevPlacementMode.current) {
      setPlaced(false);
    }
    prevPlacementMode.current = placementMode;
  }, [placementMode]);

  // Reticle color changes when snapped
  const reticleColor = snappedPlaneLabel ? '#3b82f6' : '#22c55e';

  return (
    <>
      {/* Reticle on detected surface */}
      <group ref={reticleRef as any} visible={false}>
        <mesh onClick={handleSelect} pointerEventsType={{ deny: 'grab' as any }}>
          <ringGeometry args={[0.08, 0.1, 32]} />
          <meshBasicMaterial color={reticleColor} side={THREE.DoubleSide} transparent opacity={0.8} />
        </mesh>
        {/* Inner crosshair */}
        <mesh rotation={[-Math.PI / 2, 0, 0]}>
          <ringGeometry args={[0.005, 0.015, 16]} />
          <meshBasicMaterial color={reticleColor} side={THREE.DoubleSide} transparent opacity={0.6} />
        </mesh>
        {/* Snap indicator text - shows plane type */}
        {snappedPlaneLabel && placementMode && !placed && (
          <mesh position={[0, 0.02, 0]}>
            <planeGeometry args={[0.12, 0.03]} />
            <meshBasicMaterial color={reticleColor} transparent opacity={0.6} />
          </mesh>
        )}
      </group>

      {/* Invisible tap plane to catch select events */}
      {placementMode && !placed && (
        <mesh
          position={[0, 0, -2]}
          onClick={handleSelect}
          visible={false}
          pointerEventsType={{ deny: 'grab' as any }}
        >
          <planeGeometry args={[10, 10]} />
          <meshBasicMaterial transparent opacity={0} />
        </mesh>
      )}

      {/* Show detected planes as semi-transparent overlays when in placement mode */}
      {placementMode && !placed && snapToPlanes && allPlanes.map((plane, i) => {
        const label = plane.semanticLabel ?? '';
        const isVertical = plane.orientation === 'vertical';
        let color = '#64748b';
        if (label === 'floor' || label === 'table' || (!isVertical && label !== 'ceiling')) {
          color = '#22c55e';
        } else if (label === 'wall' || isVertical) {
          color = '#3b82f6';
        } else if (label === 'ceiling') {
          color = '#a855f7';
        }

        return (
          <XRSpace key={i} space={plane.planeSpace}>
            <XRPlaneModel plane={plane}>
              <meshBasicMaterial
                color={color}
                transparent
                opacity={0.12}
                side={THREE.DoubleSide}
                depthWrite={false}
              />
            </XRPlaneModel>
          </XRSpace>
        );
      })}

      {/* Placed model or default position */}
      <group
        position={placed ? placedPosition.current : [0, 0, -1.5]}
        quaternion={placed ? placedQuaternion.current : undefined}
      >
        {children}
      </group>
    </>
  );
}

// ─── Helpers ────────────────────────────────────────────

/** Find the nearest plane to a point within maxDistance */
function findSnapPlane(planes: readonly XRPlane[], point: THREE.Vector3, maxDistance: number): XRPlane | null {
  let nearest: XRPlane | null = null;
  let minDist = maxDistance;

  for (const plane of planes) {
    const polygon = plane.polygon;
    if (!polygon || polygon.length === 0) continue;

    // Compute plane center from polygon
    let cx = 0, cy = 0, cz = 0;
    for (const pt of polygon) {
      cx += pt.x; cy += pt.y; cz += pt.z;
    }
    const len = polygon.length;
    const center = new THREE.Vector3(cx / len, cy / len, cz / len);

    const dist = center.distanceTo(point);
    if (dist < minDist) {
      minDist = dist;
      nearest = plane;
    }
  }

  return nearest;
}

/** Project a point onto a plane defined by its polygon */
function projectOntoPlane(point: THREE.Vector3, plane: XRPlane): THREE.Vector3 {
  const polygon = plane.polygon;
  if (!polygon || polygon.length < 3) return point.clone();

  // Get plane normal from first 3 vertices
  const p0 = new THREE.Vector3(polygon[0].x, polygon[0].y, polygon[0].z);
  const p1 = new THREE.Vector3(polygon[1].x, polygon[1].y, polygon[1].z);
  const p2 = new THREE.Vector3(polygon[2].x, polygon[2].y, polygon[2].z);

  const normal = new THREE.Vector3().crossVectors(
    new THREE.Vector3().subVectors(p1, p0),
    new THREE.Vector3().subVectors(p2, p0)
  ).normalize();

  // Project point onto plane
  const toPoint = new THREE.Vector3().subVectors(point, p0);
  const dist = toPoint.dot(normal);
  return point.clone().sub(normal.clone().multiplyScalar(dist));
}

/** Get a quaternion that aligns with the plane's normal direction */
function getPlaneAlignedQuaternion(plane: XRPlane): THREE.Quaternion {
  const polygon = plane.polygon;
  if (!polygon || polygon.length < 3) return new THREE.Quaternion();

  const p0 = new THREE.Vector3(polygon[0].x, polygon[0].y, polygon[0].z);
  const p1 = new THREE.Vector3(polygon[1].x, polygon[1].y, polygon[1].z);
  const p2 = new THREE.Vector3(polygon[2].x, polygon[2].y, polygon[2].z);

  const normal = new THREE.Vector3().crossVectors(
    new THREE.Vector3().subVectors(p1, p0),
    new THREE.Vector3().subVectors(p2, p0)
  ).normalize();

  // Create rotation from up vector to plane normal
  const quat = new THREE.Quaternion();
  quat.setFromUnitVectors(new THREE.Vector3(0, 1, 0), normal);
  return quat;
}
