import { useRef } from 'react';
import { useFrame, useThree } from '@react-three/fiber';
import * as THREE from 'three';
// DEVKIT: remove this import + the fake-input block in useFrame to strip devkit
import { fakeInput, isFakeActive } from '@/devkit/fakeInputStore';
import { useModelStore } from '@/stores/useModelStore';

export interface GrabState {
  /** Analog grip pressure (0..1) */
  gripValue: number;
  /** Analog trigger pressure (0..1) */
  triggerValue: number;
  /** True when grip pressure crosses grab threshold (with hysteresis) */
  isGrabbing: boolean;
  /** World-space matrix of the gripSpace (Touch Plus controller pose) */
  gripWorld: THREE.Matrix4;
  /** True when controller pose is valid this frame */
  hasPose: boolean;
}

export interface ControllerGrabSnapshot {
  left: GrabState;
  right: GrabState;
}

const GRAB_ON = 0.7;
const GRAB_OFF = 0.3;

function makeState(): GrabState {
  return {
    gripValue: 0,
    triggerValue: 0,
    isGrabbing: false,
    gripWorld: new THREE.Matrix4(),
    hasPose: false,
  };
}

/**
 * Polls WebXR input sources every frame and writes Touch Plus controller
 * state (grip analog, trigger analog, grip world pose, isGrabbing with
 * hysteresis) into a stable ref object.
 *
 * The returned ref is mutated in place — callers should also call useFrame()
 * with a *later* registration to read it. Pass a higher `renderPriority` to
 * the consumer's useFrame to ensure ordering.
 */
export function useControllerGrab() {
  const ref = useRef<ControllerGrabSnapshot>({
    left: makeState(),
    right: makeState(),
  });
  const { gl } = useThree();

  useFrame((_state, _delta, frame: XRFrame | undefined) => {
    // DEVKIT: fake-input branch — bypasses real WebXR when SimXR is active.
    if (isFakeActive()) {
      for (const hand of ['left', 'right'] as const) {
        const fake = fakeInput[hand];
        const slot = ref.current[hand];
        slot.gripValue = fake.gripValue;
        slot.triggerValue = fake.triggerValue;
        slot.hasPose = fake.hasPose;
        slot.gripWorld.copy(fake.gripWorld);
        if (!slot.isGrabbing && fake.gripValue > GRAB_ON) {
          slot.isGrabbing = true;
          console.log(`[XR][grab] ▶ ${hand} START (fake v=${fake.gripValue.toFixed(2)})`);
        } else if (slot.isGrabbing && fake.gripValue < GRAB_OFF) {
          slot.isGrabbing = false;
          console.log(`[XR][grab] ◼ ${hand} END (fake)`);
        }
      }
      return;
    }

    if (!frame) return;
    const session = frame.session;
    const referenceSpace = gl.xr.getReferenceSpace();
    if (!session || !referenceSpace) return;

    // Reset hasPose flags
    ref.current.left.hasPose = false;
    ref.current.right.hasPose = false;

    for (const source of session.inputSources) {
      if (source.handedness !== 'left' && source.handedness !== 'right') continue;
      const slot = ref.current[source.handedness];
      const gp = source.gamepad;

      if (gp) {
        // xr-standard mapping: [0]=trigger, [1]=squeeze/grip, [2]=touchpad, [3]=thumbstick
        const trigBtn = gp.buttons[0];
        const gripBtn = gp.buttons[1];
        const gripValue = gripBtn ? (gripBtn.value || (gripBtn.pressed ? 1 : 0)) : 0;
        const trigValue = trigBtn ? (trigBtn.value || (trigBtn.pressed ? 1 : 0)) : 0;
        slot.gripValue = gripValue;
        slot.triggerValue = trigValue;
        // Hysteresis
        if (!slot.isGrabbing && gripValue > GRAB_ON) {
          slot.isGrabbing = true;
          console.log(`[XR][grab] ▶ ${source.handedness} START (v=${gripValue.toFixed(2)})`);
        } else if (slot.isGrabbing && gripValue < GRAB_OFF) {
          slot.isGrabbing = false;
          console.log(`[XR][grab] ◼ ${source.handedness} END`);
        }
      }

      if (source.gripSpace) {
        const pose = frame.getPose(source.gripSpace, referenceSpace);
        if (pose) {
          slot.gripWorld.fromArray(pose.transform.matrix);
          const xrRig = useModelStore.getState().xrRig;
          if (xrRig) {
            slot.gripWorld.premultiply(xrRig.matrixWorld);
          }
          slot.hasPose = true;
        }
      }
    }
  });

  return ref;
}
