/* eslint-disable @atlaskit/design-system/ensure-design-token-usage */
import { css as cssEmotion } from '@emotion/react';
import { media } from '../responsive/media-helper';
import { backgroundColorMap, borderColorMap, borderRadiusMap, borderWidthMap, dimensionMap, layerMap, shadowMap, spaceMap, textColorMap } from './style-maps.partial';
const tokensMap = {
  backgroundColor: backgroundColorMap,
  blockSize: dimensionMap,
  borderColor: borderColorMap,
  borderRadius: borderRadiusMap,
  borderWidth: borderWidthMap,
  bottom: spaceMap,
  boxShadow: shadowMap,
  color: textColorMap,
  columnGap: spaceMap,
  gap: spaceMap,
  height: dimensionMap,
  inlineSize: dimensionMap,
  inset: spaceMap,
  insetBlock: spaceMap,
  insetBlockEnd: spaceMap,
  insetBlockStart: spaceMap,
  insetInline: spaceMap,
  insetInlineEnd: spaceMap,
  insetInlineStart: spaceMap,
  margin: spaceMap,
  marginBlock: spaceMap,
  marginBlockEnd: spaceMap,
  marginBlockStart: spaceMap,
  marginInline: spaceMap,
  marginInlineEnd: spaceMap,
  marginInlineStart: spaceMap,
  left: spaceMap,
  maxBlockSize: dimensionMap,
  maxHeight: dimensionMap,
  maxInlineSize: dimensionMap,
  maxWidth: dimensionMap,
  minBlockSize: dimensionMap,
  minHeight: dimensionMap,
  minInlineSize: dimensionMap,
  minWidth: dimensionMap,
  outlineOffset: spaceMap,
  outlineWidth: borderWidthMap,
  outlineColor: borderColorMap,
  padding: spaceMap,
  paddingBlock: spaceMap,
  paddingBlockEnd: spaceMap,
  paddingBlockStart: spaceMap,
  paddingBottom: spaceMap,
  paddingInline: spaceMap,
  paddingInlineEnd: spaceMap,
  paddingInlineStart: spaceMap,
  paddingLeft: spaceMap,
  paddingRight: spaceMap,
  paddingTop: spaceMap,
  right: spaceMap,
  rowGap: spaceMap,
  top: spaceMap,
  width: dimensionMap,
  zIndex: layerMap
};
const uniqueSymbol = Symbol('UNSAFE_INTERNAL_styles');
const isSafeEnvToThrow = () => typeof process === 'object' && typeof process.env === 'object' && process.env.NODE_ENV !== 'production';
const reNestedSelectors = /(\.|\s|&+|\*\>|#|\[.*\])/;
const rePseudos = /^::?.*$/;
const reMediaQuery = /^@media .*$/;

/**
 * Reduce our media queries into a safe string for regex comparison.
 */
const reValidMediaQuery = new RegExp(`^(${Object.values(media.above).map(mediaQuery => mediaQuery.replace(/[.()]/g, '\\$&') // Escape the ".", "(", and ")" in the media query syntax.
).join('|')})$`);
const transformStyles = styleObj => {
  if (!styleObj || typeof styleObj !== 'object') {
    return styleObj;
  }

  // If styles are defined as a CSSObject[], recursively call on each element until we reach CSSObject
  if (Array.isArray(styleObj)) {
    return styleObj.map(transformStyles);
  }

  // Modifies styleObj in place. Be careful.
  Object.entries(styleObj).forEach(([key, value]) => {
    if (isSafeEnvToThrow()) {
      // We don't support `.class`, `[data-testid]`, `> *`, `#some-id`
      if (reNestedSelectors.test(key) && !reValidMediaQuery.test(key)) {
        throw new Error(`Styles not supported for key '${key}'.`);
      }
    }

    // If key is a pseudo class or a pseudo element, then value should be an object.
    // So, call transformStyles on the value
    if (rePseudos.test(key)) {
      styleObj[key] = transformStyles(value);
      return;
    }
    if (reMediaQuery.test(key)) {
      // @ts-expect-error
      styleObj[key] = transformStyles(value);
      return;
    }

    // We have now dealt with all the special cases, so,
    // check whether what remains is a style property
    // that can be transformed.
    if (!(key in tokensMap)) {
      return;
    }
    const tokenValue = tokensMap[key][value];
    styleObj[key] = tokenValue !== null && tokenValue !== void 0 ? tokenValue : value;
  });
  return styleObj;
};
const baseXcss = style => {
  const transformedStyles = transformStyles(style);
  return {
    [uniqueSymbol]: cssEmotion(transformedStyles)
  };
};

/**
 * @internal used in primitives
 * @returns a collection of styles that can be applied to the respective primitive
 */

export const parseXcss = args => {
  if (Array.isArray(args)) {
    return args.map(x => x && parseXcss(x)).filter(Boolean);
  }
  const {
    [uniqueSymbol]: styles
  } = args;
  if (typeof process && process.env.NODE_ENV === 'development' && typeof styles === 'undefined') {
    throw new Error('Styles generated from unsafe source, use the `xcss` export from `@atlaskit/primitives`.');
  }
  return styles;
};

// Media queries should not contain nested media queries

// Pseudos should not contain nested pseudos, or media queries

// unused private functions only so we can extract the return type from a generic function
const boxWrapper = style => xcss(style);
const spaceWrapper = style => xcss(style);
/**
 * ### xcss
 *
 * `xcss` is a safer, tokens-first approach to CSS-in-JS. It allows token-backed values for
 * CSS application.
 *
 * ```tsx
 * const styles = xcss({
 *   padding: 'space.100'
 * })
 * ```
 */
export function xcss(style) {
  return baseXcss(style);
}