import { create } from 'zustand';
import { getModelWorldScaleFactor, worldToModelLocal } from '@/lib/modelTransforms';
import { sendRemoteLog } from '@/lib/remoteLogger';

// ── Persistência de placement (fineTuning + escala) e flags WebXR ─────
const PLACEMENT_KEY = 'tsxr_placements_v1';
const XRFEAT_KEY = 'xrFeatures_v2';
const CAMERA_MODE_KEY = 'tsxr_camera_mode_v1';

function loadCameraMode(): 'ortho' | 'persp' {
  try {
    const raw = localStorage.getItem(CAMERA_MODE_KEY);
    if (raw === 'persp' || raw === 'ortho') return raw;
  } catch (e) { /* ignore */ }
  return 'ortho';
}
function saveCameraMode(m: 'ortho' | 'persp') {
  try { localStorage.setItem(CAMERA_MODE_KEY, m); } catch (e) { /* ignore */ }
}

export interface XRFeatureFlags {
  handTracking: boolean;
  planeDetection: boolean;
  hitTest: boolean;
  domOverlay: boolean;
  anchors: boolean;
  meshDetection: boolean;
  depthSensing: boolean;
  layers: boolean;
  bodyTracking: boolean;
}
const DEFAULT_XR_FEATURES: XRFeatureFlags = {
  handTracking: true,
  planeDetection: true,
  hitTest: true,
  domOverlay: true,
  anchors: true,
  meshDetection: false,
  depthSensing: false,
  layers: false,
  bodyTracking: false,
};
function loadXRFeatures(): XRFeatureFlags {
  try {
    const raw = localStorage.getItem(XRFEAT_KEY);
    if (raw) return { ...DEFAULT_XR_FEATURES, ...JSON.parse(raw) };
  } catch (e) { /* ignore */ }
  return { ...DEFAULT_XR_FEATURES };
}
function saveXRFeatures(f: XRFeatureFlags) {
  try { localStorage.setItem(XRFEAT_KEY, JSON.stringify(f)); } catch (e) { /* ignore */ }
}

interface StoredPlacement { fineTuning: FineTuning; calibrationQuat?: [number, number, number, number] | null; }
function loadPlacements(): Record<string, StoredPlacement> {
  try {
    const raw = localStorage.getItem(PLACEMENT_KEY);
    if (raw) return JSON.parse(raw);
  } catch (e) { /* ignore */ }
  return {};
}
function savePlacement(fileName: string, fineTuning: FineTuning, calibrationQuat?: [number, number, number, number] | null) {
  try {
    const all = loadPlacements();
    const prev = all[fileName] ?? { fineTuning };
    all[fileName] = {
      fineTuning,
      calibrationQuat: calibrationQuat === undefined ? prev.calibrationQuat ?? null : calibrationQuat,
    };
    localStorage.setItem(PLACEMENT_KEY, JSON.stringify(all));
  } catch (e) { /* ignore */ }
}

// ── Visibility (selection-based hide/isolate) persistence ────────────
const VISIBILITY_KEY = 'tsxr_visibility_v1';
interface StoredVisibility { hidden: string[]; isolated: string[] | null; }
function loadVisibilityMap(): Record<string, StoredVisibility> {
  try {
    const raw = localStorage.getItem(VISIBILITY_KEY);
    if (raw) return JSON.parse(raw);
  } catch (e) { /* ignore */ }
  return {};
}
function saveVisibility(fileName: string, hidden: Set<string>, isolated: Set<string> | null) {
  try {
    const all = loadVisibilityMap();
    all[fileName] = { hidden: [...hidden], isolated: isolated ? [...isolated] : null };
    localStorage.setItem(VISIBILITY_KEY, JSON.stringify(all));
  } catch (e) { /* ignore */ }
}
function loadVisibility(fileName: string): { hidden: Set<string>; isolated: Set<string> | null } {
  const v = loadVisibilityMap()[fileName];
  if (!v) return { hidden: new Set(), isolated: null };
  return { hidden: new Set(v.hidden ?? []), isolated: v.isolated ? new Set(v.isolated) : null };
}

const IFC_MAT_COLORS_KEY = 'tsxr_ifc_mat_colors_v1';
const IFC_COLOR_MODE_KEY = 'tsxr_ifc_color_mode_v1';
export type IFCColorMode = 'none' | 'material' | 'description';

