/**
 * Element Styles
 *
 * Unified types and utilities for UI element CSS styling.
 * Used by constructor, RuntimePresentation, and element-type-defaults admin pages.
 */

import type { CSSProperties } from 'react';

/**
 * Normalize a numeric value to include a CSS unit suffix.
 * @param value - The value to normalize
 * @param unit - The unit to append (e.g., 'px', 'vw', 'vh', 's')
 * @returns Normalized value with unit, or empty string if invalid
 */
function normalizeWithUnit(
  value: string | number | undefined,
  unit: string,
): string {
  if (value === null || value === undefined || value === '') return '';

  const str = String(value).trim();
  if (!str) return '';

  // Already has a unit - return as is
  if (/[a-z%]+$/i.test(str)) {
    return str;
  }

  // Just a number - append unit
  const num = parseFloat(str);
  if (Number.isFinite(num)) {
    return `${num}${unit}`;
  }

  return str;
}

/** Normalize pixel values (border, borderRadius, fontSize for description) */
export const normalizePixelValue = (value: string | number | undefined) =>
  normalizeWithUnit(value, 'px');

/** Normalize viewport width values (width, minWidth, maxWidth) */
export const normalizeViewportWidth = (value: string | number | undefined) =>
  normalizeWithUnit(value, 'vw');

/** Normalize viewport height values (height, minHeight, maxHeight) */
export const normalizeViewportHeight = (value: string | number | undefined) =>
  normalizeWithUnit(value, 'vh');

/**
 * CSS style properties supported by UI elements.
 * These properties can be set in element-type-defaults and applied at runtime.
 */
export interface ElementStyleProperties {
  width?: string;
  height?: string;
  minWidth?: string;
  maxWidth?: string;
  minHeight?: string;
  maxHeight?: string;
  margin?: string;
  padding?: string;
  gap?: string;
  fontSize?: string;
  lineHeight?: string;
  fontWeight?: string;
  border?: string;
  borderRadius?: string;
  opacity?: string;
  boxShadow?: string;
  display?: string;
  position?: string;
  justifyContent?: string;
  alignItems?: string;
  textAlign?: string;
  zIndex?: string;
  backgroundColor?: string;
  color?: string;
}

/**
 * Array of CSS property names for iteration.
 * Used when applying styles from element data to CSSProperties.
 */
export const ELEMENT_STYLE_PROPS = [
  'width',
  'height',
  'minWidth',
  'maxWidth',
  'minHeight',
  'maxHeight',
  'margin',
  'padding',
  'gap',
  'fontSize',
  'lineHeight',
  'fontWeight',
  'border',
  'borderRadius',
  'boxShadow',
  'display',
  'position',
  'justifyContent',
  'alignItems',
  'textAlign',
  'zIndex',
  'backgroundColor',
  'color',
] as const;

/**
 * Properties that need numeric conversion
 */
const NUMERIC_PROPS = ['opacity', 'zIndex'] as const;

/**
 * Get trimmed CSS value from unknown input.
 * Returns empty string for null/undefined, but preserves '0' for explicit zero values.
 */
const getTrimmedValue = (value: unknown): string => {
  if (value === null || value === undefined) return '';
  // Preserve 0 as '0' - explicit zero should be applied
  if (value === 0) return '0';
  return String(value).trim();
};

/**
 * Build React CSSProperties from element style properties.
 * Handles type coercion for special properties like opacity and zIndex.
 *
 * @param element - Object containing style properties
 * @returns React CSSProperties object
 *
 * @example
 * const style = buildElementStyle({
 *   width: '100px',
 *   opacity: '0.5',
 *   display: 'flex',
 * });
 */
export function buildElementStyle(
  element: Partial<ElementStyleProperties>,
): CSSProperties {
  const style: CSSProperties = {};
  const source = element as Record<string, unknown>;

  // Properties that need viewport width unit (vw)
  const vwProps = ['width', 'minWidth', 'maxWidth'];
  // Properties that need viewport height unit (vh)
  const vhProps = ['height', 'minHeight', 'maxHeight'];
  // Properties that need pixel unit (px)
  const pxProps = ['border', 'borderRadius'];

  // Apply string properties with unit normalization where needed
  ELEMENT_STYLE_PROPS.forEach((prop) => {
    const rawValue = getTrimmedValue(source[prop]);
    if (!rawValue) return;

    let value = rawValue;

    // Apply unit normalization based on property type
    if (vwProps.includes(prop)) {
      value = normalizeViewportWidth(rawValue);
    } else if (vhProps.includes(prop)) {
      value = normalizeViewportHeight(rawValue);
    } else if (pxProps.includes(prop)) {
      value = normalizePixelValue(rawValue);
    }

    if (value) {
      (style as Record<string, unknown>)[prop] = value;
    }
  });

  // Handle opacity (needs numeric conversion)
  const opacityValue = getTrimmedValue(source.opacity);
  if (opacityValue) {
    const parsed = Number(opacityValue);
    if (Number.isFinite(parsed)) {
      style.opacity = parsed;
    }
  }

  // Handle zIndex (needs numeric conversion, already in style from loop but override with number)
  const zIndexValue = getTrimmedValue(source.zIndex);
  if (zIndexValue) {
    const parsed = Number(zIndexValue);
    if (Number.isFinite(parsed)) {
      style.zIndex = parsed;
    }
  }

  return style;
}

/**
 * All style property names including numeric ones.
 * Used for form state management in element-type-defaults admin.
 */
export const ALL_STYLE_PROPS = [...ELEMENT_STYLE_PROPS, 'opacity'] as const;

export type StylePropName = (typeof ALL_STYLE_PROPS)[number];
