import chroma from 'chroma-js';

import colors from '@workday/canvas-colors-web';
import {CanvasColor} from '@workday/canvas-kit-react/tokens';

import {pickForegroundColor} from '../utils';
// Should these be exported?
import {deepMerge} from '../utils/deepMerge';
import {memoize} from '../utils/memoize';
import {defaultCanvasTheme} from './theme';
import {
  CanvasTheme,
  CanvasThemePalette,
  ContentDirection,
  PartialCanvasTheme,
  PartialCanvasThemePalette,
} from './types';

/**
 * @deprecated ⚠️ `shiftColor` is deprecated and will be removed in a future major version. While we work on an algorithm for color shifting, you can use [oklch from or with calc](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch) to calculate colors.
 */
export const shiftColor = memoize(
  (hexColor: string, amount = 100) => {
    const canvasColor = Object.keys(colors).find(color => colors[color] === hexColor);

    if (canvasColor) {
      const colorRegex = /([a-zAz]*)(\d{3})/g;
      const match = colorRegex.exec(canvasColor);

      if (match) {
        const baseColor = match[1];
        const shadeNumber = parseInt(match[2], 10);

        const newShade = shadeNumber + amount;

        if (newShade >= 100 && newShade <= 600) {
          return colors[(baseColor + newShade) as CanvasColor];
        }
      }
    }

    try {
      const newColor =
        amount > 0
          ? chroma(hexColor).darken(amount / 100)
          : chroma(hexColor).brighten(-amount / 100);
      return newColor.hex();
    } catch (e) {
      console.warn(`Invalid color '${hexColor}' used in theme`);
      return hexColor;
    }
  },
  (color, value) => `${color}.${value}`
);

function fillPalette(
  defaultPalette: CanvasThemePalette,
  palette?: PartialCanvasThemePalette
): CanvasThemePalette | {} {
  if (!palette) {
    return {};
  }

  const main = palette.main || defaultPalette.main;
  const dark =
    palette.dark || (palette.main && shiftColor(palette.main, 100)) || defaultPalette.dark;
  const darkest =
    palette.darkest || (palette.main && shiftColor(palette.main, 200)) || defaultPalette.darkest;
  const light =
    palette.light || (palette.main && shiftColor(palette.main, -100)) || defaultPalette.light;
  const lighter =
    palette.lighter || (palette.main && shiftColor(palette.main, -150)) || defaultPalette.lighter;
  const lightest =
    palette.lightest || (palette.main && shiftColor(palette.main, -200)) || defaultPalette.lightest;
  const contrast = palette.contrast || pickForegroundColor(main) || defaultPalette.contrast;

  return {
    lightest,
    lighter,
    light,
    main,
    dark,
    darkest,
    contrast,
  };
}

function calculateCanvasTheme(partialTheme: PartialCanvasTheme): CanvasTheme {
  const {palette = {}, breakpoints = {}, direction, ...extraFields} = partialTheme;
  const {primary, alert, error, success, neutral, common = {}} = palette!;

  const mergeable: PartialCanvasTheme = {
    palette: {
      common,
      primary: fillPalette(defaultCanvasTheme.palette.primary, primary),
      alert: fillPalette(defaultCanvasTheme.palette.alert, alert),
      error: fillPalette(defaultCanvasTheme.palette.error, error),
      success: fillPalette(defaultCanvasTheme.palette.success, success),
      neutral: fillPalette(defaultCanvasTheme.palette.neutral, neutral),
    },
    breakpoints,
    direction: direction === ContentDirection.RTL ? direction : ContentDirection.LTR,
  };

  return deepMerge(deepMerge({}, defaultCanvasTheme), {...mergeable, ...extraFields});
}

/**
 * Creates a full {@link CanvasTheme} from any partial theme by deeply merging with the
 * `defaultCanvasTheme` object. The function is memoized, but it is best to run this function
 * only once and save the result.
 * @deprecated ⚠️ `createCanvasTheme` is deprecated and will be removed in a future major version. Please use our CSS Branding tokens to create a theme. For more information, please use our [theming docs](https://workday.github.io/canvas-kit/?path=/docs/features-theming-overview--docs).
 */
export const createCanvasTheme = memoize(calculateCanvasTheme, (...args) => JSON.stringify(args));