function loadIfcMaterialColors(): boolean {
  try {
    const raw = localStorage.getItem(IFC_MAT_COLORS_KEY);
    return raw !== 'false';
  } catch (e) { /* ignore */ }
  return true;
}
function saveIfcMaterialColors(enabled: boolean) {
  try { localStorage.setItem(IFC_MAT_COLORS_KEY, String(enabled)); } catch (e) { /* ignore */ }
}

function loadIfcColorMode(): IFCColorMode {
  try {
    const raw = localStorage.getItem(IFC_COLOR_MODE_KEY);
    if (raw === 'none' || raw === 'material' || raw === 'description') {
      return raw as IFCColorMode;
    }
    const legacy = localStorage.getItem(IFC_MAT_COLORS_KEY);
    if (legacy === 'false') return 'none';
  } catch (e) { /* ignore */ }
  return 'material';
}
function saveIfcColorMode(mode: IFCColorMode) {
  try { localStorage.setItem(IFC_COLOR_MODE_KEY, mode); } catch (e) { /* ignore */ }
}



export interface ScaleRatio {
  label: string;     // e.g. "1:50", "2:1", "1:1"
  factor: number;    // render multiplier: 1:50 => 0.02, 2:1 => 2, 1:1 => 1
}

export const SCALE_PRESETS: ScaleRatio[] = [
  { label: '10:1',  factor: 10 },
  { label: '4:1',   factor: 4 },
  { label: '2:1',   factor: 2 },
  { label: '1:1',   factor: 1 },
  { label: '1:2',   factor: 1 / 2 },
  { label: '1:4',   factor: 1 / 4 },
  { label: '1:8',   factor: 1 / 8 },
  { label: '1:10',  factor: 1 / 10 },
  { label: '1:20',  factor: 1 / 20 },
  { label: '1:33',  factor: 1 / 33 },
  { label: '1:50',  factor: 1 / 50 },
  { label: '1:100', factor: 1 / 100 },
  { label: '1:200', factor: 1 / 200 },
];

interface ModelInfo {
  fileName: string;
  fileSize: number;
  url: string;
}

interface FineTuning {
  posX: number;
  posY: number;
  posZ: number;
  rotX: number;
  rotY: number;
  rotZ: number;
  scale: number;
}

export interface SceneModel {
  id: string;
  fileName: string;
  fileSize: number;
  url: string;
  color: string;
  visible: boolean;
  locked: boolean;
  fineTuning: FineTuning;
  /** Local rotation (xyzw) that aligns the model's intrinsic axes to world axes. */
  calibrationQuat: [number, number, number, number] | null;
}

const MAX_MODELS = 5;
const MODEL_COLORS = ['#3b82f6', '#22c55e', '#eab308', '#a855f7', '#ec4899'];

type AnchorMode = 'tracking' | 'manual' | 'fine-tuning';
type InspectionResult = 'pending' | 'approved' | 'rejected';
type RenderMode = 'solid' | 'wireframe' | 'edges';

export interface MeasurePoint {
  x: number;
  y: number;
  z: number;
  /** Model hit by this point, used so saved dimensions attach to the right piece. */
  modelId?: string;
}

export interface Measurement {
  id: string;
  pointA: MeasurePoint;
  pointB: MeasurePoint;
  distanceMM: number;
  /** 'distance' = point-to-point (default), 'hole' = registered diameter at center, 'edge' = registered edge length */
  kind?: 'distance' | 'hole' | 'edge';
  /** Optional pre-formatted label (e.g. "Ø 12.5"). When absent, UI formats distanceMM. */
  label?: string;
  /** Model the measurement belongs to. When set, pointA/pointB are stored in that
   *  model's local frame so the measurement follows the model when repositioned. */
  modelId?: string;
}

export interface HoverInfo {
  type: 'hole' | 'edge';
  position: [number, number, number];
  value: number; // mm (diameter or length)
  modelId?: string;
  /** For edges: the two endpoints in world coords. */
  endpoints?: { a: MeasurePoint; b: MeasurePoint };
}

export interface ChecklistItem {
  id: string;
  label: string;
  status: 'pending' | 'approved' | 'rejected';
  note: string;
  audioUrl: string | null;
}

const defaultChecklist: ChecklistItem[] = [
  { id: 'dimensions', label: 'Dimensões Gerais', status: 'pending', note: '', audioUrl: null },
  { id: 'plates', label: 'Posição das Placas', status: 'pending', note: '', audioUrl: null },
  { id: 'holes', label: 'Diâmetro dos Furos', status: 'pending', note: '', audioUrl: null },
  { id: 'weld', label: 'Especificação da Solda', status: 'pending', note: '', audioUrl: null },
];

