import { useEffect, useRef, useMemo, useCallback, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { useGLTF, Grid, Html, Line, OrbitControls, Text } from '@react-three/drei';
import { XR, createXRStore, useXR, XROrigin } from '@react-three/xr';
import * as THREE from 'three';
import { useModelStore } from '@/stores/useModelStore';
import { toast } from 'sonner';
import { ArrowLeft, Download, QrCode, Crosshair, Home } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { generateMarkerDownloadURL } from '@/lib/trackingMarker';
import { XRHud } from '@/components/XRHud';
import { ShareButton } from '@/components/ShareButton';
import { XRHudInWorld } from '@/components/three/XRHudInWorld';
import { XRBroadcastMirror } from '@/components/three/XRBroadcastMirror';
import { findNearestVertex } from '@/components/three/SmartMeasure';
import { XRHitTestPlacement } from '@/components/three/XRHitTestPlacement';
import { XRGrabbable } from '@/components/three/XRGrabbable';
// DEVKIT: remove these imports + all `// DEVKIT:` blocks below to strip devkit
import { useDevKit } from '@/devkit/useDevKit';
import { DevPanel } from '@/devkit/DevPanel';
import { FakeControllers } from '@/devkit/FakeControllers';

// --- Diagnóstico XR ---
console.log('[XR] Inicializando store...');
if (navigator.xr) {
  navigator.xr.isSessionSupported('immersive-ar').then((supported) => {
    console.log('[XR] immersive-ar suportado:', supported);
  });
}

const store = createXRStore({
  hand: { left: true, right: true },
  controller: { left: true, right: true },
});

// ─── XRModel ───────────────────────────────────────────
function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').SceneModel }) {
  const { scene: rawScene } = useGLTF(sceneModel.url);
  const scene = useMemo(() => rawScene.clone(true), [rawScene]);
  const ref = useRef<THREE.Group>(null);
  const fineTuning = sceneModel.fineTuning;
  const opacity = useModelStore((s) => s.opacity);
  const renderMode = useModelStore((s) => s.renderMode);
  const wireframeColor = useModelStore((s) => s.wireframeColor);
  const wireframeThickness = useModelStore((s) => s.wireframeThickness);
  const edgeThresholdAngle = useModelStore((s) => s.edgeThresholdAngle);
  const checklist = useModelStore((s) => s.checklist);

  const modelInfo = useMemo(() => {
    const box = new THREE.Box3().setFromObject(scene);
    const size = new THREE.Vector3();
    const center = new THREE.Vector3();
    box.getSize(size);
    box.getCenter(center);
    return { size, center };
  }, [scene]);

  useEffect(() => {
    const hasRejected = checklist.some(i => i.status === 'rejected');
    const allApproved = checklist.every(i => i.status === 'approved');

    scene.traverse((child) => {
      if (child instanceof THREE.Mesh) {
        if (Array.isArray(child.material)) {
          child.material = child.material.map(m => m.clone());
        } else {
          child.material = child.material.clone();
        }

        const toRemove: THREE.Object3D[] = [];
        child.children.forEach(c => {
          if (c.userData.__edgeLine) toRemove.push(c);
        });
        toRemove.forEach(c => {
          if (c instanceof THREE.LineSegments) {
            c.geometry.dispose();
            (c.material as THREE.Material).dispose();
          }
          child.remove(c);
        });

        const materials = Array.isArray(child.material) ? child.material : [child.material];
        materials.forEach((mat: THREE.Material) => {
          if (mat instanceof THREE.MeshStandardMaterial) {
            if (renderMode === 'edges') {
              mat.visible = false;
            } else {
              mat.visible = true;
              mat.transparent = opacity < 1;
              mat.opacity = opacity;
              mat.wireframe = renderMode === 'wireframe';
              if (renderMode === 'wireframe') {
                mat.wireframeLinewidth = wireframeThickness;
                mat.color.set(wireframeColor);
              } else if (hasRejected) {
                mat.color.setHSL(0, 0.7, 0.5);
              } else if (allApproved) {
                mat.color.setHSL(0.38, 0.7, 0.45);
              } else {
                mat.color.set(sceneModel.color);
              }
            }
            mat.needsUpdate = true;
          }
        });

        if (renderMode === 'edges' && child.geometry) {
          const edgesGeo = new THREE.EdgesGeometry(child.geometry, edgeThresholdAngle);
          const lineMat = new THREE.LineBasicMaterial({ color: wireframeColor, linewidth: wireframeThickness });
          const lineSegments = new THREE.LineSegments(edgesGeo, lineMat);
          lineSegments.userData.__edgeLine = true;
          child.add(lineSegments);
        }
      }
    });
  }, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color]);

  const rotXRad = (fineTuning.rotX * Math.PI) / 180;
  const rotYRad = (fineTuning.rotY * Math.PI) / 180;
  const rotZRad = (fineTuning.rotZ * Math.PI) / 180;
  const s = fineTuning.scale ?? 1;
  const scaleRatio = useModelStore((st) => st.scaleRatio);
  const renderFactor = scaleRatio?.factor ?? 1;

  if (!sceneModel.visible) return null;

  return (
    <group scale={[renderFactor, renderFactor, renderFactor]}>
      <group
        ref={ref}
        position={[
          -modelInfo.center.x + fineTuning.posX,
          -modelInfo.center.y + fineTuning.posY,
          -modelInfo.center.z + fineTuning.posZ,
        ]}
        rotation={[rotXRad, rotYRad, rotZRad]}
        scale={[s, s, s]}
      >
        <primitive object={scene} />
      </group>
    </group>
  );
}

