/**
 * useConstructorElements Hook
 *
 * Manages element CRUD operations with defaults merging.
 * Used in constructor.tsx for adding, updating, and removing UI elements.
 */

import { useState, useCallback, useMemo } from 'react';
import type {
  CanvasElement,
  CanvasElementType,
  GalleryCard,
  GalleryInfoSpan,
  CarouselSlide,
} from '../types/constructor';
import {
  createDefaultElement,
  createLocalId,
  mergeElementWithDefaults,
  isGalleryElementType,
  isCarouselElementType,
  isNavigationElementType,
  getNavigationButtonLabel,
  getNavigationButtonKind,
  ELEMENT_TYPE_LABELS,
} from '../lib/elementDefaults';

type NavigationElementType = Extract<
  CanvasElementType,
  'navigation_next' | 'navigation_prev'
>;

interface UseConstructorElementsOptions {
  /** Initial elements array */
  initialElements?: CanvasElement[];
  /** Element defaults by type from project_element_defaults */
  elementDefaultsByType?: Partial<
    Record<CanvasElementType, Partial<CanvasElement>>
  >;
  /** Allowed navigation types for this context */
  allowedNavigationTypes?: NavigationElementType[];
  /** Callback when elements change */
  onElementsChange?: (elements: CanvasElement[]) => void;

  // Constructor-specific callbacks
  /** Initial selected element ID (for route parameter) */
  initialSelectedElementId?: string;
  /** Callback when an element is selected */
  onElementSelected?: (elementId: string) => void;
  /** Callback when selection is cleared */
  onSelectionCleared?: () => void;
  /** Callback when an element is added */
  onElementAdded?: (element: CanvasElement) => void;
  /** Callback when an element is removed */
  onElementRemoved?: (elementId: string) => void;
}

interface UseConstructorElementsResult {
  /** Current elements array */
  elements: CanvasElement[];
  /** Set elements directly */
  setElements: React.Dispatch<React.SetStateAction<CanvasElement[]>>;
  /** Currently selected element ID */
  selectedElementId: string;
  /** Currently selected element (or null) */
  selectedElement: CanvasElement | null;
  /** Select an element for editing */
  selectElement: (elementId: string) => void;
  /** Clear selection */
  clearSelection: () => void;
  /** Add a new element of the given type */
  addElement: (type: CanvasElementType) => void;
  /** Update the selected element with a partial patch */
  updateSelectedElement: (patch: Partial<CanvasElement>) => void;
  /** Update an element by ID with a partial patch */
  updateElement: (elementId: string, patch: Partial<CanvasElement>) => void;
  /** Remove the selected element */
  removeSelectedElement: () => void;
  /** Remove an element by ID */
  removeElement: (elementId: string) => void;
  /** Gallery card operations */
  galleryCards: {
    add: () => void;
    update: (cardId: string, patch: Partial<GalleryCard>) => void;
    remove: (cardId: string) => void;
  };
  /** Gallery info span operations */
  galleryInfoSpans: {
    add: () => void;
    update: (spanId: string, text: string) => void;
    remove: (spanId: string) => void;
  };
  /** Carousel slide operations */
  carouselSlides: {
    add: () => void;
    update: (slideId: string, patch: Partial<CarouselSlide>) => void;
    remove: (slideId: string) => void;
  };
  /** Update element position (for drag operations) */
  updateElementPosition: (
    elementId: string,
    xPercent: number,
    yPercent: number,
  ) => void;
  /** Normalize navigation element type when switching between forward/back */
  normalizeNavigationType: (
    element: CanvasElement,
    nextType: NavigationElementType,
  ) => CanvasElement;
}

/**
 * Normalize navigation element type when switching between forward/back.
 */
const normalizeNavigationElementType = (
  element: CanvasElement,
  nextType: NavigationElementType,
): CanvasElement => {
  if (!isNavigationElementType(element.type)) return element;

  const labelByType = ELEMENT_TYPE_LABELS;
  const nextButtonLabel = getNavigationButtonLabel(nextType);
  const hasDefaultLabel =
    element.label === labelByType.navigation_next ||
    element.label === labelByType.navigation_prev;
  const hasDefaultNavLabel =
    !element.navLabel ||
    element.navLabel === getNavigationButtonLabel('navigation_next') ||
    element.navLabel === getNavigationButtonLabel('navigation_prev');

  return {
    ...element,
    type: nextType,
    navType: getNavigationButtonKind(nextType),
    label: hasDefaultLabel ? labelByType[nextType] : element.label,
    navLabel: hasDefaultNavLabel ? nextButtonLabel : element.navLabel,
  };
};

