/**
 * Section Editor Context
 * Provides state management for the section editor with real-time preview
 */

"use client";

import React, {
  createContext,
  useContext,
  useCallback,
  useReducer,
  useMemo,
  useEffect,
} from "react";
import {
  EditorState,
  EditorConfig,
  BaseSection,
  EditorEvent,
  EditorEventHandler,
  ClipboardData,
  HistoryEntry,
  DEFAULT_EDITOR_CONFIG,
} from "./types";

// Action Types
type EditorActionType =
  | { type: "SET_SECTIONS"; payload: BaseSection[] }
  | { type: "ADD_SECTION"; payload: { section: BaseSection; index?: number } }
  | {
      type: "UPDATE_SECTION";
      payload: { sectionId: string; updates: Partial<BaseSection> };
    }
  | { type: "DELETE_SECTION"; payload: string }
  | { type: "MOVE_SECTION"; payload: { sectionId: string; newIndex: number } }
  | {
      type: "DUPLICATE_SECTION";
      payload: { sectionId: string; newIndex?: number };
    }
  | { type: "SELECT_SECTION"; payload: string | null }
  | { type: "SELECT_ELEMENT"; payload: string | null }
  | { type: "SET_CONFIG"; payload: EditorConfig }
  | { type: "UPDATE_CONFIG"; payload: Partial<EditorConfig> }
  | { type: "SET_CLIPBOARD"; payload: ClipboardData | null }
  | { type: "ADD_HISTORY"; payload: HistoryEntry }
  | { type: "UNDO" }
  | { type: "REDO" }
  | { type: "SET_DIRTY"; payload: boolean }
  | { type: "SET_LOADING"; payload: boolean }
  | { type: "SET_ERROR"; payload: string | null };

// Initial State
const initialState: EditorState = {
  sections: [],
  selectedSection: null,
  selectedElement: null,
  clipboard: null,
  history: [],
  historyIndex: -1,
  isDirty: false,
  isLoading: false,
  error: null,
};

