import { create } from 'zustand';
import { applyNodeChanges, applyEdgeChanges, NodeChange, EdgeChange } from 'reactflow';
import { PANEL_DIMENSIONS, STORAGE_KEYS } from '../constants/dimensions';
import type { ErrorRecord } from '../utils/errorReporting';
import type { ComponentLabels, ModelerContext, ModelerDependencies } from '../types/settings';

// Type definitions

interface Node {
  id: string;
  type?: string;
  data: any;
  position: { x: number; y: number };
  [key: string]: any;
}

interface Edge {
  id: string;
  source: string;
  target: string;
  data?: any;
  [key: string]: any;
}

interface Component {
  type?: string;
  category?: string;
  provider?: string;
  label: string;
  plugin: string;
  description?: string;
  documentationUrl?: string | null;
  componentType?: number;
  [key: string]: any;
}

// Favorite components mapped by component type ID (1=Start, 4=Element, 5=Link)
type FavoriteComponents = Record<number, string[]>;

interface ModelData {
  id?: string;
  version?: string;
  metadata?: {
    label?: string;
    description?: string;
    executable?: boolean;
    tags?: string[];
    changelog?: string;
  };
  nodes?: Node[];
  edges?: Edge[];
  [key: string]: any;
}

interface ConfigModalData {
  [key: string]: any;
}

type SelectionSource = 'canvas' | 'replay' | 'none';

interface ViewportTarget {
  type: 'center' | 'fit' | 'none' | 'top-align';
  nodeId?: string;
  options?: {
    zoom?: number;
    duration?: number;
    padding?: number;
    nodes?: Node[];
  };
}

interface StoreState {
  // Nodes and edges - single source of truth
  nodes: Node[];
  edges: Edge[];
  setNodes: (nodes: Node[] | ((nodes: Node[]) => Node[])) => void;
  setEdges: (edges: Edge[] | ((edges: Edge[]) => Edge[])) => void;
  applyNodeChanges: (changes: NodeChange[]) => void;
  applyEdgeChanges: (changes: EdgeChange[]) => void;
  addNode: (node: Node) => void;
  addEdge: (edge: Edge) => void;
  removeNode: (nodeId: string) => void;
  removeEdge: (edgeId: string) => void;
  updateNode: (nodeId: string, updates: Partial<Node>) => void;
  updateEdge: (edgeId: string, updates: Partial<Edge>) => void;
  
  // Selection source tracking
  lastSelectionSource: SelectionSource;
  setLastSelectionSource: (source: SelectionSource) => void;
  
  // Selected node for property panel (single selection for properties)
  selectedNode: Node | null;
  setSelectedNode: (node: Node | null) => void;
  
  // Selected edge for property panel (single selection for properties)
  selectedEdge: Edge | null;
  setSelectedEdge: (edge: Edge | null) => void;
  
  // Multiple selection for nodes and edges
  selectedNodes: string[];
  selectedEdges: string[];
  setSelectedNodes: (nodes: string[]) => void;
  setSelectedEdges: (edges: string[]) => void;
  addToSelectedNodes: (nodeId: string) => void;
  removeFromSelectedNodes: (nodeId: string) => void;
  addToSelectedEdges: (edgeId: string) => void;
  removeFromSelectedEdges: (edgeId: string) => void;
  clearSelection: () => void;
  selectNode: (node: Node) => void;
  selectEdge: (edge: Edge) => void;
  
  // Available components from Drupal
  components: Component[];
  setComponents: (components: Component[]) => void;

  // Favorite components (plugin IDs organized by component type)
  favoriteComponents: FavoriteComponents;
  setFavoriteComponents: (favorites: FavoriteComponents) => void;
  
  // Current model data
  modelData: ModelData | null;
  setModelData: (data: ModelData | null | ((prev: ModelData | null) => ModelData | null)) => void;
  
  // Property panel UI state
  panelWidth: number;
  panelIsResizing: boolean;

  // Component panel width
  componentPanelWidth: number;
  componentPanelIsResizing: boolean;

  // Replay panel width
  replayPanelWidth: number;
  replayPanelIsResizing: boolean;

  // Panel collapse states
  componentPanelCollapsed: boolean;
  replayPanelCollapsed: boolean;
  propertyPanelCollapsed: boolean;

  setPanelWidth: (width: number) => void;
  setPanelResizing: (isResizing: boolean) => void;
  
  // Component panel resizing
  setComponentPanelWidth: (width: number) => void;
  setComponentPanelResizing: (isResizing: boolean) => void;
  
  // Replay panel resizing
  setReplayPanelWidth: (width: number) => void;
  setReplayPanelResizing: (isResizing: boolean) => void;
  