const defaultFineTuning: FineTuning = { posX: 0, posY: 0, posZ: 0, rotX: 0, rotY: 0, rotZ: 0, scale: 1 };

interface ModelStore {
  // ── Multi-model scene ─────────────
  models: SceneModel[];
  activeModelId: string | null;
  addModel: (m: ModelInfo) => string; // returns id
  removeModel: (id: string) => void;
  setActiveModel: (id: string | null) => void;
  toggleModelVisible: (id: string) => void;
  toggleModelLocked: (id: string) => void;
  setModelColor: (id: string, color: string) => void;
  duplicateModel: (id: string) => void;
  clearAllModels: () => void;
  maxModels: number;

  /** Calibração (rotação local) ↔ ViewCube. Aplicada antes do fineTuning rotation. */
  setCalibration: (id: string, quat: [number, number, number, number] | null) => void;

  // ── Legacy single-model accessors (proxy to active) ─────────────
  model: ModelInfo | null;
  setModel: (model: ModelInfo | null) => void;

  fineTuning: FineTuning;
  setFineTuning: (ft: Partial<FineTuning>) => void;
  resetFineTuning: () => void;

  anchorMode: AnchorMode;
  setAnchorMode: (mode: AnchorMode) => void;

  inspectionResult: InspectionResult;
  setInspectionResult: (result: InspectionResult) => void;

  xrSupported: boolean | null;
  setXrSupported: (supported: boolean | null) => void;

  scaleRatio: ScaleRatio;
  setScaleRatio: (r: ScaleRatio) => void;
  /** Incremented whenever the user asks to reset the AR scale; XRGrabbable
   *  watches this and snaps its group.scale back to 1. */
  scaleResetNonce: number;
  resetScale: () => void;

  opacity: number;
  setOpacity: (opacity: number) => void;

  renderMode: RenderMode;
  setRenderMode: (mode: RenderMode) => void;

  /** Camera projection used in the viewer. Ortho is the default for CAD-style work. */
  cameraMode: 'ortho' | 'persp';
  setCameraMode: (m: 'ortho' | 'persp') => void;

  showGrid: boolean;
  setShowGrid: (show: boolean) => void;

  /** World Y position of the grid plane (meters). */
  gridY: number;
  setGridY: (y: number) => void;
  /** When true, grid follows the bottom of the loaded model automatically. */
  gridAutoFollow: boolean;
  setGridAutoFollow: (b: boolean) => void;
  /** Nudge grid by delta meters and disable auto-follow. */
  nudgeGridY: (delta: number) => void;
  /** When true, the next click on a model face sets grid Y to that point. */
  gridCalibMode: boolean;
  setGridCalibMode: (b: boolean) => void;
  /** When true, the VR controller/headset hit-tests real world surfaces to place grid & models. */
  gridLandingMode: boolean;
  setGridLandingMode: (b: boolean) => void;

  /** Section/clipping cut tool (X/Y/Z axis-aligned planes in world space). */
  sectionEnabled: { x: boolean; y: boolean; z: boolean };
  sectionInvert: { x: boolean; y: boolean; z: boolean };
  /** Cut level in meters (world coordinates) per axis. */
  sectionLevel: { x: number; y: number; z: number };
  sectionPanelOpen: boolean;
  /** Opacity of the floating section panel (0.2 – 1). */
  sectionPanelOpacity: number;
  setSectionEnabled: (axis: 'x' | 'y' | 'z', on: boolean) => void;
  setSectionInvert: (axis: 'x' | 'y' | 'z', on: boolean) => void;
  setSectionLevel: (axis: 'x' | 'y' | 'z', v: number) => void;
  setSectionPanelOpen: (open: boolean) => void;
  setSectionPanelOpacity: (o: number) => void;
  resetSection: () => void;

  wireframeColor: string;
  setWireframeColor: (color: string) => void;

  wireframeThickness: number;
  setWireframeThickness: (t: number) => void;

  edgeThresholdAngle: number;
  setEdgeThresholdAngle: (angle: number) => void;

  checklist: ChecklistItem[];
  setChecklistItemStatus: (id: string, status: 'approved' | 'rejected') => void;
  setChecklistItemNote: (id: string, note: string) => void;
  setChecklistItemAudio: (id: string, audioUrl: string | null) => void;
  resetChecklist: () => void;

  measureMode: boolean;
  setMeasureMode: (on: boolean) => void;
  positionMode: boolean;
  setPositionMode: (on: boolean) => void;
  walkMode: boolean;
  setWalkMode: (on: boolean) => void;

