// PostureEcorche — rotatable 3D using the user's smooth mannequin models
// (one dedicated GLB per posture, matching the reference images).
// Falls back to the still image if WebGL/GLB fails. API: <PostureEcorche value onChange />.
const { useEffect: useEffectP, useRef: useRefP, useState: useStateP } = React;

const POSTURES = [
  { id: "P1", label: "Postura correta", desc: "Coluna alinhada", model: "postures/P1.glb", img: "postures/m1.png", view: "side" },
  { id: "P2", label: "Retificação lombar", desc: "Lombar reta demais", model: "postures/P2.glb", img: "postures/m2.png", view: "side" },
  { id: "P3", label: "Hiperlordose lombar", desc: "Curvatura lombar acentuada", model: "postures/P3.glb", img: "postures/m3.png", view: "side" },
  { id: "P4", label: "Hipercifose dorsal", desc: "Costas arredondadas", model: "postures/P4.glb", img: "postures/m4.png", view: "side" },
  { id: "P5", label: "Anteriorização da cabeça", desc: "Cabeça projetada à frente", model: "postures/P5.glb", img: "postures/m5.png", view: "side" },
  { id: "P6", label: "Escoliose", desc: "Curvatura lateral da coluna", model: "postures/P6.glb", img: "postures/m6.png", view: "back" },
];

const PP = {
  accent: "#ef7b60", accentDeep: "#b75746", accentSoft: "rgba(239,123,96,0.10)",
  border: "rgba(74,39,35,0.09)", borderStrong: "rgba(74,39,35,0.16)",
  text: "#4a2723", text2: "#6b4b45", muted: "#806660",
  panel: "#fff0ef", panelAlt: "#fde2d7",
};