  // Panel collapse toggles and setters
  toggleComponentPanelCollapse: () => void;
  setComponentPanelCollapsed: (collapsed: boolean) => void;
  toggleReplayPanelCollapse: () => void;
  setReplayPanelCollapsed: (collapsed: boolean) => void;
  togglePropertyPanelCollapse: () => void;
  
  // Configuration modal state
  configModalOpen: boolean;
  configModalData: ConfigModalData | null;
  openConfigModal: (data: ConfigModalData) => void;
  closeConfigModal: () => void;
  
  // Viewport effects state  
  viewportTarget: ViewportTarget | null;
  setViewportTarget: (target: ViewportTarget | null) => void;

  // Dark mode
  darkMode: boolean;
  toggleDarkMode: () => void;

  // Context definitions from Drupal settings
  contexts: ModelerContext[];
  setContexts: (contexts: ModelerContext[]) => void;

  // Dependency definitions from Drupal settings (drupalSettings.modeler_api.dependencies)
  dependencies: ModelerDependencies;
  setDependencies: (dependencies: ModelerDependencies) => void;

  // Context selection (null = no context selected)
  selectedContextId: string | null;
  setSelectedContextId: (id: string | null) => void;

  // Context config: default key/value pairs for new component configurations
  contextConfig: Record<string, string>;
  setContextConfig: (config: Record<string, string>) => void;

  // Token dragging state (true while a token is being dragged from the replay panel)
  isTokenDragging: boolean;
  setTokenDragging: (isDragging: boolean) => void;

  // Start flow filter: IDs of start nodes whose flows should be visible.
  // null means "All" (no filtering applied).
  visibleStartNodeIds: string[] | null;
  setVisibleStartNodeIds: (ids: string[] | null) => void;

  // Component labels (model-owner-provided terminology)
  componentLabels: ComponentLabels;
  setComponentLabels: (labels: ComponentLabels) => void;

  // Error reporting
  errorLog: ErrorRecord[];
  addError: (record: ErrorRecord) => void;
  dismissError: (recordId: string) => void;
  clearErrors: () => void;
}

// Load initial panel width from localStorage
const loadPanelWidth = (): number => {
  try {
    const saved = localStorage.getItem(STORAGE_KEYS.PROPERTY_PANEL_WIDTH);
    if (saved) {
      const width = parseInt(saved, 10);
      if (!isNaN(width) && width >= PANEL_DIMENSIONS.PROPERTY_PANEL.MIN_WIDTH && width <= PANEL_DIMENSIONS.PROPERTY_PANEL.MAX_WIDTH) {
        return width;
      }
    }
  } catch (error) {
    console.warn('Failed to parse property panel width from localStorage:', error);
  }
  return PANEL_DIMENSIONS.PROPERTY_PANEL.DEFAULT_WIDTH;
};

const initialPanelWidth = loadPanelWidth();

