/**
 * Undo/Redo Utility
 * FE-STATE-006: Add undo/redo functionality for state changes
 * 
 * Provides middleware and utilities for implementing undo/redo functionality
 * in Zustand stores
 */

import { StateCreator, StoreMutatorIdentifier } from 'zustand';
import { logger } from './logger';

/**
 * Options for undo/redo middleware
 */
export interface UndoRedoOptions {
  /** Maximum number of history entries (default: 50) */
  maxHistorySize?: number;
  /** Whether to enable undo/redo (default: true) */
  enabled?: boolean;
  /** Function to determine if a state change should be tracked */
  shouldTrack?: (state: any, prevState: any) => boolean;
  /** Function to serialize state for history (for memory optimization) */
  serialize?: (state: any) => any;
  /** Function to deserialize state from history */
  deserialize?: (serialized: any) => any;
}

/**
 * History entry
 */
interface HistoryEntry {
  state: any;
  timestamp: number;
}

/**
 * Undo/Redo state
 */
interface UndoRedoState {
  history: HistoryEntry[];
  currentIndex: number;
  maxSize: number;
}

/**
 * Zustand middleware for undo/redo functionality
 * 
 * @example
 * ```typescript
 * export const useMyStore = create<MyState>()(
 *   undoRedo(
 *     (set, get) => ({
 *       // store implementation
 *     }),
 *     { maxHistorySize: 50 }
 *   )
 * );
 * ```
 */
export function undoRedo<T extends object>(
  config: StateCreator<T>,
  options: UndoRedoOptions = {},
): StateCreator<T & { undo: () => void; redo: () => void; canUndo: () => boolean; canRedo: () => boolean }> {
  const {
    maxHistorySize = 50,
    enabled = true,
    shouldTrack = () => true,
    serialize = (state) => JSON.parse(JSON.stringify(state)),
    deserialize = (serialized) => serialized,
  } = options;

  // History storage (outside the store to persist across updates)
  const historyState: UndoRedoState = {
    history: [],
    currentIndex: -1,
    maxSize: maxHistorySize,
  };

  return (set, get, api) => {
    let previousState: T | null = null;
    let isUndoRedo = false;

    // Create the store
    const store = config(
      (...args) => {
        if (!isUndoRedo) {
          set(...args);
          
          // Track state changes
          if (enabled) {
            const currentState = get();
            
            if (previousState !== null && shouldTrack(currentState, previousState)) {
              // Remove any future history if we're not at the end
              if (historyState.currentIndex < historyState.history.length - 1) {
                historyState.history = historyState.history.slice(0, historyState.currentIndex + 1);
              }

              // Add new history entry
              historyState.history.push({
                state: serialize(previousState),
                timestamp: Date.now(),
              });

              // Limit history size
              if (historyState.history.length > historyState.maxSize) {
                historyState.history.shift();
              } else {
                historyState.currentIndex++;
              }

              logger.debug('[UndoRedo] State change tracked', {
                historySize: historyState.history.length,
                currentIndex: historyState.currentIndex,
              });
            }

            previousState = serialize(currentState);
          }
        } else {
          set(...args);
        }
      },
      get,
      api,
    );

    // Add undo/redo methods
    return {
      ...store,
      undo: () => {
        if (!enabled || historyState.currentIndex < 0) {
          return;
        }

        isUndoRedo = true;
        const entry = historyState.history[historyState.currentIndex];
        if (entry) {
          const restoredState = deserialize(entry.state);
          set(() => restoredState);
          historyState.currentIndex--;
          previousState = serialize(restoredState);
          logger.debug('[UndoRedo] Undone', {
            currentIndex: historyState.currentIndex,
          });
        }
        isUndoRedo = false;
      },
      redo: () => {
        if (!enabled || historyState.currentIndex >= historyState.history.length - 1) {
          return;
        }

        isUndoRedo = true;
        historyState.currentIndex++;
        const entry = historyState.history[historyState.currentIndex];
        if (entry) {
          const restoredState = deserialize(entry.state);
          set(() => restoredState);
          previousState = serialize(restoredState);
          logger.debug('[UndoRedo] Redone', {
            currentIndex: historyState.currentIndex,
          });
        }
        isUndoRedo = false;
      },
      canUndo: () => {
        return enabled && historyState.currentIndex >= 0;
      },
      canRedo: () => {
        return enabled && historyState.currentIndex < historyState.history.length - 1;
      },
    } as T & { undo: () => void; redo: () => void; canUndo: () => boolean; canRedo: () => boolean };
  };
}