async function mountPostureEcorche({ container, onReady, onError, onBusy, initial }) {
  const THREE = window.THREE;
  const { GLTFLoader } = window.GLTF_NS || {};
  if (!THREE || !GLTFLoader) { if (onError) onError(new Error("no_three")); return null; }

  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(28, 1, 0.01, 100);
  camera.position.set(0, 0, 5);

  const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, preserveDrawingBuffer: true });
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  renderer.setClearColor(0x000000, 0);
  renderer.toneMapping = THREE.NoToneMapping;
  renderer.outputColorSpace = THREE.SRGBColorSpace;
  container.appendChild(renderer.domElement);
  renderer.domElement.style.cssText = "width:100%;height:100%;display:block;cursor:grab;touch-action:none;";

  const hemi = new THREE.HemisphereLight(0xffffff, 0xccdedb, 1.25); scene.add(hemi);
  const key = new THREE.DirectionalLight(0xffffff, 0.7); key.position.set(2, 4, 5); scene.add(key);
  const fillL = new THREE.DirectionalLight(0xffffff, 0.5); fillL.position.set(-4, 2, 3); scene.add(fillL);
  const front = new THREE.DirectionalLight(0xffffff, 0.55); front.position.set(0, 1, 6); scene.add(front);
  scene.add(new THREE.AmbientLight(0xffffff, 0.45));

  const modelGroup = new THREE.Group();
  scene.add(modelGroup);

  const modelCache = {};
  let activeUrl = null;
  let modelReady = false;
  let firstLoaded = false;

  const loader = new GLTFLoader();
  if (window.GLTF_NS && window.GLTF_NS.dracoLoader) loader.setDRACOLoader(window.GLTF_NS.dracoLoader);

  // Uniform clean-white "mannequin" surface so all 6 match the reference look.
  function cleanMaterials(model) {
    model.traverse((o) => {
      if (o.isMesh) {
        o.material = new THREE.MeshStandardMaterial({
          color: new THREE.Color(0xeef1f0),
          roughness: 0.8, metalness: 0.0,
          emissive: new THREE.Color(0x70787a), emissiveIntensity: 0.5,
          side: THREE.DoubleSide,
        });
        o.castShadow = false; o.receiveShadow = false;
        o.frustumCulled = false;             // models are re-scaled; avoid bad-bounds culling
        if (o.geometry && o.geometry.computeBoundingSphere) o.geometry.computeBoundingSphere();
      }
    });
  }

  function loadModel(url) {
    if (modelCache[url]) return Promise.resolve(modelCache[url]);
    return new Promise((resolve, reject) => {
      loader.load(url, (gltf) => {
        const model = gltf.scene;
        let mc = 0; model.traverse((o) => { if (o.isMesh) mc++; });
        if (mc === 0) { reject(new Error("empty")); return; }

        const box = new THREE.Box3().setFromObject(model);
        const size = new THREE.Vector3(); box.getSize(size);
        const center = new THREE.Vector3(); box.getCenter(center);
        model.position.sub(center);

        const maxDim = Math.max(size.x, size.y, size.z) || 1;
        const wrapper = new THREE.Group();
        wrapper.add(model);
        wrapper.scale.setScalar(1.9 / maxDim);

        const longestAxis = size.x > size.y && size.x > size.z ? "x" : size.z > size.y ? "z" : "y";
        if (longestAxis === "z") wrapper.rotation.x = -Math.PI / 2;
        else if (longestAxis === "x") wrapper.rotation.z = Math.PI / 2;

        cleanMaterials(model);
        wrapper.visible = false;
        modelGroup.add(wrapper);
        const entry = { wrapper };
        modelCache[url] = entry;
        resolve(entry);
      }, undefined, (err) => { console.error("[posture]", url, err); reject(err); });
    });
  }

  function showModel(url) {
    Object.values(modelCache).forEach((e) => { e.wrapper.visible = false; });
    if (modelCache[url]) modelCache[url].wrapper.visible = true;
    activeUrl = url;
  }

  // ── interaction ──
  let rotation = { x: 0, y: -Math.PI / 2 };
  let targetRot = { x: 0, y: -Math.PI / 2 };
  let dragging = false;
  let lastP = { x: 0, y: 0 };

  function onPointerDown(e) { dragging = true; lastP.x = e.clientX; lastP.y = e.clientY; renderer.domElement.style.cursor = "grabbing"; }
  function onPointerMove(e) {
    if (!dragging) return;
    targetRot.y += (e.clientX - lastP.x) * 0.01;
    targetRot.x += (e.clientY - lastP.y) * 0.005;
    targetRot.x = Math.max(-0.5, Math.min(0.5, targetRot.x));
    lastP.x = e.clientX; lastP.y = e.clientY;
  }
  function onPointerUp() { dragging = false; renderer.domElement.style.cursor = "grab"; }
  renderer.domElement.addEventListener("pointerdown", onPointerDown);
  window.addEventListener("pointermove", onPointerMove);
  window.addEventListener("pointerup", onPointerUp);

  function resize() {
    const w = container.clientWidth, h = container.clientHeight;
    if (!w || !h) return;
    renderer.setSize(w, h, false);
    camera.aspect = w / h; camera.updateProjectionMatrix();
  }
  const ro = new ResizeObserver(resize);
  ro.observe(container);
  resize();

  let raf;
  function animate() {
    raf = requestAnimationFrame(animate);
    rotation.x += (targetRot.x - rotation.x) * 0.12;
    rotation.y += (targetRot.y - rotation.y) * 0.12;
    modelGroup.rotation.x = rotation.x;
    modelGroup.rotation.y = rotation.y;
    renderer.render(scene, camera);
  }
  animate();

  function viewAngle(p) { return p.view === "back" ? Math.PI : -Math.PI / 2; }

  function applyPosture(id, snap) {
    const p = POSTURES.find((x) => x.id === id) || POSTURES[0];
    if (onBusy) onBusy(true);
    targetRot.x = 0;
    targetRot.y = viewAngle(p);
    if (snap) rotation.y = targetRot.y;
    return loadModel(p.model).then(() => {
      showModel(p.model);
      if (!modelReady) { modelReady = true; if (onReady) onReady(); }
    }).catch((e) => { if (onError) onError(e); })
      .finally(() => { if (onBusy) onBusy(false); });
  }

  // initial model (selected posture, or neutral P1)
  applyPosture(initial || "P1", true).then(() => { firstLoaded = true; });

  return {
    setPosture(id) { applyPosture(id, false); },
    dispose() {
      cancelAnimationFrame(raf);
      ro.disconnect();
      renderer.domElement.removeEventListener("pointerdown", onPointerDown);
      window.removeEventListener("pointermove", onPointerMove);
      window.removeEventListener("pointerup", onPointerUp);
      renderer.dispose();
      try { container.removeChild(renderer.domElement); } catch (e) {}
    },
  };
}