/** Renders all NON-active models as static background (placement + grab apply only to active). */
function XRBackgroundModels() {
  const models = useModelStore((s) => s.models);
  const activeId = useModelStore((s) => s.activeModelId);
  return (
    <>
      {models.filter(m => m.id !== activeId).map((m) => (
        <XRModel key={m.id} sceneModel={m} />
      ))}
    </>
  );
}

/** Renders the currently-active model (the one wrapped by grab/placement). */
function XRActiveModel() {
  const models = useModelStore((s) => s.models);
  const activeId = useModelStore((s) => s.activeModelId);
  const active = models.find(m => m.id === activeId);
  if (!active) return null;
  return <XRModel sceneModel={active} />;
}

// ─── ControllerFineTuning ──────────────────────────────
// Joystick for slow numeric adjustments. Disabled while any grip is held
// (grab takes priority). Trigger pressure modulates speed: light = fine,
// strong = fast. Standard VR mapping: left=lateral/forward, right=rotY/height.
function ControllerFineTuning({ freeMove }: { freeMove: boolean }) {
  const { setFineTuning } = useModelStore();
  const session = useXR((s) => s.session);

  useFrame(() => {
    if (!session) return;
    const inputSources = session.inputSources;
    if (!inputSources) return;

    let leftAxes: number[] | null = null;
    let rightAxes: number[] | null = null;
    let leftTrig = 0;
    let rightTrig = 0;
    let gripHeld = false;
    let anyGripHeld = false;

    for (const source of inputSources) {
      const gp = source.gamepad;
      if (!gp) continue;
      const gripBtn = gp.buttons[2];
      const trigBtn = gp.buttons[1];
      const gripVal = gripBtn ? (gripBtn.value || (gripBtn.pressed ? 1 : 0)) : 0;
      if (gripBtn?.pressed) gripHeld = true;
      if (gripVal > 0.3) anyGripHeld = true; // matches grab hysteresis OFF
      const trigVal = trigBtn ? (trigBtn.value || (trigBtn.pressed ? 1 : 0)) : 0;
      if (source.handedness === 'left' && gp.axes.length >= 4) {
        leftAxes = [gp.axes[2], gp.axes[3]];
        leftTrig = trigVal;
      }
      if (source.handedness === 'right' && gp.axes.length >= 4) {
        rightAxes = [gp.axes[2], gp.axes[3]];
        rightTrig = trigVal;
      }
    }

    // Grab takes priority — joystick is disabled while grabbing
    if (anyGripHeld) return;
    // In freeMove mode, joystick always moves. Otherwise requires grip — but
    // grip is reserved for grab now, so freeMove=false essentially disables.
    if (!freeMove && !gripHeld) return;

    const baseStep = 0.001;
    const baseRot = 0.1;
    const deadzone = 0.15;
    // Trigger modulation: 0 → 0.3×, 1 → 3×
    const speedL = 0.3 + leftTrig * 2.7;
    const speedR = 0.3 + rightTrig * 2.7;
    // Quadratic acceleration on thumbstick value
    const curve = (v: number) => Math.sign(v) * v * v;

    const modelStore = useModelStore.getState();
    const ft = { ...modelStore.fineTuning };

    if (leftAxes) {
      // Left: X (horizontal) / Z forward-back (vertical, up = forward = -Z)
      if (Math.abs(leftAxes[0]) > deadzone) ft.posX += curve(leftAxes[0]) * baseStep * speedL;
      if (Math.abs(leftAxes[1]) > deadzone) ft.posZ += curve(leftAxes[1]) * baseStep * speedL;
    }
    if (rightAxes) {
      // Right: rotY (horizontal) / Y height (vertical, up = up)
      if (Math.abs(rightAxes[0]) > deadzone) ft.rotY += curve(rightAxes[0]) * baseRot * speedR;
      if (Math.abs(rightAxes[1]) > deadzone) ft.posY -= curve(rightAxes[1]) * baseStep * speedR;
    }

    setFineTuning(ft);
  });

  return null;
}