/**
 * FE-STATE-006: Hook to use undo/redo functionality
 * 
 * @example
 * ```typescript
 * function MyComponent() {
 *   const { undo, redo, canUndo, canRedo } = useUndoRedo(useMyStore);
 *   
 *   return (
 *     <div>
 *       <button onClick={undo} disabled={!canUndo()}>Undo</button>
 *       <button onClick={redo} disabled={!canRedo()}>Redo</button>
 *     </div>
 *   );
 * }
 * ```
 */
export function useUndoRedo<T extends { undo: () => void; redo: () => void; canUndo: () => boolean; canRedo: () => boolean }>(
  store: () => T,
): {
  undo: () => void;
  redo: () => void;
  canUndo: boolean;
  canRedo: boolean;
} {
  const storeInstance = store();
  
  return {
    undo: storeInstance.undo,
    redo: storeInstance.redo,
    canUndo: storeInstance.canUndo(),
    canRedo: storeInstance.canRedo(),
  };
}

/**
 * FE-STATE-006: Global undo/redo manager
 * 
 * Manages undo/redo across multiple stores
 */
class UndoRedoManager {
  private stores: Map<string, { undo: () => void; redo: () => void; canUndo: () => boolean; canRedo: () => boolean }> = new Map();

  /**
   * Register a store for undo/redo
   */
  register(storeName: string, store: { undo: () => void; redo: () => void; canUndo: () => boolean; canRedo: () => boolean }): void {
    this.stores.set(storeName, store);
  }

  /**
   * Unregister a store
   */
  unregister(storeName: string): void {
    this.stores.delete(storeName);
  }

  /**
   * Undo last change in a specific store or all stores
   */
  undo(storeName?: string): void {
    if (storeName) {
      const store = this.stores.get(storeName);
      if (store && store.canUndo()) {
        store.undo();
      }
    } else {
      // Undo in all stores (find the most recent change)
      let mostRecentStore: string | null = null;
      let mostRecentTime = 0;

      for (const [name, store] of this.stores.entries()) {
        if (store.canUndo()) {
          // We don't have timestamp info, so just undo in first available store
          // In a real implementation, you'd track timestamps
          mostRecentStore = name;
          break;
        }
      }

      if (mostRecentStore) {
        this.stores.get(mostRecentStore)?.undo();
      }
    }
  }

  /**
   * Redo last undone change in a specific store or all stores
   */
  redo(storeName?: string): void {
    if (storeName) {
      const store = this.stores.get(storeName);
      if (store && store.canRedo()) {
        store.redo();
      }
    } else {
      // Redo in all stores
      for (const store of this.stores.values()) {
        if (store.canRedo()) {
          store.redo();
          break; // Only redo in one store at a time
        }
      }
    }
  }

  /**
   * Check if undo is available
   */
  canUndo(storeName?: string): boolean {
    if (storeName) {
      const store = this.stores.get(storeName);
      return store ? store.canUndo() : false;
    }
    return Array.from(this.stores.values()).some((store) => store.canUndo());
  }

  /**
   * Check if redo is available
   */
  canRedo(storeName?: string): boolean {
    if (storeName) {
      const store = this.stores.get(storeName);
      return store ? store.canRedo() : false;
    }
    return Array.from(this.stores.values()).some((store) => store.canRedo());
  }
}

// Global instance
export const undoRedoManager = new UndoRedoManager();

