import { createContext, useContext } from 'react';
import type { RenderMode } from '../components/Toolbar';
import type { MeshBuffers } from '../wasm/meshlab-core';

export interface MeshLayer {
  id: string;
  name: string;
  wasmHandle: number;
  visible: boolean;
  renderMode: RenderMode;
  color: string;
  vertexCount: number;
  faceCount: number;
  boundingBox: number[];
  fileSize: number;
  buffers: MeshBuffers;
  vertexColors?: Uint8Array;
  qualityRange?: [number, number];
  qualityLabel?: string;
  qualityColormap?: string;
}

export interface MeshSnapshot {
  layers: MeshLayer[];
  activeLayerId: string | null;
  label: string;
  timestamp: number;
}

export type ToolMode = 'navigate' | 'select_rect' | 'select_brush' | 'select_click'
  | 'measure_distance' | 'measure_angle' | 'measure_section'
  | 'paint' | 'pickpoints' | 'manipulator' | 'align';

export interface MeshState {
  layers: MeshLayer[];
  activeLayerId: string | null;
  undoStack: MeshSnapshot[];
  redoStack: MeshSnapshot[];
  statusMessage: string;
  showGrid: boolean;
  showBBox: boolean;
  globalRenderMode: RenderMode;
  bgColor: string;
  toolMode: ToolMode;
  selectedFaceCount: number;
  brushSize: number;
  // Render settings
  ssaoEnabled: boolean;
  shadowsEnabled: boolean;
  clippingEnabled: boolean;
  clippingAxis: 'X' | 'Y' | 'Z';
  clippingPosition: number; // 0-100 percent
  clippingFlipped: boolean;
  edgesEnabled: boolean;
  edgeAngle: number;
  matcapType: string;
  lightIntensity: number;
  // Sprint P: decoration toggles
  showNormals: boolean;
  showBoundary: boolean;
  showNonManifold: boolean;
  // View mode
  viewportMode: 'single' | 'split-h' | 'split-v';
}

export type MeshAction =
  | { type: 'ADD_LAYER'; layer: MeshLayer }
  | { type: 'REMOVE_LAYER'; id: string }
  | { type: 'CLEAR_ALL_LAYERS' }
  | { type: 'SET_ACTIVE_LAYER'; id: string }
  | { type: 'TOGGLE_VISIBILITY'; id: string }
  | { type: 'RENAME_LAYER'; id: string; name: string }
  | { type: 'UPDATE_LAYER'; id: string; updates: Partial<MeshLayer> }
  | { type: 'SET_RENDER_MODE'; mode: RenderMode }
  | { type: 'SET_BG_COLOR'; color: string }
  | { type: 'TOGGLE_GRID' }
  | { type: 'TOGGLE_BBOX' }
  | { type: 'SET_STATUS'; message: string }
  | { type: 'PUSH_UNDO'; label: string }
  | { type: 'UNDO' }
  | { type: 'REDO' }
  | { type: 'REPLACE_LAYERS'; layers: MeshLayer[]; activeLayerId: string | null }
  | { type: 'SET_TOOL_MODE'; mode: ToolMode }
  | { type: 'SET_SELECTED_FACE_COUNT'; count: number }
  | { type: 'SET_BRUSH_SIZE'; size: number }
  | { type: 'SET_RENDER_SETTING'; key: string; value: unknown };

const MAX_UNDO = 20;

function cloneLayersForSnapshot(layers: MeshLayer[]): MeshLayer[] {
  return layers.map((l) => ({
    ...l,
    buffers: {
      vertices: new Float32Array(l.buffers.vertices),
      indices: new Uint32Array(l.buffers.indices),
      normals: new Float32Array(l.buffers.normals),
    },
    boundingBox: [...l.boundingBox],
  }));
}

export const initialMeshState: MeshState = {
  layers: [],
  activeLayerId: null,
  undoStack: [],
  redoStack: [],
  statusMessage: 'Ready',
  showGrid: true,
  showBBox: false,
  globalRenderMode: 'solid',
  bgColor: 'dark',
  toolMode: 'navigate',
  selectedFaceCount: 0,
  brushSize: 30,
  ssaoEnabled: false,
  shadowsEnabled: false,
  clippingEnabled: false,
  clippingAxis: 'Y',
  clippingPosition: 50,
  clippingFlipped: false,
  edgesEnabled: false,
  edgeAngle: 30,
  matcapType: 'default',
  lightIntensity: 1.0,
  showNormals: false,
  showBoundary: false,
  showNonManifold: false,
  viewportMode: 'single' as const,
};

