import { isArray } from 'lodash';
import React, {
  AnchorHTMLAttributes,
  ButtonHTMLAttributes,
  FormHTMLAttributes,
  HTMLAttributes,
  InputHTMLAttributes,
  LabelHTMLAttributes,
} from 'react';
import { Link, LinkProps as RouterLinkProps } from 'react-router-dom';
import { SilkeColor } from '../../silke-theme-provider';
import styles from './silke-box.scss';

export type BoxSpace =
  | 'xxs'
  | 'xs'
  | 's'
  | 'sm'
  | 'm'
  | 'l'
  | 'xl'
  | 'xxl'
  | 'xxxl'
  | 'none'
  | boolean;
export type BoxSize = 'xs' | 's' | 'base' | 'm' | 'l' | 'xl' | 'xxl' | '256' | '512';

export type BoxAlign = 'center' | 'start' | 'end' | 'spread' | 'space' | boolean;

export type TagAttributes = {
  a: Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'color'>;
  button: Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'color'>;
  input: Omit<InputHTMLAttributes<HTMLInputElement>, 'color'>;
  div: Omit<HTMLAttributes<HTMLDivElement>, 'color'>;
  label: Omit<LabelHTMLAttributes<HTMLLabelElement>, 'color'>;
  form: Omit<FormHTMLAttributes<HTMLFormElement>, 'color'>;
  ol: Omit<HTMLAttributes<HTMLOListElement>, 'color'>;
  ul: Omit<HTMLAttributes<HTMLUListElement>, 'color'>;
  li: Omit<HTMLAttributes<HTMLLIElement>, 'color'>;
  header: Omit<HTMLAttributes<HTMLDivElement>, 'color'>;
  dl: Omit<HTMLAttributes<HTMLDListElement>, 'color'>;
};

export const Flex = ({ weight = 100 }: { weight?: number }) => (
  <div style={{ flex: `1 1 ${weight}%` }} />
);

type BaseProps = {
  /** Layout contnet in column */
  column?: boolean;
  /** Should fill container */
  fill?: boolean;
  /** Flex gap */
  gap?: BoxSpace;
  /** Vertical/horizontal space */
  pad?: BoxSpace;
  /** Vertical padding in element */
  vPad?: BoxSpace;
  /** Horizontal padding in element */
  hPad?: BoxSpace;
  /** Sets both width and height */
  size?: BoxSize;
  /** Width of the box (x2 of the spaceing, example: 'm' = 32px wide) */
  height?: BoxSize;
  /** Width of the box (x2 of the spaceing, example: 'm' = 32px wide) */
  width?: BoxSize;
  /** Width of the box (x2 of the spaceing, example: 'm' = 32px wide) */
  minHeight?: BoxSize;
  /** Width of the box (x2 of the spaceing, example: 'm' = 32px wide) */
  minWidth?: BoxSize;
  /** Background color */
  bg?: SilkeColor | 'blur';
  /** Background color on hover */
  bgHover?: SilkeColor;
  /** Text color */
  color?: SilkeColor;
  /** Border radius */
  rounded?: boolean | 'tiny' | 'small' | 'medium' | 'full';

  shadow?: 'level1' | 'level2' | 'level3' | 'level4';

  overflow?: 'hidden' | 'scroll-y';

  cursor?: 'pointer' | 'grab' | 'text';

  userSelect?: 'none' | 'text';

  /** Text color on hover */
  colorHover?: SilkeColor;
  aspectRatio?: '16:9' | '4:3' | '1:1';
  /** Flex item */
  flex?: boolean;
  /** Wrap items */
  wrap?: boolean;
  /** Align content of box, use array to define separat for x and y axis */
  align?: BoxAlign | [BoxAlign, BoxAlign];
  vAlign?: BoxAlign;
  hAlign?: BoxAlign;
  /** Debug adds an out line to all box and child boxes */
  debug?: boolean;
  scroll?: boolean | 'show';
  rel?: string;
};

type InputProps = {
  tag: 'input';
} & TagAttributes['input'] &
  BaseProps;

export type BoxButtonProps = {
  tag?: 'button';
} & TagAttributes['button'] &
  BaseProps;

type AnchorProps = {
  tag: 'a';
} & TagAttributes['a'] &
  BaseProps;

export type BoxLinkProps = {
  tag: 'link';
} & RouterLinkProps &
  BaseProps;

export type BoxDivProps = {
  tag?: 'div';
} & TagAttributes['div'] &
  BaseProps;