  // ── Selection (hide/isolate/export elements) ────────────
  selectionMode: boolean;
  setSelectionMode: (on: boolean) => void;
  /** Keys: `${modelId}:${ifcId|name|uuid}` */
  selectedElementKeys: Set<string>;
  /** Hidden element keys, per-active-model file (persisted by fileName). */
  hiddenElementKeys: Set<string>;
  /** When non-null, only these keys are visible (isolation). */
  isolatedElementKeys: Set<string> | null;
  toggleElementSelection: (key: string) => void;
  clearElementSelection: () => void;
  hideSelectedElements: () => void;
  isolateSelectedElements: () => void;
  showAllElements: () => void;
  /** Bump whenever visibility/selection state changes — viewer re-applies. */
  visibilityNonce: number;
  measurePoints: MeasurePoint[];
  addMeasurePoint: (p: MeasurePoint) => void;
  undoLastMeasurePoint: () => void;
  measurements: Measurement[];
  clearMeasurements: () => void;
  removeMeasurement: (id: string) => void;
  undoLastMeasurement: () => void;
  /** Register the current HoverInfo (hole diameter or edge length) as a saved measurement. */
  registerHoverMeasurement: (info: HoverInfo) => void;


  compareMode: boolean;
  setCompareMode: (on: boolean) => void;
  compareImage: string | null;
  setCompareImage: (url: string | null) => void;

  snapPoint: MeasurePoint | null;
  setSnapPoint: (p: MeasurePoint | null) => void;

  hoverInfo: HoverInfo | null;
  setHoverInfo: (info: HoverInfo | null) => void;

  hoverIfcProps: Record<string, string> | null;
  setHoverIfcProps: (props: Record<string, string> | null) => void;

  screenshots: string[];
  addScreenshot: (dataUrl: string) => void;
  removeScreenshot: (index: number) => void;
  clearScreenshots: () => void;

  // ── WebXR feature toggles (diagnóstico do grid de segurança Quest) ────
  xrFeatures: XRFeatureFlags;
  setXRFeature: (key: keyof XRFeatureFlags, value: boolean) => void;
  resetXRFeatures: () => void;

  ifcMaterialColors: boolean;
  setIfcMaterialColors: (enabled: boolean) => void;
  ifcColorMode: IFCColorMode;
  setIfcColorMode: (mode: IFCColorMode) => void;

  xrRig: THREE.Group | null;
  setXRRig: (rig: THREE.Group | null) => void;
}

/** Sync top-level legacy `model` / `fineTuning` from active SceneModel. */
function syncActive(state: Pick<ModelStore, 'models' | 'activeModelId'>): { model: ModelInfo | null; fineTuning: FineTuning } {
  const active = state.models.find(m => m.id === state.activeModelId) ?? null;
  return {
    model: active ? { fileName: active.fileName, fileSize: active.fileSize, url: active.url } : null,
    fineTuning: active ? { ...active.fineTuning } : { ...defaultFineTuning },
  };
}

