import cc from 'classcat';

export interface BoxOwnProps<E extends React.ElementType = React.ElementType> {
  as?: E;
  className?: string;
  style?: React.CSSProperties;
  width?: string | number;
  height?: string | number;
  position?: string;
  top?: string | number;
  left?: string | number;
  right?: string | number;
  bottom?: string | number;
  zIndex?: number;
  display?: string;
  column?: boolean;
  wrap?: boolean;
  grow?: boolean;
  flex?: 'grow' | 'none';
  align?: 'start' | 'end' | 'center';
  justify?: 'start' | 'end' | 'center' | 'between' | 'around';
  margin?: string | number;
  marginX?: string | number;
  marginY?: string | number;
  padding?: number;
  paddingX?: number;
  paddingY?: number;
  spacing?: number;
  spacingY?: number;
  overflow?: 'hidden' | 'scroll' | 'auto' | 'autoX' | 'autoY';
}

export type BoxProps<E extends React.ElementType> = BoxOwnProps<E> &
  Omit<React.ComponentProps<E>, keyof BoxOwnProps>;

const defaultElement = 'div';

export const Box = <E extends React.ElementType = typeof defaultElement>({
  as,
  className,
  style,
  width,
  height,
  position,
  top,
  left,
  right,
  bottom,
  zIndex,
  display,
  column,
  wrap,
  grow,
  flex,
  align,
  justify,
  margin,
  marginX,
  marginY,
  padding,
  paddingX,
  paddingY,
  spacing,
  spacingY,
  overflow,
  ...props
}: BoxProps<E>) => {
  const Element = as || defaultElement;
  const cn = cc([
    position,
    {
      flex: display === 'flex',

      alignStart: align === 'start',
      alignEnd: align === 'end',
      alignCenter: align === 'center',

      justifyStart: justify === 'start',
      justifyEnd: justify === 'end',
      justifyCenter: justify === 'center',
      justifyBetween: justify === 'between',
      justifyAround: justify === 'around',

      flexColumn: column,
      flexWrap: wrap,

      flexGrow: flex === 'grow',
      flexNone: flex === 'none',
    },

    {
      overflowHidden: overflow === 'hidden',
      overflowScroll: overflow === 'scroll',
      overflowAuto: overflow === 'auto',
      overflowAutoX: overflow === 'autoX',
      overflowAutoY: overflow === 'autoY',
    },

    margin !== undefined ? `margin${margin}` : '',
    marginX !== undefined ? `marginX${marginX}` : '',
    marginY !== undefined ? `marginY${marginY}` : '',

    padding !== undefined ? `padding${padding}` : '',
    paddingX !== undefined ? `paddingX${paddingX}` : '',
    paddingY !== undefined ? `paddingY${paddingY}` : '',

    spacing !== undefined ? `spacing${spacing}` : '',
    spacingY !== undefined ? `spacingY${spacingY}` : '',

    className,
  ]);

  const styles = {
    top,
    left,
    right,
    bottom,
    zIndex,
    width,
    height,
    ...style,
  };

  return <Element className={cn} style={styles} {...props} />;
};