// ─── Measurement overlay (reused from ModelViewer) ─────
function XRMeasurementOverlay() {
  const measurements = useModelStore((s) => s.measurements);
  const measurePoints = useModelStore((s) => s.measurePoints);
  const snapPoint = useModelStore((s) => s.snapPoint);
  const measureMode = useModelStore((s) => s.measureMode);

  return (
    <>
      {measureMode && snapPoint && (
        <mesh position={[snapPoint.x, snapPoint.y, snapPoint.z]} rotation={[-Math.PI / 2, 0, 0]}>
          <ringGeometry args={[0.003, 0.005, 24]} />
          <meshBasicMaterial color="#eab308" side={THREE.DoubleSide} />
        </mesh>
      )}
      {measurePoints.length === 1 && (
        <mesh position={[measurePoints[0].x, measurePoints[0].y, measurePoints[0].z]}>
          <sphereGeometry args={[0.003, 16, 16]} />
          <meshBasicMaterial color="#e8a838" />
        </mesh>
      )}
      {measurements.map((m) => {
        const a: [number, number, number] = [m.pointA.x, m.pointA.y, m.pointA.z];
        const b: [number, number, number] = [m.pointB.x, m.pointB.y, m.pointB.z];
        const mid: [number, number, number] = [(a[0]+b[0])/2, (a[1]+b[1])/2, (a[2]+b[2])/2];
        return (
          <group key={m.id}>
            <mesh position={a}><sphereGeometry args={[0.003, 16, 16]} /><meshBasicMaterial color="#22c55e" /></mesh>
            <mesh position={b}><sphereGeometry args={[0.003, 16, 16]} /><meshBasicMaterial color="#22c55e" /></mesh>
            <Line points={[a, b]} color="#22c55e" lineWidth={2} />
            <Html position={mid} center distanceFactor={1} style={{ pointerEvents: 'none' }}>
              <div className="rounded bg-card/95 border border-primary/50 px-2 py-0.5 shadow-lg backdrop-blur-sm">
                <span className="font-mono text-[11px] font-bold text-primary whitespace-nowrap">
                  {m.distanceMM.toFixed(1)} mm
                </span>
              </div>
            </Html>
          </group>
        );
      })}
    </>
  );
}

