import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
import { ShieldAlert, RotateCcw } from 'lucide-react';
import { createBabylonContext, destroyBabylonContext } from './BabylonSceneLifecycle';
import { applyViewPreset } from './BabylonCameraController';
import { buildProsthesis } from './ProceduralProsthesisV2';
import { buildMeasurementStations } from './MeasurementStationsV2';
import { buildConceptualCover } from './ConceptualCoverV2';
import { buildBranding } from './BrandingLayerV2';
import { buildDesignPreview } from './DesignPreviewLayer';
import { buildOssiguardConnector } from './OssiguardConnectorLayer';
import { buildHumanReference } from './HumanReferenceV2';
import { createSelectionManager } from './BabylonSelectionManager';
import DomLabelLayerV2 from './DomLabelLayerV2';
import { VIEWPORT_V2_BUILD_ID, VIEWPORT_V2_LABEL, BABYLON_VERSION } from './viewportV2Build';

const msg = (e) => (e && e.message ? e.message : String(e));

export default function BabylonViewportV2({
  viewCommand,
  compatibility = false,
  mode = 'cover',
  showAllLabels = false,
  humanReference = false,
  geometry = null,
  designConfig = null,
  measurementStations = null,
  transparency = 1,
  ossiguardProfile = null,
  showOssiguard = true,
  onSelectionChanged = null,
  onDiagnostics = null,
}) {
  const canvasRef = useRef(null);
  const containerRef = useRef(null);
  const ctxRef = useRef(null);
  const prosthesisRef = useRef(null);
  const stationsRef = useRef(null);
  const coverRef = useRef(null);
  const brandingRef = useRef(null);
  const designRef = useRef(null);
  const ossiguardRef = useRef(null);
  const humanRefRef = useRef(null);
  const selectionRef = useRef(null);
  const roRef = useRef(null);
  const resizeHandlerRef = useRef(null);
  const destroyedRef = useRef(false);

  const [attempt, setAttempt] = useState(0);
  const [engineError, setEngineError] = useState(null);
  const [layerErrors, setLayerErrors] = useState({ prosthesis: null, stations: null, cover: null, branding: null, design: null, ossiguard: null });
  const [labels, setLabels] = useState([]);
  const [selectedId, setSelectedId] = useState(null);

  // Refs mirror state so the diagnostics interval always reads fresh values.
  const compatRef = useRef(compatibility);
  const layerErrorsRef = useRef(layerErrors);
  const engineErrorRef = useRef(engineError);
  useEffect(() => { compatRef.current = compatibility; }, [compatibility]);
  useEffect(() => { layerErrorsRef.current = layerErrors; }, [layerErrors]);
  useEffect(() => { engineErrorRef.current = engineError; }, [engineError]);

  const buildDiagnostics = useCallback(() => {
    const ctx = ctxRef.current;
    if (!ctx) return null;
    const { scene, camera, canvas } = ctx;
    const compat = compatRef.current;
    const errs = layerErrorsRef.current;
    const activeLayers = [];
    if (prosthesisRef.current) activeLayers.push('Prosthesis');
    if (stationsRef.current && !compat) activeLayers.push('Measurement stations');
    if (coverRef.current && !compat) activeLayers.push('Conceptual cover');
    if (brandingRef.current && !compat) activeLayers.push('Branding');
    if (designRef.current && !compat) activeLayers.push('Design preview');
    if (ossiguardRef.current && !compat) activeLayers.push('Ossiguard connector');
    const optionalError = errs.prosthesis || errs.stations || errs.cover || errs.branding || errs.design || errs.ossiguard || null;
    return {
      implementation: VIEWPORT_V2_LABEL,
      babylonVersion: BABYLON_VERSION,
      engineInstances: 1,
      sceneInstances: 1,
      canvasInstances: 1,
      activeCamera: 'ArcRotateCamera',
      controlsAttached: !!(camera && canvas && canvas.isConnected),
      canvasConnected: !!(canvas && canvas.isConnected),
      canvasWidth: canvas ? canvas.clientWidth : 0,
      canvasHeight: canvas ? canvas.clientHeight : 0,
      renderLoopActive: !destroyedRef.current,
      resizeObservers: roRef.current ? 1 : 0,
      meshCount: scene ? scene.meshes.length : 0,
      activeLayers,
      prosthesisLoaded: !!prosthesisRef.current,
      coreError: engineErrorRef.current || null,
      optionalError,
      buildId: VIEWPORT_V2_BUILD_ID,
    };
  }, []);

  // --- Engine + scene init: runs once per attempt. Cleanup order is strict. ---
  useEffect(() => {
    destroyedRef.current = false;
    const canvas = canvasRef.current;
    let ctx = null;
    try {
      ctx = createBabylonContext(canvas);
    } catch (e) {
      setEngineError(msg(e));
      ctx = null;
    }
    if (!ctx) {
      if (!engineErrorRef.current) setEngineError('Canvas not ready or zero-size');
      return;
    }
    ctxRef.current = ctx;
    const { engine, scene } = ctx;
    const compat = compatRef.current;

    const errs = { prosthesis: null, stations: null, cover: null, branding: null, design: null, ossiguard: null };
    const allLabels = [];

    // Each optional layer is isolated — failure disables only that layer.
    try {
      const p = buildProsthesis(scene, geometry);
      prosthesisRef.current = p;
      allLabels.push(...p.labels);
    } catch (e) { errs.prosthesis = msg(e); }

    if (!compat) {
      try {
        const s = buildMeasurementStations(scene, measurementStations);
        stationsRef.current = s;
        allLabels.push(...s.labels);
      } catch (e) { errs.stations = msg(e); }
      try {
        const c = buildConceptualCover(scene, geometry, designConfig);
        coverRef.current = c;
        allLabels.push(c.label);
      } catch (e) { errs.cover = msg(e); }
      try {
        const b = buildBranding(scene, designConfig || {});
        brandingRef.current = b;
        allLabels.push(b.label);
      } catch (e) { errs.branding = msg(e); }
      try {
        const d = buildDesignPreview(scene, geometry, designConfig);
        designRef.current = d;
        allLabels.push(d.label);
      } catch (e) { errs.design = msg(e); }
      try {
        if (showOssiguard && ossiguardProfile) {
          const og = buildOssiguardConnector(scene, geometry, ossiguardProfile);
          ossiguardRef.current = og;
          allLabels.push(...og.labels);
        }
      } catch (e) { errs.ossiguard = msg(e); }
    }
    try {
      const h = buildHumanReference(scene);
      humanRefRef.current = h;
    } catch (e) { /* human reference is optional */ }

    setLayerErrors(errs);
    setLabels(allLabels);

    try {
      selectionRef.current = createSelectionManager(scene, (meta) => {
        if (destroyedRef.current) return;
        setSelectedId(meta ? (meta.partId || meta.stationId || null) : null);
        onSelectionChanged && onSelectionChanged(meta);
      });
    } catch (_) { /* selection is optional */ }

    engine.runRenderLoop(() => {
      if (destroyedRef.current) return;
      try { scene.render(); } catch (_) {}
    });

    const container = containerRef.current;
    const onResize = () => {
      if (destroyedRef.current) return;
      const c = ctxRef.current;
      if (!c || !c.engine || !c.canvas || !c.canvas.isConnected) return;
      const r = container ? container.getBoundingClientRect() : null;
      if (!r || r.width <= 0 || r.height <= 0) return;
      try { c.engine.resize(); } catch (_) {}
    };
    if (container && typeof ResizeObserver !== 'undefined') {
      roRef.current = new ResizeObserver(onResize);
      roRef.current.observe(container);
    }
    resizeHandlerRef.current = onResize;
    window.addEventListener('resize', onResize);

    const diagTimer = setInterval(() => {
      if (destroyedRef.current) return;
      const snap = buildDiagnostics();
      if (snap) onDiagnostics && onDiagnostics(snap);
    }, 500);
    const initial = buildDiagnostics();
    if (initial) onDiagnostics && onDiagnostics(initial);

    return () => {
      destroyedRef.current = true;
      clearInterval(diagTimer);
      try { engine.stopRenderLoop(); } catch (_) {}
      try { if (selectionRef.current) { selectionRef.current.dispose(); selectionRef.current = null; } } catch (_) {}
      try { if (prosthesisRef.current) { prosthesisRef.current.dispose(); prosthesisRef.current = null; } } catch (_) {}
      try { if (stationsRef.current) { stationsRef.current.dispose(); stationsRef.current = null; } } catch (_) {}
      try { if (coverRef.current) { coverRef.current.dispose(); coverRef.current = null; } } catch (_) {}
      try { if (brandingRef.current) { brandingRef.current.dispose(); brandingRef.current = null; } } catch (_) {}
      try { if (designRef.current) { designRef.current.dispose(); designRef.current = null; } } catch (_) {}
      try { if (ossiguardRef.current) { ossiguardRef.current.dispose(); ossiguardRef.current = null; } } catch (_) {}
      try { if (humanRefRef.current) { humanRefRef.current.dispose(); humanRefRef.current = null; } } catch (_) {}
      try { if (roRef.current) { roRef.current.disconnect(); roRef.current = null; } } catch (_) {}
      try { if (resizeHandlerRef.current) { window.removeEventListener('resize', resizeHandlerRef.current); resizeHandlerRef.current = null; } } catch (_) {}
      destroyBabylonContext(ctx);
      ctxRef.current = null;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [attempt]);

  // --- View commands (reset/front/side/top) ---
  useEffect(() => {
    if (!viewCommand || !ctxRef.current) return;
    applyViewPreset(ctxRef.current.camera, viewCommand.preset);
  }, [viewCommand]);

  // --- Mode + compatibility visibility (no engine/scene recreation) ---
  useEffect(() => {
    const ctx = ctxRef.current;
    if (!ctx) return;
    const cover = coverRef.current;
    const brand = brandingRef.current;
    const design = designRef.current;
    const ossiguard = ossiguardRef.current;
    const stations = stationsRef.current;
    const human = humanRefRef.current;
    if (compatibility) {
      cover && cover.setVisible(false);
      brand && brand.setVisible(false);
      design && design.setVisible(false);
      ossiguard && ossiguard.setVisible(false);
      stations && stations.setVisible(false);
      human && human.setVisible(false);
      ctx.hemi.intensity = 0.95;
      ctx.dir.intensity = 0.25;
    } else {
      const coverOn = mode === 'cover' || mode === 'fit';
      cover && cover.setVisible(coverOn);
      brand && brand.setVisible(coverOn);
      design && design.setVisible(coverOn);
      ossiguard && ossiguard.setVisible(showOssiguard && coverOn);
      stations && stations.setVisible(mode === 'internal' || mode === 'measure');
      human && human.setVisible(humanReference || mode === 'fit');
      ctx.hemi.intensity = 0.95;
      ctx.dir.intensity = 0.95;
    }
  }, [compatibility, mode, humanReference, showOssiguard]);

  // --- Workshop measurement records can arrive after engine creation. ---
  // Rebuild only the station layer; never recreate the Babylon engine/scene.
  useEffect(() => {
    const ctx = ctxRef.current;
    if (!ctx) return;

    try {
      if (stationsRef.current) {
        stationsRef.current.dispose();
        stationsRef.current = null;
      }

      const nextStations = buildMeasurementStations(ctx.scene, measurementStations);
      stationsRef.current = nextStations;

      nextStations.setVisible(
        !compatibility && (mode === 'internal' || mode === 'measure')
      );

      setLabels((current) => [
        ...current.filter((label) => {
          const kind = label.kind || 'part';
          return kind !== 'station' && kind !== 'dim';
        }),
        ...nextStations.labels,
      ]);

      setLayerErrors((current) => ({ ...current, stations: null }));
    } catch (e) {
      setLayerErrors((current) => ({ ...current, stations: msg(e) }));
    }
  }, [measurementStations]);

  // --- Workshop transparency updates without engine recreation. ---
  useEffect(() => {
    const cover = coverRef.current;
    if (!cover || !cover.setOpacity) return;

    const numeric = Number(transparency);
    const opacity = Number.isFinite(numeric)
      ? Math.max(0.1, Math.min(1, numeric))
      : 1;

    try {
      cover.setOpacity(compatibility ? 1 : opacity);
    } catch (_) {}
  }, [transparency, compatibility]);

  // --- Design config updates (no engine/scene recreation) ---
  useEffect(() => {
    const cover = coverRef.current;
    const brand = brandingRef.current;
    const design = designRef.current;
    if (cover && cover.update) try { cover.update(designConfig || {}); } catch (_) {}
    if (brand && brand.update) try { brand.update(designConfig || {}); } catch (_) {}
    if (design && design.update) try { design.update(designConfig || {}); } catch (_) {}
  }, [designConfig]);

  // --- Ossiguard profile updates (no engine/scene recreation) ---
  useEffect(() => {
    const og = ossiguardRef.current;
    if (og && og.update) try { og.update(ossiguardProfile || {}); } catch (_) {}
  }, [ossiguardProfile]);

  const getContext = useCallback(() => ctxRef.current, []);
  const visibleLabels = useMemo(() => {
    if (compatibility) return labels.filter((l) => (l.kind || 'part') !== 'warning');
    const showStation = mode === 'internal' || mode === 'measure';
    const showDim = mode === 'measure';
    const showWarning = mode === 'cover' || mode === 'fit';
    return labels.filter((l) => {
      const k = l.kind || 'part';
      if (k === 'warning') return showWarning;
      if (k === 'station') return showStation;
      if (k === 'dim') return showDim;
      if (showAllLabels) return true;
      return !!selectedId && l.id === `part-${selectedId}`;
    });
  }, [labels, compatibility, mode, showAllLabels, selectedId]);

  const handleRetry = () => {
    setEngineError(null);
    setAttempt((a) => a + 1);
  };

  // --- Fatal engine overlay ---
  if (engineError) {
    let webgl = 'unknown';
    try { webgl = !!document.createElement('canvas').getContext('webgl') ? 'Available' : 'Unavailable'; } catch (_) { webgl = 'Unknown'; }
    const canvas = canvasRef.current;
    return (
      <div className="flex h-full w-full items-center justify-center p-6">
        <div className="panel max-w-lg space-y-3 p-6">
          <div className="flex items-center gap-2 text-red-300"><ShieldAlert className="h-5 w-5" /><span className="text-sm font-semibold">Babylon Engine could not initialise</span></div>
          <p className="text-xs text-slate-400">The fatal viewport message is shown only when the Engine itself cannot start. Optional-layer failures never reach this state.</p>
          <dl className="divide-y divide-white/5 rounded-lg border border-white/10 bg-black/20 text-xs">
            {[
              ['Actual error', engineError],
              ['Canvas state', canvas ? (canvas.isConnected ? 'connected' : 'detached') : 'no canvas ref'],
              ['Canvas dimensions', canvas ? `${canvas.clientWidth}×${canvas.clientHeight}` : '—'],
              ['WebGL availability', webgl],
              ['Babylon version', BABYLON_VERSION],
              ['Browser renderer', typeof navigator !== 'undefined' ? navigator.userAgent : '—'],
            ].map(([k, v]) => (
              <div key={k} className="flex justify-between gap-3 px-3 py-2"><dt className="text-slate-500">{k}</dt><dd className="text-right text-slate-200">{String(v)}</dd></div>
            ))}
          </dl>
          <button onClick={handleRetry} className="btn-primary w-full"><RotateCcw className="mr-1.5 h-4 w-4" />Retry V2</button>
        </div>
      </div>
    );
  }

  const layerUnavailable = Object.entries(layerErrors).filter(([, v]) => v).map(([k]) => k);

  return (
    <div ref={containerRef} className="relative h-full w-full">
      <canvas ref={canvasRef} className="h-full w-full touch-none outline-none" />
      <DomLabelLayerV2 getContext={getContext} labels={visibleLabels} />

      {layerUnavailable.length > 0 && (
        <div className="absolute left-3 top-3 z-20 flex flex-wrap gap-1.5">
          {layerUnavailable.map((k) => (
            <span key={k} className="rounded-md border border-amber-400/40 bg-[#0b0f13]/90 px-2 py-1 text-[11px] font-medium text-amber-300">
              {labelName(k)} unavailable
            </span>
          ))}
        </div>
      )}

      <div className="pointer-events-none absolute bottom-3 left-1/2 z-20 -translate-x-1/2 rounded-lg border border-white/10 bg-[#0b0f13]/90 px-3 py-1.5 text-center text-[11px] text-slate-300 backdrop-blur">
        {designConfig ? (designConfig.custom_text_enabled ? 'Design concept applied — not manufacturing CAD' : 'Design styling applied — not manufacturing CAD') : (geometry && geometry.statusLabel ? geometry.statusLabel : 'Generic below-knee visual model — not customer geometry and not manufacturing CAD.')}
      </div>
    </div>
  );
}

function labelName(k) {
return { prosthesis: 'Prosthesis', stations: 'Measurement layer', cover: 'Cover layer', branding: 'Branding layer', design: 'Design layer', ossiguard: 'Ossiguard layer' }[k] || k;
}