import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
import { UnifiedLayoutManager, PageLayout, WidgetInstance, LayoutContainer } from '@/modules/layout/LayoutManager';
import { HistoryManager } from '@/modules/layout/HistoryManager';
import { Command } from '@/modules/layout/types';
import {
  AddWidgetCommand,
  RemoveWidgetCommand,
  UpdateWidgetSettingsCommand,
  AddContainerCommand,
  RemoveContainerCommand,
  MoveContainerCommand,
  MoveWidgetCommand,
  UpdateContainerColumnsCommand,
  UpdateContainerSettingsCommand,
  RenameWidgetCommand,
  ReplaceLayoutCommand
} from '@/modules/layout/commands';


interface LayoutContextType {
  // Generic page management
  loadPageLayout: (pageId: string, defaultName?: string) => Promise<void>;
  getLoadedPageLayout: (pageId: string) => PageLayout | null;
  clearPageLayout: (pageId: string) => Promise<void>;

  // Generic page actions
  addWidgetToPage: (pageId: string, containerId: string, widgetId: string, targetColumn?: number, initialProps?: Record<string, any>) => Promise<WidgetInstance>;
  removeWidgetFromPage: (pageId: string, widgetInstanceId: string) => Promise<void>;
  moveWidgetInPage: (pageId: string, widgetInstanceId: string, direction: 'up' | 'down' | 'left' | 'right') => Promise<void>;
  updatePageContainerColumns: (pageId: string, containerId: string, columns: number) => Promise<void>;
  updatePageContainerSettings: (pageId: string, containerId: string, settings: Partial<LayoutContainer['settings']>) => Promise<void>;
  addPageContainer: (pageId: string, parentContainerId?: string) => Promise<LayoutContainer>;
  removePageContainer: (pageId: string, containerId: string) => Promise<void>;
  movePageContainer: (pageId: string, containerId: string, direction: 'up' | 'down') => Promise<void>;
  updateWidgetProps: (pageId: string, widgetInstanceId: string, props: Record<string, any>) => Promise<void>;
  renameWidget: (pageId: string, widgetInstanceId: string, newId: string) => Promise<boolean>;
  exportPageLayout: (pageId: string) => Promise<string>;
  importPageLayout: (pageId: string, jsonData: string) => Promise<PageLayout>;
  hydratePageLayout: (pageId: string, layout: PageLayout) => void;

  // Manual save
  saveToApi: () => Promise<boolean>;

  // History Actions
  undo: () => Promise<void>;
  redo: () => Promise<void>;
  canUndo: boolean;
  canRedo: boolean;
  clearHistory: () => void;
  executeCommand: (command: Command) => Promise<void>;

  // State
  isLoading: boolean;
  loadedPages: Map<string, PageLayout>;
}

const LayoutContext = createContext<LayoutContextType | undefined>(undefined);

interface LayoutProviderProps {
  children: ReactNode;
}