export type BoxNavProps = {
  tag?: 'nav';
} & TagAttributes['div'] &
  BaseProps;

export type BoxOlProps = {
  tag?: 'ol';
} & TagAttributes['ol'] &
  BaseProps;

export type BoxUlProps = {
  tag?: 'ul';
} & TagAttributes['ul'] &
  BaseProps;

export type BoxLiProps = {
  tag?: 'li';
} & TagAttributes['li'] &
  BaseProps;

export type BoxLabelProps = {
  tag?: 'label';
} & TagAttributes['label'] &
  BaseProps;

export type BoxFormProps = {
  tag?: 'form';
} & TagAttributes['form'] &
  BaseProps;

export type BoxHeaderProps = {
  tag?: 'header';
} & TagAttributes['header'] &
  BaseProps;

export type BoxDlProps = {
  tag?: 'dl';
} & TagAttributes['dl'] &
  BaseProps;

export type BoxDtProps = {
  tag?: 'dt';
} & TagAttributes['dl'] &
  BaseProps;

export type BoxDdProps = {
  tag?: 'dd';
} & TagAttributes['dl'] &
  BaseProps;

export type BoxProps =
  | InputProps
  | BoxButtonProps
  | AnchorProps
  | BoxLinkProps
  | BoxDivProps
  | BoxLabelProps
  | BoxFormProps
  | BoxNavProps
  | BoxOlProps
  | BoxUlProps
  | BoxLiProps
  | BoxHeaderProps
  | BoxDlProps
  | BoxDtProps
  | BoxDdProps;

function BoxComp(
  {
    aspectRatio,
    column,
    fill,
    gap,
    pad,
    vPad,
    hPad,
    size,
    width = size,
    height = size,
    minWidth,
    minHeight,
    children,
    color,
    colorHover,
    bg,
    bgHover,
    tag,
    flex,
    wrap,
    align,
    vAlign,
    hAlign,
    className,
    debug,
    scroll,
    rounded,
    shadow,
    overflow,
    cursor,
    userSelect,
    ...rest
  }: BoxProps,
  ref: any,
) {
  const cl = [styles.box];
  if (className) cl.push(className);

  if (column) cl.push(styles.column);
  if (flex) cl.push(styles.flex);
  if (fill) cl.push(styles.fill);
  if (overflow) cl.push(styles['overflow-' + overflow]);
  if (wrap) cl.push(styles.wrap);
  if (shadow) cl.push(styles['shadow-' + shadow]);
  if (rounded) cl.push(styles[`rounded-${rounded === true ? 'small' : rounded}`]);
  if (cursor) cl.push(styles['cursor-' + cursor]);
  if (aspectRatio) {
    rest.style = { ...rest.style, aspectRatio: aspectRatio.replace(':', '/') };
  }

  if (pad) {
    if (pad === true) pad = 'm';
    cl.push(styles['hPad-' + pad], styles['vPad-' + pad]);
  } else {
    if (vPad) cl.push(styles['vPad-' + (vPad === true ? 'm' : vPad)]);
    if (hPad) cl.push(styles['hPad-' + (hPad === true ? 'm' : hPad)]);
  }

  if (gap) cl.push(styles['gap-' + (gap === true ? 'm' : gap)]);

  if (width) cl.push(styles['width-' + width]);
  if (height) cl.push(styles['height-' + height]);

  if (minWidth) cl.push(styles['minWidth-' + minWidth]);
  if (minHeight) cl.push(styles['minHeight-' + minHeight]);

  if (color) cl.push('color-' + color);
  if (colorHover) cl.push('color-hover-' + colorHover);
  if (bg) cl.push('bg-' + bg);
  if (bgHover) cl.push('bg-hover-' + bgHover);

  if (userSelect) cl.push(styles['user-select-' + userSelect]);

  if (isArray(align)) {
    [hAlign, vAlign] = align;
  } else if (align) {
    hAlign = align;
    vAlign = align;
  }

  if (hAlign) cl.push(styles['h-' + (hAlign === true ? 'center' : hAlign)]);
  if (vAlign) cl.push(styles['v-' + (vAlign === true ? 'center' : vAlign)]);

  if (scroll) cl.push(scroll === 'show' ? styles.showScroll : styles.scroll);
  if (debug) cl.push(styles.debug);

  return React.createElement(
    tag === 'link' ? Link : tag || 'div',
    { className: cl.join(' '), ref, ...rest } as any,
    children,
  );
}

export const SilkeBox = React.forwardRef(BoxComp);
SilkeBox.displayName = 'SilkeBox';
