import { useEffect, useRef, useState, useMemo, Suspense, Component } from 'react';
import { Canvas, useLoader, useFrame } from '@react-three/fiber';
import { OrbitControls, Grid, ContactShadows, GizmoHelper, GizmoViewcube, Bounds } from '@react-three/drei';
import * as THREE from 'three';
import ArtworkOverlay from '@/components/studio/ArtworkOverlay';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { base44 } from '@/api/base44Client';
import { Layers } from 'lucide-react';
import { Image as ContentImage } from '@/components/ui/image';

const TITANIUM = '#9fd6e6';
const PRINT = '#cfd8e0';
const DEV = import.meta.env?.DEV;
const log = (...a) => DEV && console.log('[Viewer]', ...a);

function detectWebGL() {
  try {
    const c = document.createElement('canvas');
    return !!(window.WebGLRenderingContext && (c.getContext('webgl') || c.getContext('experimental-webgl')));
  } catch { return false; }
}

/**
 * Catches errors thrown inside <Canvas> (failed STL/OBJ/GLB/texture loads,
 * overlay crashes). Renders an in-scene fallback so the Canvas stays alive
 * and the route does not crash.
 */
class SceneErrorBoundary extends Component {
  constructor(p) { super(p); this.state = { failed: false }; }
  static getDerivedStateFromError() { return { failed: true }; }
  componentDidCatch(err, info) { log('scene error (loader/overlay failed):', err, info?.componentStack); }
  render() { return this.state.failed ? (this.props.fallback || null) : this.props.children; }
}

function ModelFallback() {
  return (
    <mesh>
      <boxGeometry args={[2, 2, 2]} />
      <meshBasicMaterial color="#f87171" wireframe transparent opacity={0.6} />
    </mesh>
  );
}

function useMaterial({ wireframe, transparent, opacity, color = TITANIUM }) {
  return useMemo(() => new THREE.MeshStandardMaterial({
    color, metalness: 0.35, roughness: 0.45, wireframe, transparent, opacity, envMapIntensity: 0.8
  }), [wireframe, transparent, opacity, color]);
}

function StlModel({ url, ...m }) {
  const geo = useLoader(STLLoader, url);
  const mat = useMaterial(m);
  const g = useMemo(() => { const x = geo.clone(); x.computeVertexNormals(); return x; }, [geo]);
  useEffect(() => () => { g.dispose(); mat.dispose(); }, [g, mat]);
  return <mesh geometry={g} material={mat} castShadow receiveShadow />;
}
function ObjModel({ url, ...m }) {
  const group = useLoader(OBJLoader, url);
  const mat = useMaterial(m);
  const cloned = useMemo(() => {
    const c = group.clone(true);
    c.traverse((o) => { if (o.isMesh) { o.material = mat; o.castShadow = true; o.receiveShadow = true; } });
    return c;
  }, [group, mat]);
  useEffect(() => () => { mat.dispose(); }, [mat]);
  return <primitive object={cloned} />;
}
function GlbModel({ url, ...m }) {
  const gltf = useLoader(GLTFLoader, url);
  const mat = useMaterial(m);
  const cloned = useMemo(() => {
    const c = gltf.scene.clone(true);
    c.traverse((o) => { if (o.isMesh) { o.material = mat; o.castShadow = true; o.receiveShadow = true; } });
    return c;
  }, [gltf, mat]);
  useEffect(() => () => { mat.dispose(); }, [mat]);
  return <primitive object={cloned} />;
}
function ModelByFormat({ url, format, ...m }) {
  if (format === 'stl') return <StlModel url={url} {...m} />;
  if (format === 'obj') return <ObjModel url={url} {...m} />;
  return <GlbModel url={url} {...m} />;
}

