import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import type {
  CanvasElement,
  CanvasState,
  Position,
  PriceScenario,
  ElementType,
  PriceElement,
  CurrencyElement,
  DiscountBadgeElement,
  TextElement,
  VariablePriceRangeElement,
  TypographySettings,
  ContainerSettings,
  ResponsiveTypographySettings,
  ResponsiveSpacing,
} from "@/types";
import { createResponsiveValue, createDefaultResponsiveSpacing, isResponsiveValue } from "@/types";

// Generate unique ID
const generateId = (): string => {
  return `el_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
};

/**
 * Helper to extract desktop (flat) typography from responsive typography.
 * Used for size calculations that need non-responsive values.
 */
const getDesktopTypography = (typography: ResponsiveTypographySettings): TypographySettings => ({
  fontFamily: typography.fontFamily,
  fontSize: isResponsiveValue(typography.fontSize) ? typography.fontSize.desktop : typography.fontSize,
  fontWeight: isResponsiveValue(typography.fontWeight) ? typography.fontWeight.desktop : typography.fontWeight,
  fontStyle: typography.fontStyle,
  lineHeight: typography.lineHeight,
  letterSpacing: isResponsiveValue(typography.letterSpacing) ? typography.letterSpacing.desktop : typography.letterSpacing,
  textTransform: typography.textTransform,
  textDecoration: typography.textDecoration,
});

/**
 * Helper to extract desktop (flat) padding from responsive padding.
 */
const getDesktopPadding = (padding: ResponsiveSpacing): { top: number; right: number; bottom: number; left: number } => ({
  top: isResponsiveValue(padding.top) ? padding.top.desktop : padding.top,
  right: isResponsiveValue(padding.right) ? padding.right.desktop : padding.right,
  bottom: isResponsiveValue(padding.bottom) ? padding.bottom.desktop : padding.bottom,
  left: isResponsiveValue(padding.left) ? padding.left.desktop : padding.left,
});

/**
 * Calculate the required size for an element based on its text content and typography.
 * Uses canvas measureText for accurate text measurements.
 * Sizes elements tightly to fit text without extra empty space.
 * Supports multiline text (split by newlines).
 */
const calculateTextSize = (
  text: string,
  typography: TypographySettings,
  padding: { top: number; right: number; bottom: number; left: number },
  isPriceWithDecimal: boolean = false,
  decimalFontSize?: number
): { width: number; height: number } => {
  // Create an offscreen canvas for measuring
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");

  if (!ctx) {
    // Fallback estimation
    const lines = text.split("\n");
    const maxLineLength = Math.max(...lines.map((l) => l.length));
    return {
      width: maxLineLength * typography.fontSize * 0.6 + padding.left + padding.right,
      height: typography.fontSize * typography.lineHeight * lines.length + padding.top + padding.bottom,
    };
  }

  // Build font string
  const fontWeight = typography.fontWeight >= 700 ? "bold" : typography.fontWeight;
  const fontStyle = typography.fontStyle === "italic" ? "italic" : "";
  ctx.font = `${fontStyle} ${fontWeight} ${typography.fontSize}px ${typography.fontFamily}`;

  let textWidth: number;
  let textHeight: number;

  // Handle multiline text
  const lines = text.split("\n");
  const isMultiline = lines.length > 1;

  if (isPriceWithDecimal && !isMultiline) {
    // For price elements (single line), calculate width matching the rendering layout:
    // Currency "$" at x=0 (but within element, so starts at padding.left)
    // Main price at x=fontSize*0.6
    // Decimal at x=fontSize*0.6 + fontSize*mainLength*0.6
    const charWidth = typography.fontSize * 0.6;

    // Parse text like "$99.99" into parts
    const numericPart = text.replace(/^[^0-9]*/, "");
    const [mainPart, decimalPart] = numericPart.split(".");

    // Main price position + width of main price
    const mainEndX = charWidth + (mainPart?.length || 0) * charWidth;

    // Decimal starts at mainEndX and has width based on decimal font size
    const decimalCharWidth = decimalFontSize ? decimalFontSize * 0.6 : charWidth * 0.6;
    const decimalWidth = decimalPart ? (decimalPart.length + 1) * decimalCharWidth : 0; // +1 for separator

    // Total width is the end of the decimal text
    textWidth = mainEndX + decimalWidth;
    textHeight = decimalFontSize ? Math.max(typography.fontSize, decimalFontSize) : typography.fontSize;
  } else if (isMultiline) {
    // For multiline text, measure each line and use the max width
    let maxWidth = 0;
    for (const line of lines) {
      const metrics = ctx.measureText(line);
      const letterSpacingWidth = (line.length - 1) * typography.letterSpacing;
      const lineWidth = metrics.width + letterSpacingWidth;
      if (lineWidth > maxWidth) {
        maxWidth = lineWidth;
      }
    }
    textWidth = maxWidth;
    // Height = number of lines * fontSize * lineHeight
    textHeight = lines.length * typography.fontSize * typography.lineHeight;
  } else {
    // Standard single-line text measurement
    const metrics = ctx.measureText(text);
    const letterSpacingWidth = (text.length - 1) * typography.letterSpacing;
    textWidth = metrics.width + letterSpacingWidth;
    textHeight = typography.fontSize;
  }

  // For short text (1-3 chars like currency symbols), ensure minimum width
  // This prevents extremely narrow elements at certain font sizes
  if (text.length <= 3) {
    const minWidth = text.length * typography.fontSize * 0.5;
    textWidth = Math.max(textWidth, minWidth);
  }

  // Add a small buffer for short text to prevent clipping
  const widthBuffer = text.length <= 3 ? Math.max(2, typography.fontSize * 0.1) : 0;

  return {
    // Tight fit - only add padding, no extra space
    width: Math.round(textWidth + padding.left + padding.right + widthBuffer),
    height: Math.round(textHeight + padding.top + padding.bottom),
  };
};

/**
 * Get display text for an element type (for size calculation)
 * @param type - The element type
 * @param discountFormat - Optional format for discount badge ('percentage' | 'amount' | 'custom')
 * @param customFormat - Optional custom format string for discount badge
 * @param showDecimal - Optional flag for price elements (default true)
 */
const getElementDisplayText = (
  type: ElementType,
  discountFormat?: "percentage" | "amount" | "custom",
  customFormat?: string,
  showDecimal: boolean = true,
  textContent?: string
): string => {
  switch (type) {
    case "regularPrice":
      return showDecimal ? "$99.99" : "$99";
    case "salePrice":
      return showDecimal ? "$79.99" : "$79";
    case "currencySymbol":
      return "$";
    case "discountBadge":
      // Use appropriate text based on format for proper sizing
      if (discountFormat === "amount") {
        return "-$19.99";
      } else if (discountFormat === "custom" && customFormat) {
        // Replace placeholders with sample values to calculate proper width
        return customFormat
          .replace("{discount}", "20")
          .replace("{discountPercentage}", "20")
          .replace("{discountAmount}", "$19.99")
          .replace("{discountAmountRaw}", "19.99");
      }
      return "-20%";
    case "text":
      return textContent || "Your text here";
    default:
      return "Element";
  }
};

// Default typography (non-responsive for size calculations)
const defaultTypography: TypographySettings = {
  fontFamily: "Inter",
  fontSize: 24,
  fontWeight: 600,
  fontStyle: "normal" as const,
  lineHeight: 1.2,
  letterSpacing: 0,
  textTransform: "none" as const,
  textDecoration: "none" as const,
};

// Default responsive typography (for element storage)
const defaultResponsiveTypography = {
  fontFamily: "Inter",
  fontSize: createResponsiveValue(24),
  fontWeight: createResponsiveValue(600),
  fontStyle: "normal" as const,
  lineHeight: 1.2,
  letterSpacing: createResponsiveValue(0),
  textTransform: "none" as const,
  textDecoration: "none" as const,
};

// Default responsive border (for element storage)
const defaultResponsiveBorder = {
  width: createResponsiveValue(0),
  style: "solid" as const,
  color: "#e2e8f0",
  radius: 0,
};

// Default responsive padding
const defaultResponsivePadding = createDefaultResponsiveSpacing(0);

// Default responsive margin
const defaultResponsiveMargin = createDefaultResponsiveSpacing(0);

// Default animation
const defaultAnimation = {
  type: "none" as const,
  duration: 300,
  delay: 0,
  easing: "ease-out",
};

// Default element factory
const createDefaultElement = (type: ElementType, position: Position): CanvasElement => {
  const baseElement = {
    id: generateId(),
    type,
    name: type,
    position,
    size: { width: 100, height: 40 },
    rotation: 0,
    zIndex: 0,
    visible: true,
    locked: false,
    color: "#0f172a",
    backgroundColor: "transparent",
    opacity: 1,
    typography: { ...defaultResponsiveTypography },
    border: { ...defaultResponsiveBorder },
    padding: { ...defaultResponsivePadding },
    margin: { ...defaultResponsiveMargin },
    positionType: "relative" as const,
    animation: { ...defaultAnimation },
  };

  switch (type) {
    case "regularPrice":
    case "salePrice": {
      // Calculate tight fit for price element
      const priceText = type === "regularPrice" ? "$99.99" : "$79.99";
      const priceTypo = { ...defaultTypography };
      const defaultDecimalFontSize = 14;
      const priceSize = calculateTextSize(
        priceText,
        priceTypo,
        { top: 0, right: 0, bottom: 0, left: 0 },
        true, // isPriceWithDecimal
        defaultDecimalFontSize
      );
      return {
        ...baseElement,
        type,
        name: type === "regularPrice" ? "Regular Price" : "Sale Price",
        size: priceSize,
        decimal: {
          show: true,
          showCurrency: true,
          separator: "point",
          typography: { fontSize: createResponsiveValue(defaultDecimalFontSize) },
          position: "superscript",
        },
        lineThrough: {
          enabled: false,
          style: "single",
          direction: "horizontal",
          color: "#ef4444",
          thickness: 2,
        },
      } as PriceElement;
    }

    case "currencySymbol": {
      // Calculate tight fit for currency symbol
      // Get WooCommerce currency as default
      const wcCurrency = window.oppbAdmin?.currency;
      const defaultSymbol = wcCurrency?.symbol ?? "$";
      const defaultCode = wcCurrency?.code ?? "USD";

      // Non-responsive typography for size calculation
      const currencyTypoForSize = { ...defaultTypography, fontSize: 18 };
      const currencySize = calculateTextSize(defaultSymbol, currencyTypoForSize, {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
      });
      // Responsive typography for element
      const currencyTypo = {
        ...defaultResponsiveTypography,
        fontSize: createResponsiveValue(18),
      };
      return {
        ...baseElement,
        type,
        name: "Currency Symbol",
        size: currencySize,
        typography: currencyTypo,
        symbol: defaultSymbol,
        symbolCode: defaultCode,
        useCustomImage: false,
        customImageUrl: null,
        customImageId: null,
        useCustomSvg: false,
        customSvg: null,
        symbolPosition: "before",
      } as CurrencyElement;
    }

    case "discountBadge": {
      // Calculate tight fit for discount badge
      // Non-responsive typography for size calculation
      const badgeTypoForSize = {
        ...defaultTypography,
        fontSize: 14,
        fontWeight: 700,
      };
      const badgePadding = { top: 4, right: 8, bottom: 4, left: 8 };
      const badgeSize = calculateTextSize("-20%", badgeTypoForSize, badgePadding);
      // Responsive typography for element
      const badgeTypo = {
        ...defaultResponsiveTypography,
        fontSize: createResponsiveValue(14),
        fontWeight: createResponsiveValue(700),
      };
      // Responsive padding
      const responsiveBadgePadding = {
        top: createResponsiveValue(4),
        right: createResponsiveValue(8),
        bottom: createResponsiveValue(4),
        left: createResponsiveValue(8),
      };
      return {
        ...baseElement,
        type,
        name: "Discount Badge",
        size: badgeSize,
        color: "#ffffff",
        backgroundColor: "#ef4444",
        typography: badgeTypo,
        border: { ...defaultResponsiveBorder, radius: 4 },
        padding: responsiveBadgePadding,
        format: "percentage",
        customFormat: "-{discount}%",
      } as DiscountBadgeElement;
    }

    case "text": {
      // Calculate tight fit for text element
      // Non-responsive typography for size calculation
      const textTypoForSize = {
        ...defaultTypography,
        fontSize: 16,
        fontWeight: 400,
      };
      const textPaddingForSize = { top: 0, right: 0, bottom: 0, left: 0 };
      const defaultText = "Your text here";
      const textSize = calculateTextSize(defaultText, textTypoForSize, textPaddingForSize);
      // Responsive typography for element
      const textTypo = {
        ...defaultResponsiveTypography,
        fontSize: createResponsiveValue(16),
        fontWeight: createResponsiveValue(400),
      };
      return {
        ...baseElement,
        type,
        name: "Text",
        size: textSize,
        color: "#374151",
        typography: textTypo,
        text: defaultText,
        textAlign: "left",
        verticalAlign: "top",
        widthMode: "auto",
        customWidth: 200,
        backgroundImage: null,
        backgroundImageId: null,
        backgroundSize: "cover",
        backgroundPosition: "center",
        backgroundRepeat: "no-repeat",
      } as TextElement;
    }

    default:
      return baseElement as CanvasElement;
  }
};

// Settings mode type
export type SettingsMode = "sidebar" | "floating";

// Default container settings
const defaultContainerSettings: ContainerSettings = {
  border: defaultResponsiveBorder,
  padding: defaultResponsivePadding,
  margin: defaultResponsiveMargin,
  backgroundColor: "transparent",
  backgroundImage: null,
  backgroundImageId: null,
  backgroundSize: "cover",
  backgroundPosition: "center",
  backgroundRepeat: "no-repeat",
};

// Store interface
interface CanvasStore extends CanvasState {
  // Scenario
  activeScenario: PriceScenario;
  setActiveScenario: (scenario: PriceScenario) => void;

  // Settings Mode
  settingsMode: SettingsMode;
  setSettingsMode: (mode: SettingsMode) => void;
  toggleSettingsMode: () => void;

  // Layout loading
  loadCanvasState: (canvasData: Partial<CanvasState>) => void;
  clearCanvas: () => void;

  // Elements
  addElement: (type: ElementType, position?: Position) => void;
  addElementDirect: (element: CanvasElement) => void;
  removeElement: (id: string) => void;
  updateElement: (id: string, updates: Partial<CanvasElement>) => void;
  duplicateElement: (id: string) => void;
  reorderElements: (fromIndex: number, toIndex: number) => void;

  // Selection
  selectElement: (id: string, addToSelection?: boolean) => void;
  deselectElement: (id: string) => void;
  selectAll: () => void;
  clearSelection: () => void;
  setHoveredId: (id: string | null) => void;

  // Container selection
  selectContainer: () => void;

  // Container settings
  updateContainerSettings: (updates: Partial<ContainerSettings>) => void;

  // Position & Transform
  moveElement: (id: string, position: Position) => void;
  resizeElement: (id: string, size: { width: number; height: number }) => void;
  rotateElement: (id: string, rotation: number) => void;

  // Canvas
  setZoom: (zoom: number) => void;
  setPan: (pan: Position) => void;
  resetView: () => void;
  toggleGrid: () => void;
  toggleGuides: () => void;
  toggleContainerBounds: () => void;
  toggleSnap: () => void;
  setActiveGuides: (guides: import("@/types").SnapGuide[]) => void;

  // Undo/Redo (placeholder)
  history: CanvasState[];
  historyIndex: number;
  undo: () => void;
  redo: () => void;
  saveToHistory: () => void;
}

// Initial state
const initialState: CanvasState = {
  elements: [],
  selectedIds: [],
  hoveredId: null,
  containerSelected: false,
  containerSettings: { ...defaultContainerSettings },
  zoom: 1,
  pan: { x: 0, y: 0 },
  showGrid: true,
  showGuides: true,
  showContainerBounds: true,
  snapToGuides: true,
  snapThreshold: 5,
  activeGuides: [],
  presetId: null,
};

// Create store with immer for immutable updates
export const useCanvasStore = create<CanvasStore>()(
  immer((set, _get) => ({
    ...initialState,
    activeScenario: "simple",
    settingsMode: "floating",
    history: [],
    historyIndex: -1,

    // Scenario
    setActiveScenario: (scenario) =>
      set((state) => {
        state.activeScenario = scenario;
        // Clear selection when switching scenarios
        state.selectedIds = [];
        state.hoveredId = null;
      }),

    // Settings Mode
    setSettingsMode: (mode) =>
      set((state) => {
        state.settingsMode = mode;
      }),
    toggleSettingsMode: () =>
      set((state) => {
        state.settingsMode = state.settingsMode === "sidebar" ? "floating" : "sidebar";
      }),

    // Layout loading
    loadCanvasState: (canvasData) =>
      set((state) => {
        // Load elements if provided, with normalization for backwards compatibility
        if (canvasData.elements) {
          // Normalize elements to ensure all price elements have lineThrough property
          state.elements = canvasData.elements.map((el) => {
            if ((el.type === "regularPrice" || el.type === "salePrice") && !("lineThrough" in el)) {
              return {
                ...el,
                lineThrough: {
                  enabled: false,
                  style: "single" as const,
                  direction: "horizontal" as const,
                  color: "#ef4444",
                  thickness: 2,
                },
              };
            }
            return el;
          });
        } else {
          state.elements = [];
        }
        // Load other canvas settings
        if (canvasData.zoom !== undefined) state.zoom = canvasData.zoom;
        if (canvasData.pan !== undefined) state.pan = canvasData.pan;
        if (canvasData.showGrid !== undefined) state.showGrid = canvasData.showGrid;
        if (canvasData.showGuides !== undefined) state.showGuides = canvasData.showGuides;
        if (canvasData.snapToGuides !== undefined) state.snapToGuides = canvasData.snapToGuides;
        if (canvasData.snapThreshold !== undefined) state.snapThreshold = canvasData.snapThreshold;
        // Always reset container settings to defaults first, then merge with provided settings
        // This ensures each scenario has its own independent container settings
        state.containerSettings = {
          ...defaultContainerSettings,
          ...(canvasData.containerSettings || {}),
        };
        // Clear selection
        state.selectedIds = [];
        state.hoveredId = null;
        state.containerSelected = false;
        state.presetId = canvasData.presetId ?? null;
      }),

    clearCanvas: () =>
      set((state) => {
        state.elements = [];
        state.selectedIds = [];
        state.hoveredId = null;
        state.containerSelected = false;
        state.containerSettings = { ...defaultContainerSettings };
        state.presetId = null;
        state.zoom = 1;
        state.pan = { x: 0, y: 0 };
      }),

    // Elements
    addElement: (type, position = { x: 100, y: 100 }) =>
      set((state) => {
        // Limit price elements: only one regularPrice and one salePrice allowed
        if (type === "regularPrice" || type === "salePrice") {
          const existingPriceElement = state.elements.find((el) => el.type === type);
          if (existingPriceElement) {
            // Don't add - price element of this type already exists
            console.warn(`Cannot add another ${type} element. Only one ${type} element is allowed per layout.`);
            return;
          }
        }

        const element = createDefaultElement(type, position);
        element.zIndex = state.elements.length;
        state.elements.push(element);
        state.selectedIds = [element.id];
      }),

    addElementDirect: (element) =>
      set((state) => {
        // Limit price elements: only one regularPrice and one salePrice allowed
        if (element.type === "regularPrice" || element.type === "salePrice") {
          const existingPriceElement = state.elements.find((el) => el.type === element.type);
          if (existingPriceElement) {
            // Don't add - price element of this type already exists
            console.warn(`Cannot add another ${element.type} element. Only one ${element.type} element is allowed per layout.`);
            return;
          }
        }

        const elementWithZIndex = {
          ...element,
          zIndex: state.elements.length,
        };
        state.elements.push(elementWithZIndex);
        state.selectedIds = [element.id];
      }),

    removeElement: (id) =>
      set((state) => {
        state.elements = state.elements.filter((el) => el.id !== id);
        state.selectedIds = state.selectedIds.filter((sid) => sid !== id);
        if (state.hoveredId === id) state.hoveredId = null;
      }),

    updateElement: (id, updates) =>
      set((state) => {
        const index = state.elements.findIndex((el) => el.id === id);
        if (index !== -1) {
          const current = state.elements[index];

          // Deep merge for nested objects
          const merged: CanvasElement = {
            ...current,
            ...updates,
          } as CanvasElement;

          // Merge typography if provided
          if (updates.typography) {
            merged.typography = {
              ...current.typography,
              ...updates.typography,
            };
          }

          // Merge border if provided
          if (updates.border) {
            merged.border = {
              ...current.border,
              ...updates.border,
            };
          }

          // Merge padding if provided
          if (updates.padding) {
            merged.padding = {
              ...current.padding,
              ...updates.padding,
            };
          }

          // Merge animation if provided
          if (updates.animation) {
            merged.animation = {
              ...current.animation,
              ...updates.animation,
            };
          }

          // Merge decimal for price elements (with deep merge for typography)
          if ("decimal" in updates && updates.decimal && "decimal" in current) {
            const currentDecimal = (current as PriceElement).decimal;
            const updatesDecimal = updates.decimal;
            (merged as PriceElement).decimal = {
              ...currentDecimal,
              ...updatesDecimal,
              // Deep merge typography if both exist
              typography: {
                ...currentDecimal?.typography,
                ...updatesDecimal?.typography,
              },
              // Deep merge absoluteOffset if provided
              ...(updatesDecimal?.absoluteOffset && {
                absoluteOffset: {
                  x: updatesDecimal.absoluteOffset.x ?? currentDecimal?.absoluteOffset?.x ?? 0,
                  y: updatesDecimal.absoluteOffset.y ?? currentDecimal?.absoluteOffset?.y ?? 0,
                },
              }),
            };
          }

          // Merge lineThrough for price elements
          if ("lineThrough" in updates && updates.lineThrough && "lineThrough" in current) {
            (merged as PriceElement).lineThrough = {
              ...(current as PriceElement).lineThrough,
              ...updates.lineThrough,
            };
          }

          // Auto-resize discount badge when format or customFormat changes
          if (("format" in updates || "customFormat" in updates) && merged.type === "discountBadge") {
            const badgeElement = merged as DiscountBadgeElement;
            const displayText = getElementDisplayText(merged.type, badgeElement.format, badgeElement.customFormat);
            const newSize = calculateTextSize(displayText, getDesktopTypography(merged.typography), getDesktopPadding(merged.padding));
            merged.size = {
              width: newSize.width,
              height: newSize.height,
            };
          }

          // Auto-resize text element when text content changes (only in auto width mode)
          if ("text" in updates && merged.type === "text") {
            const textElement = merged as TextElement;
            // Only auto-resize if width mode is auto
            if (textElement.widthMode === "auto") {
              const displayText = getElementDisplayText(merged.type, undefined, undefined, true, textElement.text);
              const newSize = calculateTextSize(displayText, getDesktopTypography(merged.typography), getDesktopPadding(merged.padding));
              merged.size = {
                width: newSize.width,
                height: newSize.height,
              };
            }
          }

          // Auto-resize text element when width mode changes
          if ("widthMode" in updates && merged.type === "text") {
            const textElement = merged as TextElement;
            if (textElement.widthMode === "auto") {
              const displayText = getElementDisplayText(merged.type, undefined, undefined, true, textElement.text);
              const newSize = calculateTextSize(displayText, getDesktopTypography(merged.typography), getDesktopPadding(merged.padding));
              merged.size = {
                width: newSize.width,
                height: newSize.height,
              };
            } else if (textElement.widthMode === "custom") {
              merged.size = {
                ...merged.size,
                width: textElement.customWidth,
              };
            }
            // For "full" width mode, we let the CSS/rendering handle it
          }

          // Auto-resize element when typography changes to fit content
          if (updates.typography) {
            const discountFormat = merged.type === "discountBadge" ? (merged as DiscountBadgeElement).format : undefined;
            const customFormat = merged.type === "discountBadge" ? (merged as DiscountBadgeElement).customFormat : undefined;
            const isPriceEl = merged.type === "regularPrice" || merged.type === "salePrice";
            const isTextEl = merged.type === "text";
            // For price elements, check if decimals are shown
            const showDecimal = isPriceEl && "decimal" in merged ? (merged as PriceElement).decimal?.show ?? true : true;
            // For text elements, get the text content
            const textContent = isTextEl && "text" in merged ? (merged as TextElement).text : undefined;
            const displayText = getElementDisplayText(merged.type, discountFormat, customFormat, showDecimal, textContent);
            // Extract decimal font size as plain number (from responsive value if needed)
            const rawDecimalFontSizeTypo =
              isPriceEl && "decimal" in merged && showDecimal ? (merged as PriceElement).decimal?.typography?.fontSize : undefined;
            const decimalFontSizeTypo =
              rawDecimalFontSizeTypo !== undefined
                ? isResponsiveValue(rawDecimalFontSizeTypo)
                  ? rawDecimalFontSizeTypo.desktop
                  : rawDecimalFontSizeTypo
                : undefined;

            // Skip auto-resize for text elements with fixed/full width
            const shouldAutoResize = !isTextEl || (isTextEl && (merged as TextElement).widthMode === "auto");

            if (shouldAutoResize) {
              const newSize = calculateTextSize(
                displayText,
                getDesktopTypography(merged.typography),
                getDesktopPadding(merged.padding),
                isPriceEl && showDecimal,
                decimalFontSizeTypo
              );

              // Always set size to calculated size (no decimals)
              merged.size = {
                width: newSize.width,
                height: newSize.height,
              };
            }
          }

          // Auto-resize price elements when decimal settings change (show or fontSize)
          if (
            "decimal" in updates &&
            updates.decimal &&
            "decimal" in merged &&
            (merged.type === "regularPrice" || merged.type === "salePrice")
          ) {
            // Check if show or fontSize changed
            const showChanged = "show" in updates.decimal;
            const fontSizeChanged = updates.decimal?.typography?.fontSize !== undefined;

            if (showChanged || fontSizeChanged) {
              // Recalculate size based on decimal settings
              const priceElement = merged as PriceElement;
              const showDecimal = priceElement.decimal?.show ?? true;
              const displayText = getElementDisplayText(merged.type, undefined, undefined, showDecimal);
              // Extract decimal font size as a plain number
              const rawDecimalFontSize = showDecimal ? priceElement.decimal?.typography?.fontSize : undefined;
              const decimalFontSize =
                rawDecimalFontSize !== undefined
                  ? isResponsiveValue(rawDecimalFontSize)
                    ? rawDecimalFontSize.desktop
                    : rawDecimalFontSize
                  : undefined;
              const baseSize = calculateTextSize(
                displayText,
                getDesktopTypography(merged.typography),
                getDesktopPadding(merged.padding),
                showDecimal, // isPriceWithDecimal only if showing decimals
                decimalFontSize
              );

              merged.size = {
                width: baseSize.width,
                height: baseSize.height,
              };
            }
          }

          // Auto-resize element when padding changes
          if (updates.padding) {
            const discountFormat = merged.type === "discountBadge" ? (merged as DiscountBadgeElement).format : undefined;
            const customFormat = merged.type === "discountBadge" ? (merged as DiscountBadgeElement).customFormat : undefined;
            const isPriceEl = merged.type === "regularPrice" || merged.type === "salePrice";
            const isTextEl = merged.type === "text";
            // For price elements, check if decimals are shown
            const showDecimal = isPriceEl && "decimal" in merged ? (merged as PriceElement).decimal?.show ?? true : true;
            // For text elements, get the text content
            const textContent = isTextEl && "text" in merged ? (merged as TextElement).text : undefined;
            const displayText = getElementDisplayText(merged.type, discountFormat, customFormat, showDecimal, textContent);
            // Extract decimal font size as plain number (from responsive value if needed)
            const rawDecimalFontSizeForPadding =
              isPriceEl && "decimal" in merged && showDecimal ? (merged as PriceElement).decimal?.typography?.fontSize : undefined;
            const decimalFontSizeForPadding =
              rawDecimalFontSizeForPadding !== undefined
                ? isResponsiveValue(rawDecimalFontSizeForPadding)
                  ? rawDecimalFontSizeForPadding.desktop
                  : rawDecimalFontSizeForPadding
                : undefined;

            // Skip auto-resize for text elements with fixed/full width
            const shouldAutoResize = !isTextEl || (isTextEl && (merged as TextElement).widthMode === "auto");

            if (shouldAutoResize) {
              const newSize = calculateTextSize(
                displayText,
                getDesktopTypography(merged.typography),
                getDesktopPadding(merged.padding),
                isPriceEl && showDecimal,
                decimalFontSizeForPadding
              );

              merged.size = {
                width: newSize.width,
                height: newSize.height,
              };
            }
          }

          state.elements[index] = merged;
        }
      }),

    duplicateElement: (id) =>
      set((state) => {
        const element = state.elements.find((el) => el.id === id);
        if (element) {
          const duplicate = {
            ...JSON.parse(JSON.stringify(element)),
            id: generateId(),
            name: `${element.name} (copy)`,
            position: {
              x: element.position.x + 20,
              y: element.position.y + 20,
            },
            zIndex: state.elements.length,
          };
          state.elements.push(duplicate);
          state.selectedIds = [duplicate.id];
        }
      }),

    reorderElements: (fromIndex, toIndex) =>
      set((state) => {
        const [element] = state.elements.splice(fromIndex, 1);
        state.elements.splice(toIndex, 0, element);
        // Update z-indices
        state.elements.forEach((el: CanvasElement, i: number) => {
          el.zIndex = i;
        });
      }),

    // Selection
    selectElement: (id, addToSelection = false) =>
      set((state) => {
        state.containerSelected = false;
        if (addToSelection) {
          if (!state.selectedIds.includes(id)) {
            state.selectedIds.push(id);
          }
        } else {
          state.selectedIds = [id];
        }
      }),

    deselectElement: (id) =>
      set((state) => {
        state.selectedIds = state.selectedIds.filter((sid) => sid !== id);
      }),

    selectAll: () =>
      set((state) => {
        state.selectedIds = state.elements.map((el) => el.id);
        state.containerSelected = false;
      }),

    clearSelection: () =>
      set((state) => {
        state.selectedIds = [];
        state.containerSelected = false;
      }),

    setHoveredId: (id) =>
      set((state) => {
        state.hoveredId = id;
      }),

    // Container selection
    selectContainer: () =>
      set((state) => {
        state.selectedIds = [];
        state.containerSelected = true;
      }),

    // Container settings
    updateContainerSettings: (updates) =>
      set((state) => {
        // Helper to deep merge ResponsiveSpacing objects
        const mergeSpacing = (current: ResponsiveSpacing, incoming: Partial<ResponsiveSpacing> | undefined): ResponsiveSpacing => {
          if (!incoming) return current;
          return {
            top: incoming.top ? { ...(current.top || createResponsiveValue(0)), ...incoming.top } : current.top || createResponsiveValue(0),
            right: incoming.right
              ? {
                  ...(current.right || createResponsiveValue(0)),
                  ...incoming.right,
                }
              : current.right || createResponsiveValue(0),
            bottom: incoming.bottom
              ? {
                  ...(current.bottom || createResponsiveValue(0)),
                  ...incoming.bottom,
                }
              : current.bottom || createResponsiveValue(0),
            left: incoming.left
              ? {
                  ...(current.left || createResponsiveValue(0)),
                  ...incoming.left,
                }
              : current.left || createResponsiveValue(0),
          };
        };

        state.containerSettings = {
          ...state.containerSettings,
          ...updates,
          border: {
            ...state.containerSettings.border,
            ...(updates.border || {}),
          },
          padding: mergeSpacing(state.containerSettings.padding, updates.padding),
          margin: mergeSpacing(state.containerSettings.margin, updates.margin),
        };
      }),

    // Position & Transform
    moveElement: (id, position) =>
      set((state) => {
        const element = state.elements.find((el) => el.id === id);
        if (element) {
          element.position = position;
        }
      }),

    resizeElement: (id, size) =>
      set((state) => {
        const element = state.elements.find((el) => el.id === id);
        if (element) {
          // Calculate scale factor based on the dominant dimension change
          const oldWidth = element.size.width;
          const oldHeight = element.size.height;
          const scaleX = size.width / oldWidth;
          const scaleY = size.height / oldHeight;

          // Use the average scale to maintain proportions
          // This ensures text scales appropriately with both width and height changes
          const scaleFactor = (scaleX + scaleY) / 2;

          // Only scale font if there's a significant change (> 5%)
          if (Math.abs(scaleFactor - 1) > 0.05) {
            // Helper to scale a responsive value
            const scaleResponsiveValue = (value: { desktop: number; tablet: number; mobile: number }, min: number, max: number) => ({
              desktop: Math.max(min, Math.min(max, Math.round(value.desktop * scaleFactor))),
              tablet: Math.max(min, Math.min(max, Math.round(value.tablet * scaleFactor))),
              mobile: Math.max(min, Math.min(max, Math.round(value.mobile * scaleFactor))),
            });

            // Scale main typography fontSize (responsive)
            element.typography.fontSize = scaleResponsiveValue(element.typography.fontSize, 8, 200);

            // Scale decimal typography if present (for price elements)
            if ("decimal" in element && element.decimal?.typography) {
              const currentDecimalFontSize = element.decimal.typography.fontSize;
              // Handle both responsive and plain number values
              if (currentDecimalFontSize !== undefined && isResponsiveValue(currentDecimalFontSize)) {
                // Scale responsive value
                element.decimal.typography.fontSize = scaleResponsiveValue(currentDecimalFontSize, 6, 120);
              } else {
                // Scale plain number - wrap result in responsive value
                const plainSize = typeof currentDecimalFontSize === "number" ? currentDecimalFontSize : 14;
                const newDecimalSize = Math.round(plainSize * scaleFactor);
                element.decimal.typography.fontSize = createResponsiveValue(Math.max(6, Math.min(120, newDecimalSize)));
              }
            }

            // Scale variable price range typography if present
            if (element.type === "variablePriceRange") {
              const rangeEl = element as VariablePriceRangeElement;

              // Helper to scale partial responsive typography
              const scalePartialTypography = (
                typo: Partial<ResponsiveTypographySettings> | undefined
              ): Partial<ResponsiveTypographySettings> | undefined => {
                if (!typo) return typo;
                const result: Partial<ResponsiveTypographySettings> = {
                  ...typo,
                };
                if (typo.fontSize) {
                  if (isResponsiveValue(typo.fontSize)) {
                    result.fontSize = scaleResponsiveValue(typo.fontSize, 8, 200);
                  }
                }
                return result;
              };

              // Scale min/max price typography
              rangeEl.minPriceTypography = scalePartialTypography(rangeEl.minPriceTypography);
              rangeEl.maxPriceTypography = scalePartialTypography(rangeEl.maxPriceTypography);
              rangeEl.separatorTypography = scalePartialTypography(rangeEl.separatorTypography);

              // Scale min/max decimal typography
              // If decimal exists but doesn't have explicit fontSize, calculate it from the price font size
              if (rangeEl.minDecimal) {
                // Get the min price font size (inherit from main if not set)
                const minPriceFontSize = rangeEl.minPriceTypography?.fontSize ?? element.typography.fontSize;
                // Initialize typography if it doesn't exist
                if (!rangeEl.minDecimal.typography) {
                  rangeEl.minDecimal.typography = {};
                }
                // Get or calculate the decimal font size
                if (rangeEl.minDecimal.typography.fontSize) {
                  const minDecFontSize = rangeEl.minDecimal.typography.fontSize;
                  if (isResponsiveValue(minDecFontSize)) {
                    rangeEl.minDecimal.typography.fontSize = scaleResponsiveValue(minDecFontSize, 6, 120);
                  }
                } else {
                  // No explicit fontSize - calculate based on price font size and scale
                  const baseFontSize = isResponsiveValue(minPriceFontSize) ? minPriceFontSize.desktop : 24;
                  const calculatedDecSize = Math.round(baseFontSize * 0.6);
                  const scaledDecSize = Math.max(6, Math.min(120, Math.round(calculatedDecSize * scaleFactor)));
                  rangeEl.minDecimal.typography.fontSize = createResponsiveValue(scaledDecSize);
                }
              }
              if (rangeEl.maxDecimal) {
                // Get the max price font size (inherit from main if not set)
                const maxPriceFontSize = rangeEl.maxPriceTypography?.fontSize ?? element.typography.fontSize;
                // Initialize typography if it doesn't exist
                if (!rangeEl.maxDecimal.typography) {
                  rangeEl.maxDecimal.typography = {};
                }
                // Get or calculate the decimal font size
                if (rangeEl.maxDecimal.typography.fontSize) {
                  const maxDecFontSize = rangeEl.maxDecimal.typography.fontSize;
                  if (isResponsiveValue(maxDecFontSize)) {
                    rangeEl.maxDecimal.typography.fontSize = scaleResponsiveValue(maxDecFontSize, 6, 120);
                  }
                } else {
                  // No explicit fontSize - calculate based on price font size and scale
                  const baseFontSize = isResponsiveValue(maxPriceFontSize) ? maxPriceFontSize.desktop : 24;
                  const calculatedDecSize = Math.round(baseFontSize * 0.6);
                  const scaledDecSize = Math.max(6, Math.min(120, Math.round(calculatedDecSize * scaleFactor)));
                  rangeEl.maxDecimal.typography.fontSize = createResponsiveValue(scaledDecSize);
                }
              }
            }

            // Scale padding proportionally (responsive)
            element.padding = {
              top: scaleResponsiveValue(element.padding.top, 0, 200),
              right: scaleResponsiveValue(element.padding.right, 0, 200),
              bottom: scaleResponsiveValue(element.padding.bottom, 0, 200),
              left: scaleResponsiveValue(element.padding.left, 0, 200),
            };

            // Recalculate size based on new font size to ensure text fits
            // For discount badges, include format and customFormat for proper sizing
            const discountFormat = element.type === "discountBadge" ? (element as DiscountBadgeElement).format : undefined;
            const customFormat = element.type === "discountBadge" ? (element as DiscountBadgeElement).customFormat : undefined;
            const isPriceEl = element.type === "regularPrice" || element.type === "salePrice";
            const isTextEl = element.type === "text";
            // For price elements, check if decimals are shown
            const showDecimal = isPriceEl && "decimal" in element ? (element as PriceElement).decimal?.show ?? true : true;
            // For text elements, get text content
            const textContent = isTextEl && "text" in element ? (element as TextElement).text : undefined;
            const displayText = getElementDisplayText(element.type, discountFormat, customFormat, showDecimal, textContent);
            // Get decimal font size as plain number for size calculation
            const rawDecimalFontSize =
              isPriceEl && "decimal" in element && showDecimal ? (element as PriceElement).decimal?.typography?.fontSize : undefined;
            // Extract desktop value if it's a responsive value
            const decimalFontSize =
              rawDecimalFontSize !== undefined
                ? isResponsiveValue(rawDecimalFontSize)
                  ? rawDecimalFontSize.desktop
                  : rawDecimalFontSize
                : undefined;

            // For text elements with fixed/full width mode, respect the user's width setting
            if (isTextEl && (element as TextElement).widthMode !== "auto") {
              element.size = size;
            } else {
              const calculatedSize = calculateTextSize(
                displayText,
                getDesktopTypography(element.typography),
                getDesktopPadding(element.padding),
                isPriceEl && showDecimal,
                decimalFontSize
              );

              // Use the calculated size to ensure text fits properly
              element.size = {
                width: calculatedSize.width,
                height: calculatedSize.height,
              };
            }
          } else {
            // Small change - just update the size directly
            element.size = size;
          }
        }
      }),

    rotateElement: (id, rotation) =>
      set((state) => {
        const element = state.elements.find((el) => el.id === id);
        if (element) {
          element.rotation = rotation;
        }
      }),

    // Canvas
    setZoom: (zoom) =>
      set((state) => {
        state.zoom = Math.max(0.5, Math.min(2, zoom));
      }),

    setPan: (pan) =>
      set((state) => {
        state.pan = pan;
      }),

    resetView: () =>
      set((state) => {
        state.zoom = 1;
        state.pan = { x: 0, y: 0 };
      }),

    toggleGrid: () =>
      set((state) => {
        state.showGrid = !state.showGrid;
      }),

    toggleGuides: () =>
      set((state) => {
        state.showGuides = !state.showGuides;
      }),

    toggleContainerBounds: () =>
      set((state) => {
        state.showContainerBounds = !state.showContainerBounds;
      }),

    toggleSnap: () =>
      set((state) => {
        state.snapToGuides = !state.snapToGuides;
      }),

    setActiveGuides: (guides) =>
      set((state) => {
        state.activeGuides = guides;
      }),

    // History (basic implementation)
    saveToHistory: () =>
      set((state) => {
        const currentState = {
          elements: JSON.parse(JSON.stringify(state.elements)),
          selectedIds: [...state.selectedIds],
          hoveredId: state.hoveredId,
          containerSelected: state.containerSelected,
          containerSettings: JSON.parse(JSON.stringify(state.containerSettings)),
          zoom: state.zoom,
          pan: { ...state.pan },
          showGrid: state.showGrid,
          showGuides: state.showGuides,
          showContainerBounds: state.showContainerBounds,
          snapToGuides: state.snapToGuides,
          snapThreshold: state.snapThreshold,
          activeGuides: [],
        };
        // Remove future states if we're not at the end
        state.history = state.history.slice(0, state.historyIndex + 1);
        state.history.push(currentState);
        state.historyIndex = state.history.length - 1;
        // Limit history size
        if (state.history.length > 50) {
          state.history.shift();
          state.historyIndex--;
        }
      }),

    undo: () =>
      set((state) => {
        if (state.historyIndex > 0) {
          state.historyIndex--;
          const previousState = state.history[state.historyIndex];
          Object.assign(state, previousState);
        }
      }),

    redo: () =>
      set((state) => {
        if (state.historyIndex < state.history.length - 1) {
          state.historyIndex++;
          const nextState = state.history[state.historyIndex];
          Object.assign(state, nextState);
        }
      }),
  }))
);

// Selector hooks for performance
export const useElements = () => useCanvasStore((state) => state.elements);
export const useSelectedIds = () => useCanvasStore((state) => state.selectedIds);
export const useSelectedElements = () => {
  const elements = useCanvasStore((state) => state.elements);
  const selectedIds = useCanvasStore((state) => state.selectedIds);
  // Only recompute when elements or selectedIds change
  return elements.filter((el) => selectedIds.includes(el.id));
};
export const useHoveredId = () => useCanvasStore((state) => state.hoveredId);
export const useZoom = () => useCanvasStore((state) => state.zoom);
export const usePan = () => useCanvasStore((state) => state.pan);
export const useActiveScenario = () => useCanvasStore((state) => state.activeScenario);
export const useSettingsMode = () => useCanvasStore((state) => state.settingsMode);
export const useIsCompactMode = () => useCanvasStore((state) => state.settingsMode === "floating");

// Container state selectors
export const useContainerSelected = () => useCanvasStore((state) => state.containerSelected);
export const useContainerSettings = () => useCanvasStore((state) => state.containerSettings);

// Check if a price element type already exists
export const useHasPriceElement = (type: "regularPrice" | "salePrice") =>
  useCanvasStore((state) => state.elements.some((el) => el.type === type));

// Check if regularPrice element exists
export const useHasRegularPrice = () => useCanvasStore((state) => state.elements.some((el) => el.type === "regularPrice"));

// Check if salePrice element exists
export const useHasSalePrice = () => useCanvasStore((state) => state.elements.some((el) => el.type === "salePrice"));

// Check if variablePriceRange element exists
export const useHasVariablePriceRange = () => useCanvasStore((state) => state.elements.some((el) => el.type === "variablePriceRange"));
