import { addEdge, applyNodeChanges, applyEdgeChanges, Node, Edge, Connection, NodeChange, EdgeChange } from '@xyflow/react';
import { createWithEqualityFn } from 'zustand/traditional';
import { nanoid } from 'nanoid';
import debounce from 'lodash/debounce';
import { GraphState } from './types';
import { initialEdges, initialNodes } from './data';

const MAX_HISTORY_LENGTH = 100;
const HISTORY_DEBOUNCE_MS = 100;

const useGraphStore = createWithEqualityFn<GraphState>((set, get) => {
    return {
        past: [],
        future: [],
        present: {
            nodes: initialNodes,
            edges: initialEdges,
        },
        record: (callback: () => void) => {
            const currentState = get().present;

            console.group('Recording new state');
            console.log('Current state:', currentState);
            console.log('Past states count:', get().past.length);
            console.log('Future states count:', get().future.length);

            set(state => {
                const newPast = [...state.past.slice(-MAX_HISTORY_LENGTH), currentState];
                console.log('New past states count:', newPast.length);
                console.groupEnd();
                return {
                    past: newPast,
                    future: [],
                };
            });

            callback();
        },

        undo: () => {
            const { past, present } = get();
            console.group('Undo operation');
            console.log('Current state:', present);
            console.log('Past states count:', past.length);

            if (past.length === 0) {
                console.warn('Cannot undo - no past states available');
                console.groupEnd();
                return;
            }

            const previous = past[past.length - 1];
            const newPast = past.slice(0, past.length - 1);

            console.log('Reverting to previous state:', previous);
            console.log('New past states count:', newPast.length);
            console.log('New future states count:', get().future.length + 1);
            console.groupEnd();

            set({
                past: newPast,
                present: previous,
                future: [present, ...get().future],
            });
        },

        redo: () => {
            const { future, present } = get();
            console.group('Redo operation');
            console.log('Current state:', present);
            console.log('Future states count:', future.length);

            if (future.length === 0) {
                console.warn('Cannot redo - no future states available');
                console.groupEnd();
                return;
            }

            const next = future[0];
            const newFuture = future.slice(1);

            console.log('Moving to next state:', next);
            console.log('New past states count:', get().past.length + 1);
            console.log('New future states count:', newFuture.length);
            console.groupEnd();

            set({
                past: [...get().past, present],
                present: next,
                future: newFuture,
            });
        },
        setNodes: (nodes: Node[]) => {
            set(state => ({
                present: {
                    nodes: nodes,
                    edges: state.present.edges
                }
            }));
        },
        setEdges: (edges: Edge[]) => {
            set(state => ({
                present: {
                    nodes: state.present.nodes,
                    edges: edges
                }
            }));
        },
        onNodesChange: (changes: NodeChange[]) => {
            set(state => ({
                present: {
                    nodes: applyNodeChanges(changes, state.present.nodes),
                    edges: state.present.edges
                }
            }))
        },

        onEdgesChange: (changes: EdgeChange[]) => {

            set(state => ({
                present: {
                    nodes: state.present.nodes,
                    edges: applyEdgeChanges(changes, state.present.edges)
                }
            }))
        },

        canUndo: () => get().past.length > 0,
        canRedo: () => get().future.length > 0,

        updateNode: (nodeId: string, data: any) => {
            const newNodes = get().present.nodes.map(node =>
                node.id === nodeId ? { ...node, data: { ...node.data, ...data } } : node
            );
            set({
                present: {
                    nodes: newNodes,
                    edges: get().present.edges
                }
            });
        },
        deleteNode: (nodeId: string) => {
            const newNodes = get().present.nodes.filter(node => node.id !== nodeId);
            const newEdges = get().present.edges.filter(
                edge => edge.source !== nodeId && edge.target !== nodeId
            );
        },

        updateEdge: (edgeId: string, data: any) => {
            const newEdges = get().present.edges.map(edge =>
                edge.id === edgeId ? { ...edge, data: { ...edge.data, ...data } } : edge
            );
        },

    };
});

export default useGraphStore;