function Zone({ zone, color }) {
  const size = zone?.size || 2.2;
  const pos = zone?.position ? [zone.position.x, zone.position.y, zone.position.z] : [0, 0, 0];
  return <mesh position={pos}><boxGeometry args={[size, size, size]} /><meshBasicMaterial color={color} transparent opacity={0.16} depthWrite={false} /></mesh>;
}
function ProtectedZones({ zones, visibleIds }) {
  if (!Array.isArray(zones) || !zones.length) return null;
  return <>{zones.map((z, i) => (visibleIds === null || (Array.isArray(visibleIds) && visibleIds.includes(i)) ? <Zone key={`p${i}`} zone={z} color="#f87171" /> : null))}</>;
}
function DecorationZones({ zones, show }) {
  if (!show || !Array.isArray(zones) || !zones.length) return null;
  return <>{zones.map((z, i) => <Zone key={`d${i}`} zone={z} color="#22d3ee" />)}</>;
}
function ConnectorMarker({ show }) { if (!show) return null; return <mesh position={[0, -0.05, 0]} rotation={[-Math.PI / 2, 0, 0]}><ringGeometry args={[1.3, 1.55, 48]} /><meshBasicMaterial color="#a78bfa" transparent opacity={0.55} /></mesh>; }

function FloorReticle() {
  return (
    <group position={[0, -2.39, 0]} rotation={[-Math.PI / 2, 0, 0]}>
      <mesh><ringGeometry args={[3.0, 3.04, 96]} /><meshBasicMaterial color="#0e7490" transparent opacity={0.55} /></mesh>
      <mesh><ringGeometry args={[4.2, 4.23, 96]} /><meshBasicMaterial color="#155e75" transparent opacity={0.32} /></mesh>
      <mesh><ringGeometry args={[5.4, 5.42, 96]} /><meshBasicMaterial color="#0e7490" transparent opacity={0.18} /></mesh>
      {Array.from({ length: 12 }).map((_, i) => (
        <mesh key={i} rotation={[0, 0, (i / 12) * Math.PI * 2]} position={[4.2, 0, 0]}>
          <planeGeometry args={[0.12, 0.5]} /><meshBasicMaterial color="#22d3ee" transparent opacity={0.4} />
        </mesh>
      ))}
    </group>
  );
}

const _v = new THREE.Vector3();
function CameraRig({ controls, animating, target }) {
  useFrame(() => {
    const c = controls.current;
    if (!c || !animating.current) return;
    const t = target.current;
    c.object.position.lerp(_v.set(t[0], t[1], t[2]), 0.09);
    c.target.lerp(_v.set(0, 0, 0), 0.09);
    c.update();
    if (c.object.position.distanceTo(_v.set(t[0], t[1], t[2])) < 0.06) animating.current = false;
  });
  return null;
}

function ViewerLights() {
  return (
    <>
      <ambientLight intensity={0.45} />
      <directionalLight position={[6, 9, 6]} intensity={1.1} castShadow shadow-mapSize={[1024, 1024]} />
      <directionalLight position={[-7, 5, -5]} intensity={0.35} color="#5eead4" />
      <pointLight position={[0, 2, 8]} intensity={0.6} color="#22d3ee" distance={20} />
    </>
  );
}

const MODE_OPTIONS = [
  ['studio', 'Studio'], ['wireframe', 'Wireframe'], ['geometry', 'Geometry'],
  ['protected', 'Protected Zones'], ['decoration', 'Decoration Zones'], ['connector', 'Connector Clearance'], ['print', 'Print Preview']
];

// Zone/overlay modes temporarily disabled during viewer stabilisation.
// They read nested cover zone data that can throw when records are incomplete.
const DISABLED_MODES = new Set(['protected', 'decoration', 'connector']);