export const LayoutProvider: React.FC<LayoutProviderProps> = ({ children }) => {
  const [loadedPages, setLoadedPages] = useState<Map<string, PageLayout>>(new Map());
  const [isLoading, setIsLoading] = useState(true);

  // History State
  const [canUndo, setCanUndo] = useState(false);
  const [canRedo, setCanRedo] = useState(false);

  // Pending Metadata State
  const [pendingMetadata, setPendingMetadata] = useState<Map<string, Record<string, any>>>(new Map());

  const updateLayoutCallback = useCallback((pageId: string, layout: PageLayout) => {
    setLoadedPages(prev => new Map(prev).set(pageId, layout));
    // UnifiedLayoutManager.savePageLayout(layout).catch(e => console.error("Failed to persist layout update", e));
  }, []);

  const updatePageMetadataCallback = useCallback((pageId: string, metadata: Record<string, any>) => {
    setPendingMetadata(prev => {
      const newMap = new Map(prev);
      const existing = newMap.get(pageId) || {};
      newMap.set(pageId, { ...existing, ...metadata });
      return newMap;
    });
  }, []);

  const [historyManager] = useState(() => new HistoryManager({
    pageId: '',
    layouts: loadedPages,
    updateLayout: updateLayoutCallback,
    pageMetadata: pendingMetadata,
    updatePageMetadata: updatePageMetadataCallback
  }));

  // Update history manager context when state changes
  useEffect(() => {
    // We need to update the context inside historyManager so it has access to latest state
    // but HistoryManager doesn't preserve context reference, it uses it per execute/undo/redo.
    // However, for 'executeCommand' prop below, we construct a fresh context.
  }, [loadedPages, pendingMetadata, updateLayoutCallback, updatePageMetadataCallback]);


  const updateHistoryState = useCallback(() => {
    setCanUndo(historyManager.canUndo());
    setCanRedo(historyManager.canRedo());
  }, [historyManager]);

  useEffect(() => {
    updateHistoryState();
  }, [historyManager, loadedPages, pendingMetadata]);

  // Load layout
  const loadPageLayout = useCallback(async (pageId: string, defaultName?: string) => {
    try {
      const layout = await UnifiedLayoutManager.getPageLayout(pageId, defaultName);
      setLoadedPages(prev => new Map(prev).set(pageId, layout));
    } catch (e) {
      console.error("Failed to load page layout", e);
    } finally {
      setIsLoading(false);
    }
  }, []);

  const getLoadedPageLayout = useCallback((pageId: string) => {
    return loadedPages.get(pageId) || null;
  }, [loadedPages]);

  const clearPageLayout = useCallback(async (pageId: string) => {
    setLoadedPages(prev => {
      const next = new Map(prev);
      next.delete(pageId);
      return next;
    });
  }, []);

  // Widget Actions
  const addWidgetToPage = useCallback(async (pageId: string, containerId: string, widgetId: string, targetColumn?: number, initialProps?: Record<string, any>) => {
    const layout = loadedPages.get(pageId);
    if (!layout) throw new Error("Layout not loaded");

    const widgetInstance = UnifiedLayoutManager.createWidgetInstance(widgetId);
    if (initialProps) {
      widgetInstance.props = { ...widgetInstance.props, ...initialProps };
    }

    // Use Command for Undo/Redo
    const command = new AddWidgetCommand(pageId, containerId, widgetInstance, -1); // Index handling might need improvement if strict positioning required
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
    return widgetInstance;
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const removeWidgetFromPage = useCallback(async (pageId: string, widgetInstanceId: string) => {
    const command = new RemoveWidgetCommand(pageId, widgetInstanceId);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const updateWidgetProps = useCallback(async (pageId: string, widgetInstanceId: string, props: Record<string, any>) => {
    const command = new UpdateWidgetSettingsCommand(pageId, widgetInstanceId, props);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  // Other Actions (Direct Update for now)
  const moveWidgetInPage = useCallback(async (pageId: string, widgetInstanceId: string, direction: 'up' | 'down' | 'left' | 'right') => {
    const layout = loadedPages.get(pageId);
    if (!layout) return;

    const command = new MoveWidgetCommand(pageId, widgetInstanceId, direction);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const updatePageContainerColumns = useCallback(async (pageId: string, containerId: string, columns: number) => {
    const layout = loadedPages.get(pageId);
    if (!layout) return;
    const command = new UpdateContainerColumnsCommand(pageId, containerId, columns);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const updatePageContainerSettings = useCallback(async (pageId: string, containerId: string, settings: Partial<LayoutContainer['settings']>) => {
    const layout = loadedPages.get(pageId);
    if (!layout) return;
    const command = new UpdateContainerSettingsCommand(pageId, containerId, settings);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const addPageContainer = useCallback(async (pageId: string, parentContainerId?: string) => {
    const layout = loadedPages.get(pageId);
    if (!layout) throw new Error("Layout not loaded");
    // Create container instance first (pure)
    // UnifiedLayoutManager.addContainer mutates, checking if we can generate detached container?
    // We can't easily generate detached container with UnifiedLayoutManager.addContainer without a layout.
    // So we manually create it here or use a helper that doesn't attach.
    // UnifiedLayoutManager.generateContainerId is static.

    // Manual creation to follow command pattern:
    const newContainer: LayoutContainer = {
      id: UnifiedLayoutManager.generateContainerId(),
      type: 'container',
      columns: 1,
      gap: 16,
      widgets: [],
      children: [],
      order: 0 // Command execution will set order
    };

    const command = new AddContainerCommand(pageId, newContainer, parentContainerId);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
    return newContainer;
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const removePageContainer = useCallback(async (pageId: string, containerId: string) => {
    const layout = loadedPages.get(pageId);
    if (!layout) return;
    const command = new RemoveContainerCommand(pageId, containerId);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const movePageContainer = useCallback(async (pageId: string, containerId: string, direction: 'up' | 'down') => {
    const layout = loadedPages.get(pageId);
    if (!layout) return;
    const command = new MoveContainerCommand(pageId, containerId, direction);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const renameWidget = useCallback(async (pageId: string, widgetInstanceId: string, newId: string) => {
    const layout = loadedPages.get(pageId);
    if (!layout) return false;
    const command = new RenameWidgetCommand(pageId, widgetInstanceId, newId);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
    return true; // Command execution is async void, we assume success if no throw
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const exportPageLayout = useCallback(async (pageId: string) => {
    return UnifiedLayoutManager.exportPageLayout(pageId);
  }, []);

  const importPageLayout = useCallback(async (pageId: string, jsonData: string) => {
    // Use parseLayoutJSON (pure) then execute ReplaceLayoutCommand
    const parsedLayout = UnifiedLayoutManager.parseLayoutJSON(jsonData, pageId);

    const command = new ReplaceLayoutCommand(pageId, parsedLayout);
    await historyManager.execute(command, {
      pageId,
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
    return parsedLayout;
  }, [loadedPages, historyManager, updateLayoutCallback, pendingMetadata, updatePageMetadataCallback, updateHistoryState]);

  const hydratePageLayout = useCallback((pageId: string, layout: PageLayout) => {
    setLoadedPages(prev => new Map(prev).set(pageId, layout));
  }, []);

  const saveToApi = useCallback(async (): Promise<boolean> => {
    console.warn("LayoutContext.saveToApi is deprecated. Storage logic has moved to UserPageEdit and db.ts.");
    return true;
  }, []);

  const undo = async () => {
    await historyManager.undo({
      pageId: '',
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
  };

  const redo = async () => {
    await historyManager.redo({
      pageId: '',
      layouts: loadedPages,
      updateLayout: updateLayoutCallback,
      pageMetadata: pendingMetadata,
      updatePageMetadata: updatePageMetadataCallback
    });
    updateHistoryState();
  };

  const clearHistory = useCallback(() => {
    historyManager.clear();
    updateHistoryState();
  }, [historyManager, updateHistoryState]);

  return (
    <LayoutContext.Provider value={{
      // ... existing values ...
      loadPageLayout,
      getLoadedPageLayout,
      clearPageLayout,
      addWidgetToPage,
      removeWidgetFromPage,
      moveWidgetInPage,
      updatePageContainerColumns,
      updatePageContainerSettings,
      addPageContainer,
      removePageContainer,
      movePageContainer,
      updateWidgetProps,
      renameWidget,
      saveToApi,
      exportPageLayout,
      importPageLayout,
      hydratePageLayout,
      isLoading,
      loadedPages,
      undo,
      redo,
      canUndo,
      canRedo,
      clearHistory,
      executeCommand: async (command: Command) => {
        await historyManager.execute(command, {
          pageId: '',
          layouts: loadedPages,
          updateLayout: updateLayoutCallback,
          pageMetadata: pendingMetadata,
          updatePageMetadata: updatePageMetadataCallback
        });
        updateHistoryState();
      }
    }}>
      {children}
    </LayoutContext.Provider>
  );
};

export const useLayout = (): LayoutContextType => {
  const context = useContext(LayoutContext);
  if (context === undefined) {
    throw new Error('useLayout must be used within a LayoutProvider');
  }
  return context;
};