// ─── Snap handler for XR raycasting ────────────────────
function XRSnapHandler() {
  const { camera, scene, gl } = useThree();
  const measureMode = useModelStore((s) => s.measureMode);
  const setSnapPoint = useModelStore((s) => s.setSnapPoint);
  const addMeasurePoint = useModelStore((s) => s.addMeasurePoint);
  const snapPoint = useModelStore((s) => s.snapPoint);
  const raycaster = useMemo(() => new THREE.Raycaster(), []);
  const mouse = useMemo(() => new THREE.Vector2(), []);

  useEffect(() => {
    if (!measureMode) return;
    const onMove = (e: MouseEvent) => {
      const rect = gl.domElement.getBoundingClientRect();
      mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
      mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
    };
    gl.domElement.addEventListener('pointermove', onMove);
    return () => gl.domElement.removeEventListener('pointermove', onMove);
  }, [gl, mouse, measureMode]);

  useFrame(() => {
    if (!measureMode) { setSnapPoint(null); return; }
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(scene.children, true);
    const hit = intersects.find(i => {
      const obj = i.object;
      if (obj instanceof THREE.GridHelper) return false;
      if (obj instanceof THREE.Mesh && (obj.geometry instanceof THREE.SphereGeometry || obj.geometry instanceof THREE.RingGeometry)) return false;
      if (obj.userData.__edgeLine) return false;
      return obj instanceof THREE.Mesh;
    });
    if (hit && hit.object instanceof THREE.Mesh) {
      const canvas = gl.domElement;
      const snap = findNearestVertex(hit.object, hit.point, camera, { width: canvas.clientWidth, height: canvas.clientHeight }, 10);
      setSnapPoint(snap ? { x: snap.x, y: snap.y, z: snap.z } : null);
    } else {
      setSnapPoint(null);
    }
  });

  // Click to measure
  useEffect(() => {
    if (!measureMode) return;
    const onClick = (e: MouseEvent) => {
      if (snapPoint) {
        addMeasurePoint({ x: snapPoint.x, y: snapPoint.y, z: snapPoint.z });
        return;
      }
      const rect = gl.domElement.getBoundingClientRect();
      mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
      mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
      raycaster.setFromCamera(mouse, camera);
      const intersects = raycaster.intersectObjects(scene.children, true);
      const hit = intersects.find(i => i.object instanceof THREE.Mesh && !i.object.userData.__edgeLine);
      if (hit) addMeasurePoint({ x: hit.point.x, y: hit.point.y, z: hit.point.z });
    };
    gl.domElement.addEventListener('click', onClick);
    return () => gl.domElement.removeEventListener('click', onClick);
  }, [measureMode, snapPoint, gl, camera, scene, raycaster, mouse, addMeasurePoint]);

  useEffect(() => {
    gl.domElement.style.cursor = measureMode ? 'crosshair' : 'grab';
    return () => { gl.domElement.style.cursor = 'grab'; };
  }, [measureMode, gl]);

  return null;
}

// ─── XR Grid ───────────────────────────────────────────
function XRGrid() {
  const showGrid = useModelStore((s) => s.showGrid);
  if (!showGrid) return null;
  return (
    <Grid
      position={[0, -0.09, 0]}
      infiniteGrid
      cellSize={0.01}
      sectionSize={0.1}
      cellThickness={0.5}
      sectionThickness={1}
      cellColor="#334155"
      sectionColor="#475569"
      fadeDistance={5}
      fadeStrength={1}
    />
  );
}

// ImageTrackingAnchor removed — replaced by XRHitTestPlacement

