// @flow
import type { StackedProps, StackedOptions } from './stacking.proptypes';
import {
  bpMedium,
  bpLarge,
  bpXLarge,
  breakPoints,
  type BreakPoint,
} from '../../breakpoints';

const JUSTIFY_CONTENT_MAP = {
  start: 'flex-start',
  end: 'flex-end',
  center: 'center',
  'space-between': 'space-between',
  'space-around': 'space-around',
  'space-evenly': 'space-evenly',
};

const DEFAULT_ALIGN = 'stretch';
const DEFAULT_JUSTIFY = 'flex-start';

const ALIGN_CONTENT_MAP = {
  start: 'flex-start',
  end: 'flex-end',
  center: 'center',
  stretch: 'stretch',
};

const getStackedOptions = ({
  stacked,
  justify,
  align,
  wrap,
}: StackedOptions): string => {
  if (!stacked) {
    return '';
  }

  const rules = [
    'display: flex;',
    `flex-direction: ${stacked === 'column' ? 'column' : 'row'};`,
    `justify-content: ${
      justify ? JUSTIFY_CONTENT_MAP[justify] : DEFAULT_JUSTIFY
    };`,
    `align-items: ${align ? ALIGN_CONTENT_MAP[align] : DEFAULT_ALIGN};`,
  ];

  if (wrap) {
    rules.push('flex-wrap: wrap');
  }

  return rules.join(' ');
};

export const getStackedRules = (stackedProps: StackedProps): string => {
  const rules = [];

  const globalOptions: StackedOptions = {
    stacked:
      typeof stackedProps.stacked === 'string'
        ? stackedProps.stacked
        : undefined,
    align:
      typeof stackedProps.align === 'string' ? stackedProps.align : undefined,
    justify:
      typeof stackedProps.justify === 'string'
        ? stackedProps.justify
        : undefined,
    wrap: Array.isArray(stackedProps.wrap) ? undefined : stackedProps.wrap,
  };

  rules.push(getStackedOptions(globalOptions));

  const optionsByBreakpoint: {
    [BreakPoint]: StackedOptions,
  } = breakPoints.reduce((acc, bp) => {
    acc[bp] = {
      stacked:
        stackedProps.stacked instanceof Object
          ? stackedProps.stacked?.[bp]
          : undefined,
      align:
        stackedProps.align instanceof Object
          ? stackedProps.align?.[bp]
          : undefined,
      justify:
        stackedProps.justify instanceof Object
          ? stackedProps.justify?.[bp]
          : undefined,
      wrap: Array.isArray(stackedProps.wrap)
        ? stackedProps.wrap.includes(bp)
        : undefined,
    };
    return acc;
  }, {});

  if (optionsByBreakpoint.sm.stacked) {
    rules.push(
      `@media (max-width: ${bpMedium}) {${getStackedOptions(
        optionsByBreakpoint.sm,
      )}}`,
    );
  }

  if (optionsByBreakpoint.md.stacked) {
    rules.push(
      `@media (min-width: ${bpMedium}) {${getStackedOptions(
        optionsByBreakpoint.md,
      )}}`,
    );
  }

  if (optionsByBreakpoint.lg.stacked) {
    rules.push(
      `@media (min-width: ${bpLarge}) {${getStackedOptions(
        optionsByBreakpoint.lg,
      )}}`,
    );
  }

  if (optionsByBreakpoint.xl.stacked) {
    rules.push(
      `@media (min-width: ${bpXLarge}) {${getStackedOptions(
        optionsByBreakpoint.xl,
      )}}`,
    );
  }

  return rules.join('\n');
};