function PostureEcorche({ value, onChange }) {
  const ref = useRefP(null);
  const apiRef = useRefP(null);
  const [status, setStatus] = useStateP("loading"); // loading | ready | error
  const [busy, setBusy] = useStateP(false);

  useEffectP(() => {
    let cancelled = false;
    const tryMount = async () => {
      let waited = 0;
      while (!window.THREE || !window.GLTF_NS?.GLTFLoader) {
        if (cancelled) return;
        if (waited > 8000) { setStatus("error"); return; }
        await new Promise((r) => setTimeout(r, 100));
        waited += 100;
      }
      try {
        const api = await mountPostureEcorche({
          container: ref.current,
          initial: value,
          onReady: () => { if (!cancelled) setStatus("ready"); },
          onError: () => { if (!cancelled) setStatus("error"); },
          onBusy: (b) => { if (!cancelled) setBusy(b); },
        });
        if (cancelled && api) api.dispose();
        else apiRef.current = api;
      } catch (e) { console.error(e); if (!cancelled) setStatus("error"); }
    };
    tryMount();
    return () => { cancelled = true; if (apiRef.current) apiRef.current.dispose(); };
  }, []);

  useEffectP(() => { if (apiRef.current && value) apiRef.current.setPosture(value); }, [value]);

  const active = POSTURES.find((p) => p.id === value);
  const isError = status === "error";

  return (
    <div style={{ display: "flex", gap: 18, alignItems: "stretch" }}>
      {/* viewport */}
      <div style={{
        flex: "1 1 auto", minWidth: 280, height: 460, position: "relative",
        borderRadius: 18, overflow: "hidden",
        background: "radial-gradient(ellipse 74% 70% at 50% 44%, #fff0ef 0%, #f9d5bd 100%)",
        border: `1px solid ${PP.border}`,
      }}>
        <div style={{ position: "absolute", inset: 0, background: "radial-gradient(circle at 50% 44%, rgba(239,123,96,0.12), transparent 60%)", pointerEvents: "none" }} />

        {/* 3D canvas host (hidden if WebGL failed) */}
        <div ref={ref} style={{ width: "100%", height: "100%", position: "relative", zIndex: 1, display: isError ? "none" : "block" }} />

        {/* image fallback */}
        {isError && (
          <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", padding: "44px 20px 30px" }}>
            <img src={(active || POSTURES[0]).img} alt={(active || POSTURES[0]).label}
              style={{ maxHeight: "100%", maxWidth: "62%", objectFit: "contain", filter: "drop-shadow(0 22px 26px rgba(20,50,55,0.22))" }} />
          </div>
        )}

        <div style={{ position: "absolute", top: 14, left: 16, fontSize: 11, color: PP.text2, fontWeight: 500, letterSpacing: "0.03em", zIndex: 2 }}>
          {isError ? "Selecione a postura mais parecida com a sua" : "⟳ Arraste para girar · toque numa postura"}
        </div>

        {status === "loading" && (
          <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", color: PP.muted, fontSize: 12, fontWeight: 500, pointerEvents: "none", zIndex: 2 }}>
            <div style={{ textAlign: "center" }}>
              <div style={{ display: "flex", gap: 5, justifyContent: "center", marginBottom: 12 }}>
                {[0, 1, 2].map((i) => (<div key={i} style={{ width: 7, height: 7, borderRadius: 4, background: PP.accent, animation: "pulse 1.2s ease-in-out infinite", animationDelay: `${i * 0.15}s` }} />))}
              </div>
              Carregando modelo 3D
            </div>
          </div>
        )}

        {busy && status === "ready" && (
          <div style={{
            position: "absolute", top: 14, right: 14, display: "flex", alignItems: "center", gap: 8,
            padding: "6px 12px", borderRadius: 999, background: "rgba(255,255,255,0.85)",
            backdropFilter: "blur(12px)", border: `1px solid ${PP.border}`, zIndex: 2,
            fontSize: 10, color: PP.muted, fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase", pointerEvents: "none",
          }}>
            <div style={{ display: "flex", gap: 3 }}>
              {[0, 1, 2].map((i) => (<div key={i} style={{ width: 4, height: 4, borderRadius: 2, background: PP.accent, animation: "pulse 1.2s ease-in-out infinite", animationDelay: `${i * 0.15}s` }} />))}
            </div>
            Trocando
          </div>
        )}

        {active && (
          <div style={{
            position: "absolute", bottom: 14, left: 14, right: 14, zIndex: 2,
            padding: "12px 16px", borderRadius: 14, background: "rgba(255,255,255,0.88)",
            backdropFilter: "blur(12px)", border: `1px solid ${PP.border}`, boxShadow: "0 12px 28px -14px rgba(20,50,55,0.3)",
          }}>
            <div style={{ fontSize: 10.5, color: PP.accentDeep, fontWeight: 700, letterSpacing: "0.12em", marginBottom: 2 }}>{active.id}</div>
            <div style={{ fontSize: 14, color: PP.text, fontWeight: 700 }}>{active.label}</div>
            <div style={{ fontSize: 12, color: PP.muted, marginTop: 1 }}>{active.desc}</div>
          </div>
        )}
      </div>

      {/* posture list */}
      <div style={{ flex: "0 0 244px", display: "flex", flexDirection: "column", gap: 8 }}>
        {POSTURES.map((p) => {
          const sel = value === p.id;
          return (
            <button key={p.id} onClick={() => onChange(p.id)} style={{
              padding: "12px 14px", textAlign: "left",
              background: sel ? PP.accentSoft : PP.panel,
              border: `1.5px solid ${sel ? PP.accent : PP.border}`,
              borderRadius: 13, cursor: "pointer", color: PP.text,
              display: "flex", alignItems: "center", gap: 12,
              transition: "all 0.2s", transform: sel ? "translateY(-1px)" : "none",
              boxShadow: sel ? "0 10px 22px -12px rgba(239,123,96,0.45)" : "none",
            }}>
              <div style={{
                width: 30, height: 30, borderRadius: 9,
                background: sel ? PP.accent : PP.panelAlt, color: sel ? "#fff" : PP.accentDeep,
                display: "grid", placeItems: "center", fontSize: 11.5, fontWeight: 700, flexShrink: 0,
              }}>{p.id}</div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, fontWeight: 600, lineHeight: 1.2 }}>{p.label}</div>
                <div style={{ fontSize: 11, color: PP.muted, marginTop: 2 }}>{p.desc}</div>
              </div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

window.PostureEcorche = PostureEcorche;