export function meshReducer(state: MeshState, action: MeshAction): MeshState {
  switch (action.type) {
    case 'ADD_LAYER':
      return {
        ...state,
        layers: [...state.layers, action.layer],
        activeLayerId: action.layer.id,
      };

    case 'REMOVE_LAYER': {
      const newLayers = state.layers.filter((l) => l.id !== action.id);
      const newActive =
        state.activeLayerId === action.id
          ? newLayers.length > 0
            ? newLayers[newLayers.length - 1].id
            : null
          : state.activeLayerId;
      return { ...state, layers: newLayers, activeLayerId: newActive };
    }

    case 'CLEAR_ALL_LAYERS':
      return { ...state, layers: [], activeLayerId: null };

    case 'SET_ACTIVE_LAYER':
      return { ...state, activeLayerId: action.id };

    case 'TOGGLE_VISIBILITY':
      return {
        ...state,
        layers: state.layers.map((l) =>
          l.id === action.id ? { ...l, visible: !l.visible } : l
        ),
      };

    case 'RENAME_LAYER':
      return {
        ...state,
        layers: state.layers.map((l) =>
          l.id === action.id ? { ...l, name: action.name } : l
        ),
      };

    case 'UPDATE_LAYER':
      return {
        ...state,
        layers: state.layers.map((l) =>
          l.id === action.id ? { ...l, ...action.updates } : l
        ),
      };

    case 'SET_RENDER_MODE':
      return { ...state, globalRenderMode: action.mode };

    case 'SET_BG_COLOR':
      return { ...state, bgColor: action.color };

    case 'TOGGLE_GRID':
      return { ...state, showGrid: !state.showGrid };

    case 'TOGGLE_BBOX':
      return { ...state, showBBox: !state.showBBox };

    case 'SET_STATUS':
      return { ...state, statusMessage: action.message };

    case 'PUSH_UNDO': {
      const snapshot: MeshSnapshot = {
        layers: cloneLayersForSnapshot(state.layers),
        activeLayerId: state.activeLayerId,
        label: action.label,
        timestamp: Date.now(),
      };
      const newStack = [...state.undoStack, snapshot].slice(-MAX_UNDO);
      return { ...state, undoStack: newStack, redoStack: [] };
    }

    case 'UNDO': {
      if (state.undoStack.length === 0) return state;
      const prev = state.undoStack[state.undoStack.length - 1];
      const currentSnapshot: MeshSnapshot = {
        layers: cloneLayersForSnapshot(state.layers),
        activeLayerId: state.activeLayerId,
        label: prev.label,
        timestamp: Date.now(),
      };
      return {
        ...state,
        layers: prev.layers,
        activeLayerId: prev.activeLayerId,
        undoStack: state.undoStack.slice(0, -1),
        redoStack: [...state.redoStack, currentSnapshot],
        statusMessage: `Undo: ${prev.label}`,
      };
    }

    case 'REDO': {
      if (state.redoStack.length === 0) return state;
      const next = state.redoStack[state.redoStack.length - 1];
      const currentSnapshot: MeshSnapshot = {
        layers: cloneLayersForSnapshot(state.layers),
        activeLayerId: state.activeLayerId,
        label: next.label,
        timestamp: Date.now(),
      };
      return {
        ...state,
        layers: next.layers,
        activeLayerId: next.activeLayerId,
        redoStack: state.redoStack.slice(0, -1),
        undoStack: [...state.undoStack, currentSnapshot],
        statusMessage: `Redo: ${next.label}`,
      };
    }

    case 'REPLACE_LAYERS':
      return {
        ...state,
        layers: action.layers,
        activeLayerId: action.activeLayerId,
      };

    case 'SET_TOOL_MODE':
      return { ...state, toolMode: action.mode };

    case 'SET_SELECTED_FACE_COUNT':
      return { ...state, selectedFaceCount: action.count };

    case 'SET_BRUSH_SIZE':
      return { ...state, brushSize: Math.max(5, Math.min(200, action.size)) };

    case 'SET_RENDER_SETTING':
      return { ...state, [action.key]: action.value };

    default:
      return state;
  }
}

export interface MeshContextValue {
  state: MeshState;
  dispatch: React.Dispatch<MeshAction>;
}

export const MeshContext = createContext<MeshContextValue | null>(null);

export function useMeshStore(): MeshContextValue {
  const ctx = useContext(MeshContext);
  if (!ctx) throw new Error('useMeshStore must be used within MeshContext.Provider');
  return ctx;
}