export default function CoverViewer({
  cover, sourceUrl, mode = 'studio', onModeChange, modes = MODE_OPTIONS,
  layers = [], assets = [], selected = null,
  lockAspect = false, onLiveTransform, onCommitTransform, onSelectLayer
}) {
  const controlsRef = useRef();
  const animating = useRef(false);
  const target = useRef([6, 5, 6]);
  const ctxLossCleanup = useRef(null);
  const [mouse, setMouse] = useState('orbit'); // orbit | pan | zoom
  const [quality, setQuality] = useState({ dpr: [1, 2], shadows: true });
  const [hasWebGL, setHasWebGL] = useState(true);
  useEffect(() => {
    setHasWebGL(detectWebGL());
    base44.auth.me().then(u => {
      const p = u?.preferredGraphicsQuality;
      if (p === 'high') setQuality({ dpr: [1, 2], shadows: true });
      else if (p === 'balanced') setQuality({ dpr: [1, 1.5], shadows: true });
      else if (p === 'basic') setQuality({ dpr: [1, 1], shadows: false });
      else { const cores = navigator.hardwareConcurrency || 4; setQuality({ dpr: [1, cores >= 8 ? 2 : 1.5], shadows: cores >= 4 }); }
    }).catch(() => {});
  }, []);
  // Orthographic temporarily unavailable during viewer stabilisation.
  // Switching cameras previously remounted the whole <Canvas> (via key change);
  // perspective is the only working mode until ortho is re-validated.

  // Fall back to a safe mode if a disabled mode is requested.
  const safeMode = DISABLED_MODES.has(mode) ? 'studio' : mode;

  const url = sourceUrl || cover?.sourceFile;
  const format = (cover?.sourceFormat || 'glb').toLowerCase();
  const wireframe = safeMode === 'wireframe' || safeMode === 'geometry';
  const transparent = safeMode === 'geometry' || safeMode === 'print';
  const opacity = safeMode === 'print' ? 0.42 : transparent ? 0.55 : 1;
  const color = safeMode === 'print' ? PRINT : TITANIUM;
  // Zone overlays disabled during viewer stabilisation.
  const showProtected = false;
  const showDecoration = false;
  const showConnector = false;
  useEffect(() => { log('mount'); return () => log('unmount'); }, []);
  useEffect(() => () => ctxLossCleanup.current?.(), []);
  useEffect(() => {
    if (url) log('model-load start', format, url);
    else log('no source mesh — empty viewport');
  }, [url, format]);

  const preset = (name) => {
    const targets = { front: [0, 0.5, 10], back: [0, 0.5, -10], left: [-10, 0.5, 0.01], right: [10, 0.5, 0.01], top: [0.01, 10, 0.01], iso: [7, 5, 7] };
    target.current = targets[name] || targets.iso;
    animating.current = true;
  };
  const fit = () => { target.current = [7, 5, 7]; animating.current = true; };
  const reset = () => { controlsRef.current?.reset(); };

  const safeLayers = Array.isArray(layers) ? layers : [];
  const safeAssets = Array.isArray(assets) ? assets : [];

  if (!hasWebGL) {
    return (
      <div className="viewport-grid relative grid h-full min-h-[420px] w-full place-items-center overflow-hidden rounded-2xl border border-white/[0.07] text-center">
        <div className="max-w-sm p-6">
          <div className="mx-auto mb-3 grid h-14 w-14 place-items-center rounded-2xl border border-cyan-400/30 bg-cyan-400/10 text-cyan-200"><Layers size={26} /></div>
          <b className="block text-amber-200">3D preview unavailable on this device</b>
          <p className="mt-2 text-sm text-slate-400">We've switched to a 2D preview. You can still place artwork — final geometry is produced by the Geometry Engine.</p>
          {cover?.previewImageUrl && <div className="mt-4 overflow-hidden rounded-xl border border-white/10"><ContentImage src={cover.previewImageUrl} alt={cover.name} fittingType="fill" className="h-40 w-full" /></div>}
          {safeLayers.some(l => l.visible) && <p className="mt-3 text-xs text-cyan-200">{safeLayers.filter(l => l.visible).length} design layer(s) placed</p>}
        </div>
      </div>
    );
  }

  return (
    <div className="viewport-grid relative h-full min-h-[420px] w-full overflow-hidden rounded-2xl border border-white/[0.07]">
      <Canvas
        shadows={quality.shadows}
        dpr={quality.dpr}
        camera={{ position: [6, 5, 6], fov: 42, near: 0.1, far: 1000 }}
        onCreated={({ gl }) => {
          const h = (e) => { e.preventDefault(); log('webgl context loss'); };
          gl.domElement.addEventListener('webglcontextloss', h);
          ctxLossCleanup.current = () => gl.domElement.removeEventListener('webglcontextloss', h);
        }}
      >
        <color attach="background" args={['#070b0f']} />
        <fog attach="fog" args={['#070b0f', 16, 38]} />
        <ViewerLights />

        {/* Model: loader failures caught here so the Canvas survives a bad/missing mesh. */}
        <SceneErrorBoundary key={`model:${format}:${url || 'empty'}`} fallback={<ModelFallback />}>
          <Suspense fallback={null}>
            {url && (
              <Bounds fit clip observe margin={1.25}>
                <ModelByFormat url={url} format={format} wireframe={wireframe} transparent={transparent} opacity={opacity} color={color} />
              </Bounds>
            )}
          </Suspense>
        </SceneErrorBoundary>

        {/* Artwork overlay: per-layer texture failures caught in ArtworkOverlay; this is a second net. */}
        <SceneErrorBoundary>
          <Suspense fallback={null}>
            <ArtworkOverlay
              layers={safeLayers} assets={safeAssets} selected={selected}
              lockAspect={lockAspect}
              decorationZones={cover?.decorationZones}
              onLive={onLiveTransform} onCommit={onCommitTransform} onSelect={onSelectLayer}
            />
          </Suspense>
        </SceneErrorBoundary>

        {/* Zone overlays — kept for restoration, gated off during viewer stabilisation. */}
        {showProtected && <ProtectedZones zones={Array.isArray(cover?.protectedZones) ? cover.protectedZones : []} visibleIds={null} />}
        {showDecoration && <DecorationZones zones={cover?.decorationZones} show={showDecoration} />}
        {showConnector && <ConnectorMarker show={showConnector} />}

        <FloorReticle />
        <ContactShadows position={[0, -2.4, 0]} opacity={0.5} scale={18} blur={2.6} far={6} color="#000000" />
        <Grid args={[40, 40]} cellSize={0.5} sectionSize={2.5} infiniteGrid fadeDistance={30} fadeStrength={1.5} cellColor="#173139" sectionColor="#234a55" />
        <CameraRig controls={controlsRef} animating={animating} target={target} />
        <OrbitControls ref={controlsRef} makeDefault enableDamping dampingFactor={0.08} minDistance={3} maxDistance={22}
          enableRotate={mouse === 'orbit'} enablePan={mouse === 'pan'} enableZoom />
        <GizmoHelper alignment="top-right" margin={[72, 72]}><GizmoViewcube /></GizmoHelper>
      </Canvas>

      {/* Mode tab strip (top-left) — disabled modes labelled unavailable. */}
      <div className="absolute left-3 top-3 flex flex-wrap gap-1 rounded-lg border border-white/10 bg-black/45 p-1 backdrop-blur">
        {(Array.isArray(modes) ? modes : MODE_OPTIONS).map(([k, l]) => {
          const disabled = DISABLED_MODES.has(k);
          const active = safeMode === k;
          return (
            <button
              key={k}
              disabled={disabled}
              onClick={() => !disabled && onModeChange?.(k)}
              title={disabled ? 'Temporarily unavailable during viewer stabilisation' : l}
              className={`rounded-md px-2.5 py-1 text-[11px] font-medium transition ${active ? 'bg-cyan-400/20 text-cyan-100' : disabled ? 'cursor-not-allowed text-slate-600 line-through' : 'text-slate-400 hover:text-white'}`}
            >
              {l}
            </button>
          );
        })}
      </div>

      {/* View controls (right vertical) */}
      <div className="absolute right-3 top-1/2 flex -translate-y-1/2 flex-col gap-1">
        <button onClick={() => setMouse('orbit')} title="Orbit" aria-label="Orbit" aria-pressed={mouse === 'orbit'} className={`tool-rail-btn ${mouse === 'orbit' ? 'active' : ''}`}><span aria-hidden="true">◉</span></button>
        <button onClick={() => setMouse('pan')} title="Pan" aria-label="Pan" aria-pressed={mouse === 'pan'} className={`tool-rail-btn ${mouse === 'pan' ? 'active' : ''}`}><span aria-hidden="true">✜</span></button>
        <button onClick={() => setMouse('zoom')} title="Zoom (scroll)" aria-label="Zoom" aria-pressed={mouse === 'zoom'} className={`tool-rail-btn ${mouse === 'zoom' ? 'active' : ''}`}><span aria-hidden="true">⊕</span></button>
        <span className="my-0.5 h-px w-full bg-white/10" aria-hidden="true" />
        <button onClick={fit} title="Fit" aria-label="Fit to view" className="tool-rail-btn"><span aria-hidden="true">⤢</span></button>
        <button onClick={reset} title="Reset" aria-label="Reset view" className="tool-rail-btn"><span aria-hidden="true">↺</span></button>
        <span className="my-0.5 h-px w-full bg-white/10" aria-hidden="true" />
        {['front', 'back', 'left', 'right', 'top'].map((v) => <button key={v} onClick={() => preset(v)} title={v} aria-label={`${v} view`} className="tool-rail-btn text-[9px] font-semibold uppercase">{v[0]}</button>)}
        <button onClick={() => preset('iso')} title="Iso" aria-label="Isometric view" className="tool-rail-btn text-[9px] font-semibold uppercase">ISO</button>
        <span className="my-0.5 h-px w-full bg-white/10" aria-hidden="true" />
        <button disabled title="Orthographic temporarily unavailable during viewer stabilisation" aria-label="Orthographic (unavailable)" className="tool-rail-btn text-[8px] font-semibold opacity-40">ORT</button>
      </div>

      {/* SHiRE floor watermark */}
      <span className="floor-watermark pointer-events-none absolute bottom-12 left-1/2 -translate-x-1/2 text-[13px]">SHiRE</span>

      {!url && (
        <div className="absolute inset-0 grid place-items-center text-center text-sm text-slate-500">
          <div className="max-w-xs">
            <b className="block text-amber-200">No source mesh attached</b>
            <p className="mt-2">Upload an STL / OBJ / GLB in the Cover Library to load geometry into this viewport.</p>
          </div>
        </div>
      )}

      {cover && (
        <div className="absolute left-3 bottom-3 rounded-lg border border-white/10 bg-black/45 px-3 py-2 text-[11px] backdrop-blur">
          <p className="font-heading font-semibold text-white">{cover.name} <span className="mono text-slate-500">v{cover.version}</span></p>
          <p className="mono mt-0.5 text-[10px] text-slate-400">{cover.sourceFormat?.toUpperCase()} · {cover.limbType} · {cover.side}</p>
        </div>
      )}

      {/* Zone overlays disabled — keep component definitions for later restoration. */}
      {false && showProtected && <span className="rounded border border-red-500/30 bg-red-500/10 px-2 py-0.5 text-red-300">Protected zones</span>}
      {false && showDecoration && <span className="rounded border border-cyan-400/30 bg-cyan-400/10 px-2 py-0.5 text-cyan-200">Decoration zones</span>}
      {false && showConnector && <span className="rounded border border-violet-400/30 bg-violet-400/10 px-2 py-0.5 text-violet-200">Connector clearance</span>}

      <span className="pointer-events-none absolute right-3 bottom-3 sim-badge">Simulation only</span>
      {safeLayers.some((l) => l.visible) && (
        <span className="pointer-events-none absolute left-3 bottom-14 max-w-[70%] rounded border border-amber-400/30 bg-amber-400/10 px-2 py-1 text-[10px] leading-3 text-amber-100 backdrop-blur">Preview placement — final surface projection requires Geometry Engine</span>
      )}
    </div>
  );
}