// Reducer
function editorReducer(
  state: EditorState,
  action: EditorActionType
): EditorState {
  switch (action.type) {
    case "SET_SECTIONS":
      return {
        ...state,
        sections: action.payload,
        isDirty: false,
      };

    case "ADD_SECTION": {
      const { section, index = state.sections.length } = action.payload;
      const newSections = [...state.sections];
      newSections.splice(index, 0, section);

      // Update order for all sections
      const orderedSections = newSections.map((s, i) => ({ ...s, order: i }));

      return {
        ...state,
        sections: orderedSections,
        selectedSection: section.id,
        isDirty: true,
      };
    }

    case "UPDATE_SECTION": {
      const { sectionId, updates } = action.payload;
      const sections = state.sections.map((section) =>
        section.id === sectionId
          ? {
              ...section,
              ...updates,
              metadata: { ...section.metadata, updatedAt: new Date() },
            }
          : section
      );

      return {
        ...state,
        sections,
        isDirty: true,
      };
    }

    case "DELETE_SECTION": {
      const sections = state.sections
        .filter((section) => section.id !== action.payload)
        .map((section, index) => ({ ...section, order: index }));

      return {
        ...state,
        sections,
        selectedSection:
          state.selectedSection === action.payload
            ? null
            : state.selectedSection,
        isDirty: true,
      };
    }

    case "MOVE_SECTION": {
      const { sectionId, newIndex } = action.payload;
      const sections = [...state.sections];
      const currentIndex = sections.findIndex((s) => s.id === sectionId);

      if (currentIndex === -1) return state;

      const [movedSection] = sections.splice(currentIndex, 1);
      sections.splice(newIndex, 0, movedSection);

      // Update order for all sections
      const orderedSections = sections.map((s, i) => ({ ...s, order: i }));

      return {
        ...state,
        sections: orderedSections,
        isDirty: true,
      };
    }

    case "DUPLICATE_SECTION": {
      const { sectionId, newIndex } = action.payload;
      const originalSection = state.sections.find((s) => s.id === sectionId);
      if (!originalSection) return state;

      const duplicatedSection: BaseSection = {
        ...originalSection,
        id: `${originalSection.id}_copy_${Date.now()}`,
        name: `${originalSection.name} (Copy)`,
        metadata: {
          ...originalSection.metadata,
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      };

      const insertIndex = newIndex ?? originalSection.order + 1;
      const newSections = [...state.sections];
      newSections.splice(insertIndex, 0, duplicatedSection);

      // Update order for all sections
      const orderedSections = newSections.map((s, i) => ({ ...s, order: i }));

      return {
        ...state,
        sections: orderedSections,
        selectedSection: duplicatedSection.id,
        isDirty: true,
      };
    }

    case "SELECT_SECTION":
      return {
        ...state,
        selectedSection: action.payload,
        selectedElement: null,
      };

    case "SELECT_ELEMENT":
      return {
        ...state,
        selectedElement: action.payload,
      };

    case "SET_CONFIG":
      return {
        ...state,
        config: action.payload,
      };

    case "UPDATE_CONFIG":
      return {
        ...state,
        config: { ...state.config, ...action.payload },
      };

    case "SET_CLIPBOARD":
      return {
        ...state,
        clipboard: action.payload,
      };

    case "ADD_HISTORY": {
      const newHistory = [
        ...state.history.slice(0, state.historyIndex + 1),
        action.payload,
      ];

      // Limit history size
      const maxHistory = 50; // You can make this configurable
      if (newHistory.length > maxHistory) {
        newHistory.shift();
      }

      return {
        ...state,
        history: newHistory,
        historyIndex: newHistory.length - 1,
      };
    }

    case "UNDO": {
      if (state.historyIndex < 0) return state;

      const historyEntry = state.history[state.historyIndex];
      let newSections = [...state.sections];

      // Apply undo based on action type
      switch (historyEntry.action) {
        case "add":
          newSections = newSections.filter(
            (s) => s.id !== (historyEntry.after as BaseSection).id
          );
          break;
        case "update":
          newSections = newSections.map((s) =>
            s.id === (historyEntry.before as BaseSection).id
              ? (historyEntry.before as BaseSection)
              : s
          );
          break;
        case "delete":
          // Note: This is simplified - in real implementation you'd need to restore position
          newSections.push(historyEntry.before as BaseSection);
          break;
      }

      return {
        ...state,
        sections: newSections,
        historyIndex: state.historyIndex - 1,
        isDirty: true,
      };
    }

    case "REDO": {
      if (state.historyIndex >= state.history.length - 1) return state;

      const historyEntry = state.history[state.historyIndex + 1];
      let newSections = [...state.sections];

      // Apply redo based on action type
      switch (historyEntry.action) {
        case "add":
          newSections.push(historyEntry.after as BaseSection);
          break;
        case "update":
          newSections = newSections.map((s) =>
            s.id === (historyEntry.after as BaseSection).id
              ? (historyEntry.after as BaseSection)
              : s
          );
          break;
        case "delete":
          newSections = newSections.filter(
            (s) => s.id !== (historyEntry.before as BaseSection).id
          );
          break;
      }

      return {
        ...state,
        sections: newSections,
        historyIndex: state.historyIndex + 1,
        isDirty: true,
      };
    }

    case "SET_DIRTY":
      return {
        ...state,
        isDirty: action.payload,
      };

    case "SET_LOADING":
      return {
        ...state,
        isLoading: action.payload,
      };

    case "SET_ERROR":
      return {
        ...state,
        error: action.payload,
      };

    default:
      return state;
  }
}

// Context
interface EditorContextValue {
  // State
  state: EditorState;
  config: EditorConfig;

  // Section management
  setSections: (sections: BaseSection[]) => void;
  addSection: (section: BaseSection, index?: number) => void;
  updateSection: (sectionId: string, updates: Partial<BaseSection>) => void;
  deleteSection: (sectionId: string) => void;
  moveSection: (sectionId: string, newIndex: number) => void;
  duplicateSection: (sectionId: string, newIndex?: number) => void;

  // Selection
  selectSection: (sectionId: string | null) => void;
  selectElement: (elementId: string | null) => void;

  // Configuration
  setConfig: (config: EditorConfig) => void;
  updateConfig: (updates: Partial<EditorConfig>) => void;

  // Clipboard
  copySection: (sectionId: string) => void;
  pasteSection: (index?: number) => void;

  // History
  undo: () => void;
  redo: () => void;
  canUndo: boolean;
  canRedo: boolean;

  // Utility
  setDirty: (dirty: boolean) => void;
  setLoading: (loading: boolean) => void;
  setError: (error: string | null) => void;

  // Events
  emitEvent: (event: Omit<EditorEvent, "timestamp">) => void;
  addEventListener: (handler: EditorEventHandler) => () => void;
}

const EditorContext = createContext<EditorContextValue | null>(null);

// Provider Props
interface EditorProviderProps {
  children: React.ReactNode;
  initialSections?: BaseSection[];
  initialConfig?: Partial<EditorConfig>;
  onSectionsChange?: (sections: BaseSection[]) => void;
  onConfigChange?: (config: EditorConfig) => void;
  onEvent?: EditorEventHandler;
}

// Provider Component
export function EditorProvider({
  children,
  initialSections = [],
  initialConfig = {},
  onSectionsChange,
  onConfigChange,
  onEvent,
}: EditorProviderProps) {
  const [state, dispatch] = useReducer(editorReducer, {
    ...initialState,
    sections: initialSections,
  });

  const [config, setConfigState] = React.useState<EditorConfig>({
    ...DEFAULT_EDITOR_CONFIG,
    ...initialConfig,
  });

  const [eventHandlers, setEventHandlers] = React.useState<
    EditorEventHandler[]
  >([]);

  // Section management
  const setSections = useCallback((sections: BaseSection[]) => {
    dispatch({ type: "SET_SECTIONS", payload: sections });
  }, []);

  const addSection = useCallback((section: BaseSection, index?: number) => {
    const historyEntry: HistoryEntry = {
      id: `add_${Date.now()}`,
      action: "add",
      target: "section",
      before: null,
      after: section,
      timestamp: new Date(),
    };

    dispatch({ type: "ADD_SECTION", payload: { section, index } });
    dispatch({ type: "ADD_HISTORY", payload: historyEntry });
  }, []);

  const updateSection = useCallback(
    (sectionId: string, updates: Partial<BaseSection>) => {
      const currentSection = state.sections.find((s) => s.id === sectionId);
      if (!currentSection) return;

      const historyEntry: HistoryEntry = {
        id: `update_${Date.now()}`,
        action: "update",
        target: "section",
        before: currentSection,
        after: { ...currentSection, ...updates },
        timestamp: new Date(),
      };

      dispatch({ type: "UPDATE_SECTION", payload: { sectionId, updates } });
      dispatch({ type: "ADD_HISTORY", payload: historyEntry });
    },
    [state.sections]
  );

  const deleteSection = useCallback(
    (sectionId: string) => {
      const currentSection = state.sections.find((s) => s.id === sectionId);
      if (!currentSection) return;

      const historyEntry: HistoryEntry = {
        id: `delete_${Date.now()}`,
        action: "delete",
        target: "section",
        before: currentSection,
        after: null,
        timestamp: new Date(),
      };

      dispatch({ type: "DELETE_SECTION", payload: sectionId });
      dispatch({ type: "ADD_HISTORY", payload: historyEntry });
    },
    [state.sections]
  );

  const moveSection = useCallback((sectionId: string, newIndex: number) => {
    dispatch({ type: "MOVE_SECTION", payload: { sectionId, newIndex } });
  }, []);

  const duplicateSection = useCallback(
    (sectionId: string, newIndex?: number) => {
      dispatch({ type: "DUPLICATE_SECTION", payload: { sectionId, newIndex } });
    },
    []
  );

  // Selection
  const selectSection = useCallback((sectionId: string | null) => {
    dispatch({ type: "SELECT_SECTION", payload: sectionId });
  }, []);

  const selectElement = useCallback((elementId: string | null) => {
    dispatch({ type: "SELECT_ELEMENT", payload: elementId });
  }, []);

  // Configuration
  const setConfig = useCallback(
    (newConfig: EditorConfig) => {
      setConfigState(newConfig);
      onConfigChange?.(newConfig);
    },
    [onConfigChange]
  );

  const updateConfig = useCallback(
    (updates: Partial<EditorConfig>) => {
      const newConfig = { ...config, ...updates };
      setConfigState(newConfig);
      onConfigChange?.(newConfig);
    },
    [config, onConfigChange]
  );

  // Clipboard
  const copySection = useCallback(
    (sectionId: string) => {
      const section = state.sections.find((s) => s.id === sectionId);
      if (!section) return;

      const clipboardData: ClipboardData = {
        type: "section",
        data: section,
        timestamp: new Date(),
      };

      dispatch({ type: "SET_CLIPBOARD", payload: clipboardData });
    },
    [state.sections]
  );

  const pasteSection = useCallback(
    (index?: number) => {
      if (!state.clipboard || state.clipboard.type !== "section") return;

      const originalSection = state.clipboard.data as BaseSection;
      const newSection: BaseSection = {
        ...originalSection,
        id: `${originalSection.id}_paste_${Date.now()}`,
        name: `${originalSection.name} (Pasted)`,
        metadata: {
          ...originalSection.metadata,
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      };

      addSection(newSection, index);
    },
    [state.clipboard, addSection]
  );

  // History
  const undo = useCallback(() => {
    dispatch({ type: "UNDO" });
  }, []);

  const redo = useCallback(() => {
    dispatch({ type: "REDO" });
  }, []);

  const canUndo = state.historyIndex >= 0;
  const canRedo = state.historyIndex < state.history.length - 1;

  // Utility
  const setDirty = useCallback((dirty: boolean) => {
    dispatch({ type: "SET_DIRTY", payload: dirty });
  }, []);

  const setLoading = useCallback((loading: boolean) => {
    dispatch({ type: "SET_LOADING", payload: loading });
  }, []);

  const setError = useCallback((error: string | null) => {
    dispatch({ type: "SET_ERROR", payload: error });
  }, []);

  // Events
  const emitEvent = useCallback(
    (event: Omit<EditorEvent, "timestamp">) => {
      const fullEvent: EditorEvent = {
        ...event,
        timestamp: new Date(),
      };

      eventHandlers.forEach((handler) => handler(fullEvent));
      onEvent?.(fullEvent);
    },
    [eventHandlers, onEvent]
  );

  const addEventListener = useCallback(
    (handler: EditorEventHandler): (() => void) => {
      setEventHandlers((prev) => [...prev, handler]);

      return () => {
        setEventHandlers((prev) => prev.filter((h) => h !== handler));
      };
    },
    []
  );

  // Effects
  useEffect(() => {
    onSectionsChange?.(state.sections);
  }, [state.sections, onSectionsChange]);

  // Auto-save
  useEffect(() => {
    if (!config.autoSave || !state.isDirty) return;

    const timeoutId = setTimeout(() => {
      // Auto-save logic would go here
      console.log("Auto-saving sections...", state.sections);
      setDirty(false);
    }, config.autoSaveInterval);

    return () => clearTimeout(timeoutId);
  }, [
    state.sections,
    state.isDirty,
    config.autoSave,
    config.autoSaveInterval,
    setDirty,
  ]);

  // Context value
  const contextValue: EditorContextValue = useMemo(
    () => ({
      state,
      config,
      setSections,
      addSection,
      updateSection,
      deleteSection,
      moveSection,
      duplicateSection,
      selectSection,
      selectElement,
      setConfig,
      updateConfig,
      copySection,
      pasteSection,
      undo,
      redo,
      canUndo,
      canRedo,
      setDirty,
      setLoading,
      setError,
      emitEvent,
      addEventListener,
    }),
    [
      state,
      config,
      setSections,
      addSection,
      updateSection,
      deleteSection,
      moveSection,
      duplicateSection,
      selectSection,
      selectElement,
      setConfig,
      updateConfig,
      copySection,
      pasteSection,
      undo,
      redo,
      canUndo,
      canRedo,
      setDirty,
      setLoading,
      setError,
      emitEvent,
      addEventListener,
    ]
  );

  return (
    <EditorContext.Provider value={contextValue}>
      {children}
    </EditorContext.Provider>
  );
}

// Hook
export function useEditor(): EditorContextValue {
  const context = useContext(EditorContext);
  if (!context) {
    throw new Error("useEditor must be used within an EditorProvider");
  }
  return context;
}

export default EditorProvider;