/**
 * Hook for managing constructor elements with CRUD operations.
 *
 * @example
 * const {
 *   elements,
 *   selectedElement,
 *   selectElement,
 *   addElement,
 *   updateSelectedElement,
 *   removeSelectedElement,
 * } = useConstructorElements({
 *   initialElements: parsedElements,
 *   elementDefaultsByType: projectDefaults,
 *   allowedNavigationTypes: ['navigation_next', 'navigation_prev'],
 * });
 */
export function useConstructorElements({
  initialElements = [],
  elementDefaultsByType = {},
  allowedNavigationTypes = ['navigation_next', 'navigation_prev'],
  onElementsChange,
  initialSelectedElementId,
  onElementSelected,
  onSelectionCleared,
  onElementAdded,
  onElementRemoved,
}: UseConstructorElementsOptions = {}): UseConstructorElementsResult {
  const [elements, setElements] = useState<CanvasElement[]>(initialElements);
  // Initialize selectedElementId from route param if element exists in initialElements
  const [selectedElementId, setSelectedElementId] = useState(() => {
    if (
      initialSelectedElementId &&
      initialElements.some((el) => el.id === initialSelectedElementId)
    ) {
      return initialSelectedElementId;
    }
    return '';
  });

  const selectedElement = useMemo(
    () => elements.find((el) => el.id === selectedElementId) || null,
    [elements, selectedElementId],
  );

  const selectElement = useCallback(
    (elementId: string) => {
      setSelectedElementId(elementId);
      onElementSelected?.(elementId);
    },
    [onElementSelected],
  );

  const clearSelection = useCallback(() => {
    setSelectedElementId('');
    onSelectionCleared?.();
  }, [onSelectionCleared]);

  const addElement = useCallback(
    (type: CanvasElementType) => {
      const effectiveType: CanvasElementType = isNavigationElementType(type)
        ? allowedNavigationTypes.includes(type as NavigationElementType)
          ? type
          : allowedNavigationTypes[0]
        : type;

      const baseElement = createDefaultElement(effectiveType, elements.length);
      const defaults = elementDefaultsByType[effectiveType];
      const newElement = mergeElementWithDefaults(baseElement, defaults);

      setElements((prev) => {
        const next = [...prev, newElement];
        onElementsChange?.(next);
        return next;
      });
      setSelectedElementId(newElement.id);
      onElementSelected?.(newElement.id);
      onElementAdded?.(newElement);
    },
    [
      allowedNavigationTypes,
      elements.length,
      elementDefaultsByType,
      onElementsChange,
      onElementSelected,
      onElementAdded,
    ],
  );

  const updateSelectedElement = useCallback(
    (patch: Partial<CanvasElement>) => {
      if (!selectedElementId) return;

      setElements((prev) => {
        const next = prev.map((el) =>
          el.id === selectedElementId ? { ...el, ...patch } : el,
        );
        onElementsChange?.(next);
        return next;
      });
    },
    [selectedElementId, onElementsChange],
  );

  const updateElement = useCallback(
    (elementId: string, patch: Partial<CanvasElement>) => {
      setElements((prev) => {
        const next = prev.map((el) =>
          el.id === elementId ? { ...el, ...patch } : el,
        );
        onElementsChange?.(next);
        return next;
      });
    },
    [onElementsChange],
  );

  const removeSelectedElement = useCallback(() => {
    if (!selectedElementId) return;
    const removedId = selectedElementId;

    let nextSelectedId = '';
    setElements((prev) => {
      const filtered = prev.filter((el) => el.id !== selectedElementId);
      nextSelectedId = filtered[0]?.id || '';
      onElementsChange?.(filtered);
      return filtered;
    });

    if (nextSelectedId) {
      setSelectedElementId(nextSelectedId);
      onElementSelected?.(nextSelectedId);
    } else {
      setSelectedElementId('');
      onSelectionCleared?.();
    }
    onElementRemoved?.(removedId);
  }, [
    selectedElementId,
    onElementsChange,
    onElementSelected,
    onSelectionCleared,
    onElementRemoved,
  ]);

  const removeElement = useCallback(
    (elementId: string) => {
      setElements((prev) => {
        const next = prev.filter((el) => el.id !== elementId);
        onElementsChange?.(next);
        return next;
      });

      if (selectedElementId === elementId) {
        setSelectedElementId('');
      }
    },
    [selectedElementId, onElementsChange],
  );

  const updateElementPosition = useCallback(
    (elementId: string, xPercent: number, yPercent: number) => {
      setElements((prev) =>
        prev.map((el) =>
          el.id === elementId ? { ...el, xPercent, yPercent } : el,
        ),
      );
    },
    [],
  );

  // Gallery card operations
  const galleryCards = useMemo(
    () => ({
      add: () => {
        if (!selectedElement || !isGalleryElementType(selectedElement.type))
          return;
        const nextCards = [
          ...(selectedElement.galleryCards || []),
          {
            id: createLocalId(),
            imageUrl: '',
            title: `Card ${(selectedElement.galleryCards || []).length + 1}`,
            description: '',
          },
        ];
        updateSelectedElement({ galleryCards: nextCards });
      },
      update: (cardId: string, patch: Partial<GalleryCard>) => {
        if (!selectedElement || !isGalleryElementType(selectedElement.type))
          return;
        const nextCards = (selectedElement.galleryCards || []).map((card) =>
          card.id === cardId ? { ...card, ...patch } : card,
        );
        updateSelectedElement({ galleryCards: nextCards });
      },
      remove: (cardId: string) => {
        if (!selectedElement || !isGalleryElementType(selectedElement.type))
          return;
        const nextCards = (selectedElement.galleryCards || []).filter(
          (card) => card.id !== cardId,
        );
        updateSelectedElement({ galleryCards: nextCards });
      },
    }),
    [selectedElement, updateSelectedElement],
  );

  // Gallery info span operations
  const galleryInfoSpans = useMemo(
    () => ({
      add: () => {
        if (!selectedElement || !isGalleryElementType(selectedElement.type))
          return;
        const nextSpans: GalleryInfoSpan[] = [
          ...(selectedElement.galleryInfoSpans || []),
          {
            id: createLocalId(),
            text: '',
          },
        ];
        updateSelectedElement({ galleryInfoSpans: nextSpans });
      },
      update: (spanId: string, text: string) => {
        if (!selectedElement || !isGalleryElementType(selectedElement.type))
          return;
        const nextSpans = (selectedElement.galleryInfoSpans || []).map((span) =>
          span.id === spanId ? { ...span, text } : span,
        );
        updateSelectedElement({ galleryInfoSpans: nextSpans });
      },
      remove: (spanId: string) => {
        if (!selectedElement || !isGalleryElementType(selectedElement.type))
          return;
        const nextSpans = (selectedElement.galleryInfoSpans || []).filter(
          (span) => span.id !== spanId,
        );
        updateSelectedElement({ galleryInfoSpans: nextSpans });
      },
    }),
    [selectedElement, updateSelectedElement],
  );

  // Carousel slide operations
  const carouselSlides = useMemo(
    () => ({
      add: () => {
        if (!selectedElement || !isCarouselElementType(selectedElement.type))
          return;
        const nextSlides = [
          ...(selectedElement.carouselSlides || []),
          {
            id: createLocalId(),
            imageUrl: '',
            caption: `Slide ${(selectedElement.carouselSlides || []).length + 1}`,
          },
        ];
        updateSelectedElement({ carouselSlides: nextSlides });
      },
      update: (slideId: string, patch: Partial<CarouselSlide>) => {
        if (!selectedElement || !isCarouselElementType(selectedElement.type))
          return;
        const nextSlides = (selectedElement.carouselSlides || []).map(
          (slide) => (slide.id === slideId ? { ...slide, ...patch } : slide),
        );
        updateSelectedElement({ carouselSlides: nextSlides });
      },
      remove: (slideId: string) => {
        if (!selectedElement || !isCarouselElementType(selectedElement.type))
          return;
        const nextSlides = (selectedElement.carouselSlides || []).filter(
          (slide) => slide.id !== slideId,
        );
        updateSelectedElement({ carouselSlides: nextSlides });
      },
    }),
    [selectedElement, updateSelectedElement],
  );

  return {
    elements,
    setElements,
    selectedElementId,
    selectedElement,
    selectElement,
    clearSelection,
    addElement,
    updateSelectedElement,
    updateElement,
    removeSelectedElement,
    removeElement,
    galleryCards,
    galleryInfoSpans,
    carouselSlides,
    updateElementPosition,
    normalizeNavigationType: normalizeNavigationElementType,
  };
}

export { normalizeNavigationElementType };
export default useConstructorElements;