// ─── XRSession Page ────────────────────────────────────
const XRSession = () => {
  const navigate = useNavigate();
  const { model, anchorMode, setAnchorMode } = useModelStore();
  const [inXR, setInXR] = useState(false);
  const [freeMove, setFreeMove] = useState(true);
  const [placementMode, setPlacementMode] = useState(true); // start in placement mode
  const [snapToPlanes, setSnapToPlanes] = useState(true);
  const [allowScale, setAllowScale] = useState(false);
  const [liveCode, setLiveCode] = useState<string | null>(null);
  const [liveViewers, setLiveViewers] = useState(0);
  // DEVKIT: simXR forces in-XR rendering on desktop without a real WebXR session
  const devkit = useDevKit();
  const [simXR, setSimXR] = useState(false);
  const effectiveInXR = inXR || simXR;

  useEffect(() => {
    if (!model) navigate('/');
  }, [model, navigate]);

  useEffect(() => {
    const unsubscribe = store.subscribe((state) => {
      const session = state.session;
      if (session && !inXR) {
        console.log('[XR] ✅ Sessão AR ativa!');
        setInXR(true);
        setAnchorMode('manual');
        toast.success('Sessão AR iniciada!');
        session.addEventListener('end', () => {
          console.log('[XR] ❌ Sessão AR encerrada');
          setInXR(false);
        });
      }
    });
    return unsubscribe;
  }, [inXR, setAnchorMode]);

  const handleDownloadMarker = useCallback(async () => {
    const url = await generateMarkerDownloadURL();
    const a = document.createElement('a');
    a.href = url;
    a.download = 'TrackSteelXR_Marker.png';
    a.click();
    URL.revokeObjectURL(url);
    toast.success('Marcador baixado — Imprima em 15×15cm');
  }, []);

  if (!model) return null;

  return (
    <div className="flex h-screen flex-col bg-background">
      {/* Header */}
      <header className="flex items-center justify-between border-b bg-card px-4 py-3 z-10">
        <div className="flex items-center gap-2">
          <Button variant="ghost" size="icon" onClick={() => navigate('/viewer')} title="Voltar ao Viewer">
            <ArrowLeft className="h-5 w-5" />
          </Button>
          <Button variant="ghost" size="icon" onClick={() => navigate('/')} title="Tela inicial">
            <Home className="h-5 w-5" />
          </Button>
          <h1 className="font-mono text-sm font-semibold text-foreground">
            Modo <span className="text-primary">XR Imersivo</span>
          </h1>
        </div>

        <div className="flex items-center gap-2">
          {!inXR && (
            <>
              <Button variant="outline" size="sm" className="gap-1.5" onClick={handleDownloadMarker}>
                <Download className="h-3.5 w-3.5" />
                <span className="font-mono text-xs">Marcador</span>
              </Button>
              <Button className="gap-2 glow-primary" onClick={() => {
                console.log('[XR] Botão AR clicado');
                store.enterAR().catch((e) => console.error('[XR] enterAR rejeitado:', e));
              }}>
                Iniciar Sessão AR
              </Button>
            </>
          )}
          {inXR && (
            <span className="font-mono text-xs text-primary flex items-center gap-1.5">
              <span className="h-2 w-2 rounded-full bg-primary animate-pulse" />
              XR Ativo
            </span>
          )}
        </div>
      </header>

      {/* 3D Canvas */}
      <div className="flex-1 relative">
        <Canvas
          gl={{ antialias: true, alpha: true, powerPreference: 'high-performance', preserveDrawingBuffer: true }}
          camera={{ position: [1.5, 1.2, 1.5], fov: 50, near: 0.01, far: 100 }}
          className="!bg-transparent"
          onCreated={({ gl, invalidate }) => {
            gl.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
            gl.setClearColor(0x000000, 0);
            const loop = () => { invalidate(); requestAnimationFrame(loop); };
            loop();
          }}
        >
          <XR store={store}>
            <ambientLight intensity={0.8} />
            <directionalLight position={[3, 5, 3]} intensity={1.2} />
            <directionalLight position={[-3, 3, -3]} intensity={0.4} />

            <XROrigin />

            {effectiveInXR ? (
              <>
                {/* In XR (or SimXR): all models share the same placement origin so
                    switching active doesn't make others appear to "disappear".
                    Only the ACTIVE model receives grab transforms. */}
                {simXR ? (
                  <group position={[0, 1.0, -1.2]}>
                    <XRBackgroundModels />
                    <XRGrabbable
                      allowScale={allowScale}
                      lockedActive={isActiveLocked}
                      onGrabStart={() => { if (placementMode) setPlacementMode(false); }}
                    >
                      <XRActiveModel />
                    </XRGrabbable>
                  </group>
                ) : (
                  <XRHitTestPlacement
                    placementMode={placementMode}
                    snapToPlanes={snapToPlanes}
                    onPlace={() => {
                      setPlacementMode(false);
                      toast.success('Modelo posicionado na superfície!');
                    }}
                  >
                    <XRBackgroundModels />
                    <XRGrabbable
                      allowScale={allowScale}
                      lockedActive={isActiveLocked}
                      onGrabStart={() => { if (placementMode) setPlacementMode(false); }}
                    >
                      <XRActiveModel />
                    </XRGrabbable>
                  </XRHitTestPlacement>
                )}

                <XRMeasurementOverlay />
                <ControllerFineTuning freeMove={freeMove} />
                

                {/* In-world HUD — visible inside passthrough where DOM overlays cannot reach */}
                <XRHudInWorld
                  freeMove={freeMove}
                  onToggleFreeMove={() => setFreeMove(!freeMove)}
                  placementMode={placementMode}
                  onTogglePlacement={() => setPlacementMode(!placementMode)}
                  snapToPlanes={snapToPlanes}
                  onToggleSnap={() => setSnapToPlanes(!snapToPlanes)}
                  allowScale={allowScale}
                  onToggleAllowScale={() => setAllowScale(!allowScale)}
                  liveCode={liveCode}
                  liveViewers={liveViewers}
                  onStartLive={() => (window as unknown as { __trackSteelStartLive?: () => void }).__trackSteelStartLive?.()}
                  onStopLive={() => (window as unknown as { __trackSteelStopLive?: () => void }).__trackSteelStopLive?.()}
                />

                {/* Opaque mirror canvas used as the WebRTC source while in AR */}
                <XRBroadcastMirror enabled />

                {/* DEVKIT: fake controllers visible in scene + keyboard driver */}
                {devkit && simXR && <FakeControllers />}
                {/* DEVKIT: orbit camera in SimXR so you can navigate around the model */}
                {simXR && (
                  <OrbitControls
                    makeDefault
                    enableDamping
                    dampingFactor={0.1}
                    minDistance={0.05}
                    maxDistance={50}
                    target={[0, 1.0, -1.2]}
                  />
                )}
              </>
            ) : (
              <>
                {/* Desktop preview (no headset): show all models with OrbitControls */}
                <group position={[0, 0, 0]}>
                  <XRBackgroundModels />
                  <XRActiveModel />
                </group>
                <XRMeasurementOverlay />
                <OrbitControls
                  makeDefault
                  enableDamping
                  dampingFactor={0.1}
                  minDistance={0.05}
                  maxDistance={50}
                  target={[0, 0, 0]}
                />
              </>
            )}

            <XRSnapHandler />
            <XRGrid />
          </XR>
        </Canvas>

        {/* Floating DOM HUD overlay — only visible OUTSIDE passthrough.
            Inside AR, the DOM is occluded by the headset compositor; the
            in-world XRHudInWorld replaces it. */}
        <div style={{ display: inXR ? 'none' : 'contents' }}>
          <XRHud
            freeMove={freeMove}
            onToggleFreeMove={() => setFreeMove(!freeMove)}
            placementMode={placementMode}
            onTogglePlacement={() => setPlacementMode(!placementMode)}
            snapToPlanes={snapToPlanes}
            onToggleSnap={() => setSnapToPlanes(!snapToPlanes)}
            allowScale={allowScale}
            onToggleAllowScale={() => setAllowScale(!allowScale)}
          />
        </div>

        {/* Hidden ShareButton instance — registers window.__trackSteelStartLive
            so the in-world Share tab can trigger broadcasts. Also keeps live
            state in sync with the in-XR HUD. */}
        <div style={{ display: 'none' }}>
          <ShareButton
            onHandleChange={(h) => setLiveCode(h?.code ?? null)}
            onViewerCountChange={setLiveViewers}
          />
        </div>

        {/* DEVKIT: floating diagnostic panel + SimXR toggle */}
        {devkit && (
          <DevPanel simXR={simXR} onToggleSimXR={() => setSimXR((v) => !v)} />
        )}
      </div>
    </div>
  );
};

export default XRSession;