export const useStore = create<StoreState>((set, get) => ({
  // Nodes and edges - single source of truth
  nodes: [],
  edges: [],
  
  setNodes: (nodes) => set(state => ({
    nodes: typeof nodes === 'function' ? nodes(state.nodes) : nodes
  })),
  
  setEdges: (edges) => set(state => ({
    edges: typeof edges === 'function' ? edges(state.edges) : edges
  })),
  
  applyNodeChanges: (changes) => set(state => ({
    nodes: applyNodeChanges(changes, state.nodes)
  })),
  
  applyEdgeChanges: (changes) => set(state => ({
    edges: applyEdgeChanges(changes, state.edges)
  })),
  
  addNode: (node) => set(state => ({
    nodes: [...state.nodes, node]
  })),
  
  addEdge: (edge) => set(state => ({
    edges: [...state.edges, edge]
  })),
  
  removeNode: (nodeId) => set(state => ({
    nodes: state.nodes.filter(n => n.id !== nodeId),
    // Also remove connected edges
    edges: state.edges.filter(e => e.source !== nodeId && e.target !== nodeId),
    // Clear selection if removed node was selected
    selectedNode: state.selectedNode?.id === nodeId ? null : state.selectedNode,
    selectedNodes: state.selectedNodes.filter(id => id !== nodeId)
  })),
  
  removeEdge: (edgeId) => set(state => ({
    edges: state.edges.filter(e => e.id !== edgeId),
    // Clear selection if removed edge was selected
    selectedEdge: state.selectedEdge?.id === edgeId ? null : state.selectedEdge,
    selectedEdges: state.selectedEdges.filter(id => id !== edgeId)
  })),
  
  updateNode: (nodeId, updates) => set(state => ({
    nodes: state.nodes.map(n => n.id === nodeId ? { ...n, ...updates } : n)
  })),
  
  updateEdge: (edgeId, updates) => set(state => ({
    edges: state.edges.map(e => e.id === edgeId ? { ...e, ...updates } : e)
  })),
  
  // Selection source tracking
  lastSelectionSource: 'none' as SelectionSource,
  setLastSelectionSource: (source) => set({ lastSelectionSource: source }),
  
  // Selected node for property panel (single selection for properties)
  selectedNode: null,
  setSelectedNode: (node) => set({ selectedNode: node }),
  
  // Selected edge for property panel (single selection for properties)
  selectedEdge: null,
  setSelectedEdge: (edge) => set({ selectedEdge: edge }),
  
  // Multiple selection for nodes and edges
  selectedNodes: [],
  selectedEdges: [],
  setSelectedNodes: (nodes) => set({ selectedNodes: nodes }),
  setSelectedEdges: (edges) => set({ selectedEdges: edges }),
  addToSelectedNodes: (nodeId) => set(state => ({ 
    selectedNodes: [...state.selectedNodes, nodeId] 
  })),
  removeFromSelectedNodes: (nodeId) => set(state => ({ 
    selectedNodes: state.selectedNodes.filter(id => id !== nodeId) 
  })),
  addToSelectedEdges: (edgeId) => set(state => ({ 
    selectedEdges: [...state.selectedEdges, edgeId] 
  })),
  removeFromSelectedEdges: (edgeId) => set(state => ({ 
    selectedEdges: state.selectedEdges.filter(id => id !== edgeId) 
  })),
  clearSelection: () => set({ 
    selectedNodes: [], 
    selectedEdges: [],
    selectedNode: null,
    selectedEdge: null
  }),
  selectNode: (node) => set({
    selectedNode: node,
    selectedEdge: null,
    selectedNodes: [node.id],
    selectedEdges: [],
  }),
  selectEdge: (edge) => set({
    selectedNode: null,
    selectedEdge: edge,
    selectedNodes: [],
    selectedEdges: [edge.id],
  }),
  
  // Available components from Drupal
  components: [],
  setComponents: (components) => set({ components }),

  // Favorite components (plugin IDs organized by component type)
  favoriteComponents: {},
  setFavoriteComponents: (favorites) => set({ favoriteComponents: favorites }),
  
  // Current model data
  modelData: null,
  setModelData: (data) => set((state) => ({ 
    modelData: typeof data === 'function' ? data(state.modelData) : data 
  })),
  
  // Property panel UI state
  panelWidth: initialPanelWidth,
  panelIsResizing: false,
  
  // Component panel width
  componentPanelWidth: parseInt(localStorage.getItem(STORAGE_KEYS.COMPONENT_PANEL_WIDTH) || String(PANEL_DIMENSIONS.COMPONENT_PANEL.DEFAULT_WIDTH)),
  componentPanelIsResizing: false,
  
  // Replay panel width  
  replayPanelWidth: parseInt(localStorage.getItem(STORAGE_KEYS.REPLAY_PANEL_WIDTH) || String(PANEL_DIMENSIONS.REPLAY_PANEL.DEFAULT_WIDTH)),
  replayPanelIsResizing: false,
  
  // Panel collapse states
  componentPanelCollapsed: JSON.parse(localStorage.getItem(STORAGE_KEYS.COMPONENT_PANEL_COLLAPSED) || 'false'),
  replayPanelCollapsed: JSON.parse(localStorage.getItem(STORAGE_KEYS.REPLAY_PANEL_COLLAPSED) || 'false'),
  propertyPanelCollapsed: JSON.parse(localStorage.getItem(STORAGE_KEYS.PROPERTY_PANEL_COLLAPSED) || 'false'),
  
  setPanelWidth: (width) => {
    const clampedWidth = Math.max(PANEL_DIMENSIONS.PROPERTY_PANEL.MIN_WIDTH, Math.min(PANEL_DIMENSIONS.PROPERTY_PANEL.MAX_WIDTH, width));
    set({ panelWidth: clampedWidth });
    localStorage.setItem(STORAGE_KEYS.PROPERTY_PANEL_WIDTH, clampedWidth.toString());
  },
  
  setPanelResizing: (isResizing) => set({ panelIsResizing: isResizing }),
  
  // Component panel resizing
  setComponentPanelWidth: (width) => {
    const clampedWidth = Math.max(PANEL_DIMENSIONS.COMPONENT_PANEL.MIN_WIDTH, Math.min(PANEL_DIMENSIONS.COMPONENT_PANEL.MAX_WIDTH, width));
    set({ componentPanelWidth: clampedWidth });
    localStorage.setItem(STORAGE_KEYS.COMPONENT_PANEL_WIDTH, clampedWidth.toString());
  },
  
  setComponentPanelResizing: (isResizing) => set({ componentPanelIsResizing: isResizing }),
  
  // Replay panel resizing
  setReplayPanelWidth: (width) => {
    const clampedWidth = Math.max(PANEL_DIMENSIONS.REPLAY_PANEL.MIN_WIDTH, Math.min(PANEL_DIMENSIONS.REPLAY_PANEL.MAX_WIDTH, width));
    set({ replayPanelWidth: clampedWidth });
    localStorage.setItem(STORAGE_KEYS.REPLAY_PANEL_WIDTH, clampedWidth.toString());
  },
  
  setReplayPanelResizing: (isResizing) => set({ replayPanelIsResizing: isResizing }),
  
  // Panel collapse toggles
  toggleComponentPanelCollapse: () => {
    const newState = !get().componentPanelCollapsed;
    set({ componentPanelCollapsed: newState });
    localStorage.setItem(STORAGE_KEYS.COMPONENT_PANEL_COLLAPSED, JSON.stringify(newState));
  },
  
  setComponentPanelCollapsed: (collapsed: boolean) => {
    set({ componentPanelCollapsed: collapsed });
    // Don't persist to localStorage - this is for initial state based on context
  },
  
  toggleReplayPanelCollapse: () => {
    const newState = !get().replayPanelCollapsed;
    set({ replayPanelCollapsed: newState });
    localStorage.setItem(STORAGE_KEYS.REPLAY_PANEL_COLLAPSED, JSON.stringify(newState));
  },

  setReplayPanelCollapsed: (collapsed: boolean) => {
    set({ replayPanelCollapsed: collapsed });
    localStorage.setItem(STORAGE_KEYS.REPLAY_PANEL_COLLAPSED, JSON.stringify(collapsed));
  },
  
  togglePropertyPanelCollapse: () => {
    const newState = !get().propertyPanelCollapsed;
    set({ propertyPanelCollapsed: newState });
    localStorage.setItem(STORAGE_KEYS.PROPERTY_PANEL_COLLAPSED, JSON.stringify(newState));
  },
  
  // Configuration modal state
  configModalOpen: false,
  configModalData: null,
  openConfigModal: (data) => set({ configModalOpen: true, configModalData: data }),
  closeConfigModal: () => set({ configModalOpen: false, configModalData: null }),
  
  // Viewport effects state
  viewportTarget: null,
  setViewportTarget: (target) => set({ viewportTarget: target }),

  // Dark mode (persisted to localStorage)
  darkMode: (() => {
    try {
      const saved = localStorage.getItem(STORAGE_KEYS.THEME);
      if (saved === 'dark') return true;
      if (saved === 'light') return false;
      // Fall back to OS preference
      return window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false;
    } catch {
      return false;
    }
  })(),
  toggleDarkMode: () => {
    const newState = !get().darkMode;
    set({ darkMode: newState });
    localStorage.setItem(STORAGE_KEYS.THEME, newState ? 'dark' : 'light');
  },

  // Context definitions
  contexts: [],
  setContexts: (contexts) => set({ contexts }),

  // Dependency definitions
  dependencies: {},
  setDependencies: (dependencies) => set({ dependencies }),

  // Context selection
  selectedContextId: null,
  setSelectedContextId: (id) => set({ selectedContextId: id }),

  // Context config: default key/value pairs for new component configurations
  contextConfig: {},
  setContextConfig: (config) => set({ contextConfig: config }),

  // Token dragging state
  isTokenDragging: false,
  setTokenDragging: (isDragging) => set({ isTokenDragging: isDragging }),

  // Start flow filter (null = show all)
  visibleStartNodeIds: null,
  setVisibleStartNodeIds: (ids) => set({ visibleStartNodeIds: ids }),

  // Component labels (model-owner-provided terminology)
  componentLabels: {},
  setComponentLabels: (labels) => set({ componentLabels: labels }),

  // Error reporting
  errorLog: [],
  addError: (record) => set((state) => ({
    errorLog: [...state.errorLog, record],
  })),
  dismissError: (recordId) => set((state) => ({
    errorLog: state.errorLog.map((r) =>
      r.id === recordId ? { ...r, dismissed: true } : r
    ),
  })),
  clearErrors: () => set({ errorLog: [] }),
}));

// Export types for use in other files
export type { Node, Edge, Component, ModelData, ViewportTarget };