export const useModelStore = create<ModelStore>((set, get) => ({
  xrRig: null,
  setXRRig: (rig) => set({ xrRig: rig }),

  // ── Multi-model ─────────────
  models: [],
  activeModelId: null,
  maxModels: MAX_MODELS,

  addModel: (m) => {
    const state = get();
    if (state.models.length >= MAX_MODELS) {
      return '';
    }
    const id = `mdl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
    const color = MODEL_COLORS[state.models.length % MODEL_COLORS.length];
    const offsetIdx = state.models.length;
    // Recupera placement anterior salvo (mesmo fileName) — sobrevive a sair/entrar do AR.
    const saved = loadPlacements()[m.fileName];
    const fineTuning: FineTuning = saved?.fineTuning
      ? { ...saved.fineTuning }
      : { ...defaultFineTuning, posX: offsetIdx * 0.15 };
    const calibrationQuat = saved?.calibrationQuat ?? null;
    const newModel: SceneModel = {
      id,
      fileName: m.fileName,
      fileSize: m.fileSize,
      url: m.url,
      color,
      visible: true,
      locked: false,
      fineTuning,
      calibrationQuat,
    };
    const models = [...state.models, newModel];
    set({ models, activeModelId: id, ...syncActive({ models, activeModelId: id }) });
    return id;
  },

  removeModel: (id) => {
    const state = get();
    const models = state.models.filter(m => m.id !== id);
    const activeModelId = state.activeModelId === id
      ? (models[0]?.id ?? null)
      : state.activeModelId;
    set({ models, activeModelId, ...syncActive({ models, activeModelId }) });
  },

  setActiveModel: (id) => {
    const state = get();
    const active = state.models.find(m => m.id === id);
    const vis = active ? loadVisibility(active.fileName) : { hidden: new Set<string>(), isolated: null };
    set({
      activeModelId: id,
      ...syncActive({ models: state.models, activeModelId: id }),
      hiddenElementKeys: vis.hidden,
      isolatedElementKeys: vis.isolated,
      selectedElementKeys: new Set(),
      visibilityNonce: state.visibilityNonce + 1,
    });
  },

  toggleModelVisible: (id) => set((state) => ({
    models: state.models.map(m => m.id === id ? { ...m, visible: !m.visible } : m),
  })),

  toggleModelLocked: (id) => set((state) => ({
    models: state.models.map(m => m.id === id ? { ...m, locked: !m.locked } : m),
  })),

  setModelColor: (id, color) => set((state) => ({
    models: state.models.map(m => m.id === id ? { ...m, color } : m),
  })),

  duplicateModel: (id) => {
    const state = get();
    if (state.models.length >= MAX_MODELS) return;
    const src = state.models.find(m => m.id === id);
    if (!src) return;
    const newId = `mdl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
    const color = MODEL_COLORS[state.models.length % MODEL_COLORS.length];
    const copy: SceneModel = {
      ...src,
      id: newId,
      color,
      fineTuning: { ...src.fineTuning, posX: src.fineTuning.posX + 0.15 },
    };
    const models = [...state.models, copy];
    set({ models, activeModelId: newId, ...syncActive({ models, activeModelId: newId }) });
  },

  clearAllModels: () => set({ models: [], activeModelId: null, model: null, fineTuning: { ...defaultFineTuning } }),

  setCalibration: (id, quat) => set((state) => {
    const models = state.models.map(m => {
      if (m.id !== id) return m;
      savePlacement(m.fileName, m.fineTuning, quat);
      return { ...m, calibrationQuat: quat };
    });
    return { models };
  }),

  // ── Legacy single-model proxies ─────────────
  model: null,
  setModel: (m) => {
    if (!m) {
      set({ models: [], activeModelId: null, model: null, fineTuning: { ...defaultFineTuning } });
      return;
    }
    // Legacy: replace all models with this single one
    const id = `mdl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
    const newModel: SceneModel = {
      id, fileName: m.fileName, fileSize: m.fileSize, url: m.url,
      color: MODEL_COLORS[0], visible: true, locked: false,
      fineTuning: { ...defaultFineTuning },
      calibrationQuat: null,
    };
    set({ models: [newModel], activeModelId: id, ...syncActive({ models: [newModel], activeModelId: id }) });
  },

  fineTuning: { ...defaultFineTuning },
  setFineTuning: (ft) => set((state) => {
    const activeId = state.activeModelId;
    const activeBefore = state.models.find(m => m.id === activeId);
    // Lock guard: when the active model is locked, ignore any movement/scale change.
    if (activeBefore?.locked) return {};
    const newFT = { ...state.fineTuning, ...ft };
    const models = activeId
      ? state.models.map(m => m.id === activeId ? { ...m, fineTuning: newFT } : m)
      : state.models;
    // Persiste placement por fileName para sobreviver à saída/re-entrada do AR.
    const active = models.find(m => m.id === activeId);
    if (active) savePlacement(active.fileName, newFT);
    return { fineTuning: newFT, models };
  }),
  resetFineTuning: () => set((state) => {
    const activeId = state.activeModelId;
    const activeBefore = state.models.find(m => m.id === activeId);
    if (activeBefore?.locked) return {};
    const reset = { ...defaultFineTuning };
    const models = activeId
      ? state.models.map(m => m.id === activeId ? { ...m, fineTuning: reset } : m)
      : state.models;
    const active = models.find(m => m.id === activeId);
    if (active) savePlacement(active.fileName, reset);
    return { fineTuning: reset, models };
  }),

  anchorMode: 'manual',
  setAnchorMode: (anchorMode) => set({ anchorMode }),

  inspectionResult: 'pending',
  setInspectionResult: (inspectionResult) => set({ inspectionResult }),

  xrSupported: null,
  setXrSupported: (xrSupported) => set({ xrSupported }),

  scaleRatio: SCALE_PRESETS.find(p => p.label === '1:1')!,
  setScaleRatio: (scaleRatio) => set((state) => {
    const activeBefore = state.models.find(m => m.id === state.activeModelId);
    if (activeBefore?.locked) return {};
    return { scaleRatio };
  }),
  scaleResetNonce: 0,
  resetScale: () => set((state) => {
    const activeId = state.activeModelId;
    const activeBefore = state.models.find(m => m.id === activeId);
    if (activeBefore?.locked) return {};
    const oneToOne = SCALE_PRESETS.find(p => p.label === '1:1')!;
    const models = activeId
      ? state.models.map(m => m.id === activeId
          ? { ...m, fineTuning: { ...m.fineTuning, scale: 1 } }
          : m)
      : state.models;
    const active = models.find(m => m.id === activeId);
    const fineTuning = active ? { ...active.fineTuning } : { ...state.fineTuning, scale: 1 };
    if (active) savePlacement(active.fileName, fineTuning);
    return {
      scaleRatio: oneToOne,
      models,
      fineTuning,
      scaleResetNonce: state.scaleResetNonce + 1,
    };
  }),

  opacity: 1,
  setOpacity: (opacity) => set({ opacity }),

  renderMode: 'solid',
  setRenderMode: (renderMode) => set({ renderMode }),

  cameraMode: loadCameraMode(),
  setCameraMode: (cameraMode) => { saveCameraMode(cameraMode); set({ cameraMode }); },

  showGrid: false,
  setShowGrid: (showGrid) => set({ showGrid }),

  gridY: -0.09,
  setGridY: (gridY) => set({ gridY, gridAutoFollow: false }),
  gridAutoFollow: true,
  setGridAutoFollow: (gridAutoFollow) => set({ gridAutoFollow }),
  nudgeGridY: (delta) => set((s) => ({ gridY: s.gridY + delta, gridAutoFollow: false })),
  gridCalibMode: false,
  setGridCalibMode: (gridCalibMode) => set({ gridCalibMode }),
  gridLandingMode: false,
  setGridLandingMode: (gridLandingMode) => set({ gridLandingMode }),

  sectionEnabled: { x: false, y: false, z: false },
  sectionInvert: { x: false, y: false, z: false },
  sectionLevel: { x: 0, y: 0, z: 0 },
  sectionPanelOpen: false,
  sectionPanelOpacity: 0.9,
  setSectionEnabled: (axis, on) => set((s) => ({ sectionEnabled: { ...s.sectionEnabled, [axis]: on } })),
  setSectionInvert: (axis, on) => set((s) => ({ sectionInvert: { ...s.sectionInvert, [axis]: on } })),
  setSectionLevel: (axis, v) => set((s) => ({ sectionLevel: { ...s.sectionLevel, [axis]: v } })),
  setSectionPanelOpen: (sectionPanelOpen) => set({ sectionPanelOpen }),
  setSectionPanelOpacity: (sectionPanelOpacity) => set({ sectionPanelOpacity }),
  resetSection: () => set({
    sectionEnabled: { x: false, y: false, z: false },
    sectionInvert: { x: false, y: false, z: false },
    sectionLevel: { x: 0, y: 0, z: 0 },
  }),

  wireframeColor: '#00f3ff',
  setWireframeColor: (wireframeColor) => set({ wireframeColor }),

  wireframeThickness: 2,
  setWireframeThickness: (wireframeThickness) => set({ wireframeThickness }),

  edgeThresholdAngle: 15,
  setEdgeThresholdAngle: (edgeThresholdAngle) => set({ edgeThresholdAngle }),

  checklist: defaultChecklist.map(i => ({ ...i })),
  setChecklistItemStatus: (id, status) => set((state) => ({
    checklist: state.checklist.map(item =>
      item.id === id ? { ...item, status } : item
    ),
  })),
  setChecklistItemNote: (id, note) => set((state) => ({
    checklist: state.checklist.map(item =>
      item.id === id ? { ...item, note } : item
    ),
  })),
  setChecklistItemAudio: (id, audioUrl) => set((state) => ({
    checklist: state.checklist.map(item =>
      item.id === id ? { ...item, audioUrl } : item
    ),
  })),
  resetChecklist: () => set({ checklist: defaultChecklist.map(i => ({ ...i })) }),

  measureMode: false,
  setMeasureMode: (measureMode) => set((s) => ({ measureMode, measurePoints: [], positionMode: measureMode ? false : s.positionMode, selectionMode: measureMode ? false : s.selectionMode, walkMode: false })),
  positionMode: false,
  setPositionMode: (positionMode) => set((s) => ({ positionMode, measureMode: positionMode ? false : s.measureMode, selectionMode: positionMode ? false : s.selectionMode, walkMode: false })),
  walkMode: false,
  setWalkMode: (walkMode) => {
    set({ walkMode });
    if (walkMode) set({ positionMode: false, measureMode: false, selectionMode: false, cameraMode: 'persp' });
  },

  // ── Selection state ─────────────────────────────────────
  selectionMode: false,
  setSelectionMode: (on) => set((s) => ({
    selectionMode: on,
    measureMode: on ? false : s.measureMode,
    positionMode: s.positionMode,
    selectedElementKeys: on ? s.selectedElementKeys : new Set(),
  })),
  selectedElementKeys: new Set<string>(),
  hiddenElementKeys: new Set<string>(),
  isolatedElementKeys: null,
  visibilityNonce: 0,
  toggleElementSelection: (key) => set((s) => {
    const next = new Set(s.selectedElementKeys);
    if (next.has(key)) next.delete(key); else next.add(key);
    return { selectedElementKeys: next, visibilityNonce: s.visibilityNonce + 1 };
  }),
  clearElementSelection: () => set((s) => ({ selectedElementKeys: new Set(), visibilityNonce: s.visibilityNonce + 1 })),
  hideSelectedElements: () => set((s) => {
    if (s.selectedElementKeys.size === 0) return {} as Partial<ModelStore>;
    const hidden = new Set(s.hiddenElementKeys);
    s.selectedElementKeys.forEach(k => hidden.add(k));
    const active = s.models.find(m => m.id === s.activeModelId);
    if (active) saveVisibility(active.fileName, hidden, s.isolatedElementKeys);
    return { hiddenElementKeys: hidden, selectedElementKeys: new Set(), visibilityNonce: s.visibilityNonce + 1 };
  }),
  isolateSelectedElements: () => set((s) => {
    if (s.selectedElementKeys.size === 0) return {} as Partial<ModelStore>;
    const iso = new Set(s.selectedElementKeys);
    const active = s.models.find(m => m.id === s.activeModelId);
    if (active) saveVisibility(active.fileName, s.hiddenElementKeys, iso);
    return { isolatedElementKeys: iso, selectedElementKeys: new Set(), visibilityNonce: s.visibilityNonce + 1 };
  }),
  showAllElements: () => set((s) => {
    const active = s.models.find(m => m.id === s.activeModelId);
    if (active) saveVisibility(active.fileName, new Set(), null);
    return { hiddenElementKeys: new Set(), isolatedElementKeys: null, selectedElementKeys: new Set(), visibilityNonce: s.visibilityNonce + 1 };
  }),
  measurePoints: [],
  addMeasurePoint: (p) => set((state) => {
    sendRemoteLog('info', `addMeasurePoint acionado: ponto atual ${state.measurePoints.length + 1}`, p);
    const points = [...state.measurePoints, p];
    if (points.length === 2) {
      const [a, b] = points;
      const modelId = (a.modelId && a.modelId === b.modelId ? a.modelId : state.activeModelId) ?? undefined;
      const aConv = modelId ? worldToModelLocal(modelId, a) : null;
      const bConv = modelId ? worldToModelLocal(modelId, b) : null;
      const attached = !!(aConv && bConv);
      const pa = attached ? aConv! : a;
      const pb = attached ? bConv! : b;
      const dx = (pa.x - pb.x) * 1000;
      const dy = (pa.y - pb.y) * 1000;
      const dz = (pa.z - pb.z) * 1000;
      const distanceMM = Math.sqrt(dx * dx + dy * dy + dz * dz);
      sendRemoteLog('info', `addMeasurePoint: calculando distância`, {
        distanceMM,
        modelId,
        attached
      });
      const measurement: Measurement = {
        id: `m_${Date.now()}`,
        pointA: pa,
        pointB: pb,
        distanceMM,
        modelId: attached ? modelId : undefined,
      };
      sendRemoteLog('info', `addMeasurePoint: medição gerada com sucesso`, measurement);
      return { measurePoints: [], measurements: [...state.measurements, measurement] };
    }
    return { measurePoints: points };
  }),
  measurements: [],
  clearMeasurements: () => set({ measurements: [], measurePoints: [] }),
  removeMeasurement: (id) => set((state) => ({
    measurements: state.measurements.filter(m => m.id !== id),
  })),
  undoLastMeasurePoint: () => set((state) => ({
    measurePoints: state.measurePoints.slice(0, -1),
  })),
  undoLastMeasurement: () => set((state) => {
    if (state.measurePoints.length > 0) {
      return { measurePoints: state.measurePoints.slice(0, -1) };
    }
    return { measurements: state.measurements.slice(0, -1) };
  }),
  registerHoverMeasurement: (info) => set((state) => {
    const modelId = info.modelId ?? state.activeModelId ?? undefined;
    const modelScale = modelId ? getModelWorldScaleFactor(modelId) : 1;
    const toLocal = (p: MeasurePoint): { point: MeasurePoint; attached: boolean } => {
      const conv = modelId ? worldToModelLocal(modelId, p) : null;
      return conv ? { point: conv, attached: true } : { point: p, attached: false };
    };
    let measurement: Measurement;
    let attached = false;
    if (info.type === 'hole') {
      const cw: MeasurePoint = { x: info.position[0], y: info.position[1], z: info.position[2] };
      const c = toLocal(cw);
      attached = c.attached;
      const valueMM = attached ? info.value / modelScale : info.value;
      measurement = {
        id: `m_${Date.now()}`,
        pointA: c.point,
        pointB: c.point,
        distanceMM: valueMM,
        kind: 'hole',
        label: `Ø ${valueMM.toFixed(1)}`,
        modelId: attached ? modelId : undefined,
      };
    } else {
      const aw = info.endpoints?.a ?? { x: info.position[0], y: info.position[1], z: info.position[2] };
      const bw = info.endpoints?.b ?? aw;
      const a = toLocal(aw);
      const b = toLocal(bw);
      attached = a.attached && b.attached;
      const pa = attached ? a.point : aw;
      const pb = attached ? b.point : bw;
      const dx = (pa.x - pb.x) * 1000;
      const dy = (pa.y - pb.y) * 1000;
      const dz = (pa.z - pb.z) * 1000;
      const valueMM = Math.sqrt(dx * dx + dy * dy + dz * dz);
      measurement = {
        id: `m_${Date.now()}`,
        pointA: pa,
        pointB: pb,
        distanceMM: valueMM,
        kind: 'edge',
        label: `${valueMM.toFixed(1)} mm`,
        modelId: attached ? modelId : undefined,
      };
    }
    return { measurements: [...state.measurements, measurement], hoverInfo: null };
  }),


  compareMode: false,
  setCompareMode: (compareMode) => set({ compareMode }),
  compareImage: null,
  setCompareImage: (compareImage) => set({ compareImage }),

  snapPoint: null,
  setSnapPoint: (snapPoint) => set({ snapPoint }),

  hoverInfo: null,
  setHoverInfo: (hoverInfo) => set({ hoverInfo }),

  hoverIfcProps: null,
  setHoverIfcProps: (hoverIfcProps) => set({ hoverIfcProps }),

  screenshots: [],
  addScreenshot: (dataUrl) => set((state) => ({ screenshots: [...state.screenshots, dataUrl] })),
  removeScreenshot: (index) => set((state) => ({
    screenshots: state.screenshots.filter((_, i) => i !== index),
  })),
  clearScreenshots: () => set({ screenshots: [] }),

  xrFeatures: loadXRFeatures(),
  setXRFeature: (key, value) => set((state) => {
    const next = { ...state.xrFeatures, [key]: value };
    saveXRFeatures(next);
    return { xrFeatures: next };
  }),
  resetXRFeatures: () => {
    const next = { ...DEFAULT_XR_FEATURES };
    saveXRFeatures(next);
    set({ xrFeatures: next });
  },
  ifcMaterialColors: loadIfcMaterialColors(),
  setIfcMaterialColors: (enabled: boolean) => {
    saveIfcMaterialColors(enabled);
    set((state) => {
      const mode = enabled ? 'material' : 'none';
      saveIfcColorMode(mode);
      return { ifcMaterialColors: enabled, ifcColorMode: mode };
    });
  },
  ifcColorMode: loadIfcColorMode(),
  setIfcColorMode: (mode: IFCColorMode) => {
    saveIfcColorMode(mode);
    const enabled = mode === 'material';
    saveIfcMaterialColors(enabled);
    set({ ifcColorMode: mode, ifcMaterialColors: enabled });
  },
}));

// Autodetecção global de suporte a WebXR
if (typeof navigator !== 'undefined' && navigator.xr) {
  navigator.xr.isSessionSupported('immersive-ar')
    .then((supported) => useModelStore.getState().setXrSupported(supported))
    .catch(() => useModelStore.getState().setXrSupported(false));
} else {
  useModelStore.getState().setXrSupported(false);
}
