import { ApphouseThemeTokens } from '../../styles/defaults/app.token.values';
import {
  hasFontSizeUnit,
  isValidFontSizeUnit
} from '../../utils/units/isValidFontSizeUnit';
import { toPt } from '../../utils/units/toPt';
import { toRems } from '../../utils/units/toRems';
import { toSp } from '../../utils/units/toSp';
import { Token } from '../Token';
import { THEMES } from '../presets';
import { commonColors } from '../presets/commonColors';
import { COLOR, TOKEN } from '../style.interface';
import { SampleTestTheme } from './theme.utils.test';
import {
  getColorTokensLookupReferenceFromPalettes,
  getLookupTable,
  getReferenceTokenFromObject,
  getStyleTokenReferenceType,
  getTokenListId,
  getValueReferenceStringFromObject,
  hasColorReference,
  toTokensObject
} from './tokens.utils';

describe('objectify tokens', () => {
  test('getTokenListId', () => {
    let tokenId = getTokenListId({ key: 'small', type: 'spacing' });
    expect(tokenId).toBe('spacing.small');

    tokenId = getTokenListId({});
    expect(tokenId).toBe('unknownTokenId');

    tokenId = getTokenListId(undefined);
    expect(tokenId).toBe('unknownTokenId');

    tokenId = getTokenListId({ key: 'small' });
    expect(tokenId).toBe('unknownTokenId');

    tokenId = getTokenListId(5);
    expect(tokenId).toBe('unknownTokenId');

    tokenId = getTokenListId('oueoujqowdjoa');
    expect(tokenId).toBe('unknownTokenId');
  });

  test('objectifyToken dark theme', () => {
    const tokens: Record<string, Token> = {
      'fontSize.small': new Token({
        key: 'small',
        value: '11px',
        type: 'fontSize'
      }),
      'fontSize.standard': new Token({
        key: 'standard',
        value: '15px',
        type: 'fontSize'
      }),
      'fontWeight.bold': new Token({
        key: 'bold',
        value: '700',
        type: 'fontWeight'
      })
    };

    const objToken = toTokensObject(tokens);

    expect(objToken).toEqual({
      fontSize: {
        small: '11px',
        standard: '15px'
      },
      fontWeight: {
        bold: '700'
      }
    });
  });
  test('getTokenReferenceType', () => {
    let type = getStyleTokenReferenceType('backgroundColor');
    expect(type).toEqual(COLOR);

    type = getStyleTokenReferenceType('color');
    expect(type).toEqual(COLOR);

    type = getStyleTokenReferenceType('spacings');
    expect(type).toEqual(TOKEN);
  });
  test('hasColorReference', () => {
    let ref = hasColorReference('backgroundColor');
    expect(ref).toEqual(true);

    ref = hasColorReference('color');
    expect(ref).toEqual(true);

    ref = hasColorReference('spacings');
    expect(ref).toEqual(false);
  });

  test('getReferenceTokenFromObject', () => {
    const colors = {
      WHITE: '#FFF',
      BLACK: '#000'
    };
    const obj = {
      backgroundColor: colors.WHITE
    };
    let referenceObject = getReferenceTokenFromObject(obj);
    expect(referenceObject).toEqual({
      key: 'backgroundColor',
      type: 'color',
      value: '#FFF'
    });

    referenceObject = getReferenceTokenFromObject(obj, 'button.primary');
    expect(referenceObject).toEqual({
      key: 'button.primary.backgroundColor',
      type: 'color',
      value: '#FFF'
    });
  });

  test('getReferenceTokenFromObject when there are nested selectors', () => {
    const primaryButtonStyles = THEMES.APPHOUSE_DARK.styles.button.primary;

    let referenceObject = getReferenceTokenFromObject(primaryButtonStyles);
    expect(referenceObject).toEqual({
      key: 'backgroundColor',
      type: 'color',
      value: '#FFF'
    });

    referenceObject = getReferenceTokenFromObject(
      primaryButtonStyles,
      'button.primary'
    );
    expect(referenceObject).toEqual({
      key: 'button.primary.backgroundColor',
      type: 'color',
      value: '#FFF'
    });
  });

  test('getLookupTable', () => {
    const colors = {
      WHITE: '#FFF',
      BLACK: '#000'
    };
    const colorsHash = getLookupTable(colors, {}, 'base');
    expect(colorsHash).toEqual({
      'base.WHITE': '#FFF',
      'base.BLACK': '#000'
    });

    const spacings = {
      borders: {
        medium: '2px',
        large: '3px',
        small: '2px'
      },
      margins: {
        medium: '5px',
        a: {
          b: {
            c: '6px'
          }
        }
      }
    };

    const spacingsHash = getLookupTable(spacings, {}, 'spacings');
    expect(spacingsHash).toEqual({
      'spacings.borders.small': '2px',
      'spacings.borders.medium': '2px', //this will not be part of it because it is a duplicate value
      'spacings.borders.large': '3px',
      'spacings.margins.medium': '5px',
      'spacings.margins.a.b.c': '6px'
    });
  });

  test('getValueReferenceStringFromObject', () => {
    const colors = {
      WHITE: '#FFF',
      BLACK: '#000'
    };
    let colorsHash = getLookupTable(colors, {}, 'base');
    let refString = getValueReferenceStringFromObject('#FFF', colorsHash);
    expect(refString).toEqual('base.WHITE');

    colorsHash = getLookupTable(colors, {});
    refString = getValueReferenceStringFromObject('#FFF', colorsHash);
    expect(refString).toEqual('WHITE');

    colorsHash = getLookupTable(colors, {}, 'colors');
    refString = getValueReferenceStringFromObject('#000', colorsHash);
    expect(refString).toEqual('colors.BLACK');
  });

  test('getValueReferenceStringFromObject using namespace', () => {
    const spacings = {
      borders: {
        medium: '2px',
        large: '3px'
      },
      padding: {
        small: '2px'
      },
      margins: {
        medium: '5px',
        a: {
          b: {
            c: '6px'
          }
        }
      }
    };
    const colorsHash = getLookupTable(spacings, {}, 'spacings');
    let refString = getValueReferenceStringFromObject(
      '#FFF',
      colorsHash,
      'padding'
    );
    expect(refString).toEqual('#FFF');

    refString = getValueReferenceStringFromObject('2px', colorsHash, 'padding');
    expect(refString).toEqual('spacings.padding.small');
  });

  test('correctly create lookup table for base colors', () => {
    const colorsHash = getLookupTable(commonColors, {}, 'base');

    expect(colorsHash).toEqual({
      'base.border': '#4B4A4A',
      'base.brand': 'rgb(0, 113, 227)',
      'base.brandAlt': 'rgb(0, 113, 227)',
      'base.error': '#FF0000',
      'base.info': 'rgb(0, 113, 227)',
      'base.onBrand': '#ececec',
      'base.onError': '#ececec',
      'base.onInfo': '#ececec',
      'base.onSuccess': '#1F1F1F',
      'base.onWarning': '#1F1F1F',
      'base.required': '#FF0000',
      'base.success': '#00B324',
      'base.toggleOn': 'rgb(0, 113, 227)',
      'base.warning': '#ffa500'
    });
  });

  test('correctly create lookup table for app tokens', () => {
    const tokens = getLookupTable(ApphouseThemeTokens);
    expect(tokens).toEqual({
      'borderWidth.default': '2px',
      'borderWidth.none': 0,
      'borderWidth.thin': '1px',
      'fontFamily.default':
        '-apple-system,BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica, sans-serif',
      'fontFamily.heading':
        '-apple-system,BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica, sans-serif',
      'fontFamily.monospace': 'monospace',
      'fontFamily.text':
        '-apple-system,BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica, sans-serif',
      'fontSize.caption': '10px',
      'fontSize.header': '42px',
      'fontSize.standard': '13px',
      'fontSize.standardLarge': '15px',
      'fontSize.standardSmall': '11px',
      'fontSize.subheader': '26px',
      'fontSize.subtitle': '16px',
      'fontSize.title': '18px',
      'fontWeight.bold': 600,
      'fontWeight.bolder': 700,
      'fontWeight.light': 300,
      'fontWeight.standard': 500,
      'iconSize.l': 30,
      'iconSize.m': 24,
      'iconSize.s': 12,
      'iconSize.standard': 16,
      'mediaQuery.l':
        '@media only screen and (min-width: 1280px) and (max-width: 1919px)',
      'mediaQuery.m':
        '@media only screen and (min-width: 960px) and (max-width: 1279px)',
      'mediaQuery.s':
        '@media only screen and (min-width: 600px) and (max-width: 959px)',
      'mediaQuery.xl': '@media only screen and (min-width: 1920px)',
      'mediaQuery.xs': '@media only screen and (max-width: 599px)',
      'radius.circle': '50%',
      'radius.default': '6px',
      'radius.l': '12px',
      'radius.m': '8px',
      'radius.s': '4px',
      'spacings.default': 8,
      'spacings.l': 20,
      'spacings.m': 14,
      'spacings.r': 10,
      'spacings.s': 4,
      'spacings.xl': 30,
      'spacings.xs': 2,
      'zIndex.default': 1,
      'zIndex.menu': 400,
      'zIndex.overlay': 1000,
      'zIndex.panel': 0,
      'zIndex.popup': 5000,
      'zIndex.toast': 300
    });
  });

  test('correctly creates lookup table for dark themed colors', () => {
    const darkColorsLookup = getLookupTable(
      THEMES.APPHOUSE_DARK.colors,
      {},
      'theme'
    );

    expect(darkColorsLookup).toEqual({
      'theme.primary': '#1F1F1F',
      'theme.primaryInverse': '#ececec',
      'theme.selection': '#39393B',
      'theme.surface': '#252526',
      'theme.surface10': '#2d2d2d',
      'theme.backgroundFocus': undefined,
      'theme.backgroundHover': undefined,
      'theme.border': '#4B4A4A',
      'theme.brand': 'rgb(0, 113, 227)',
      'theme.brandAlt': 'rgb(0, 113, 227)',
      'theme.error': '#FF0000',
      'theme.info': 'rgb(0, 113, 227)',
      'theme.onBrand': '#ececec',
      'theme.onError': '#ececec',
      'theme.onInfo': '#ececec',
      'theme.onSuccess': '#1F1F1F',
      'theme.onWarning': '#1F1F1F',
      'theme.required': '#FF0000',
      'theme.success': '#00B324',
      'theme.toggleOn': 'rgb(0, 113, 227)',
      'theme.warning': '#ffa500',
      'theme.borderOnPrimary': '#4B4A4A',
      'theme.borderOnPrimaryInverse': '#4B4A4A',
      'theme.borderOnSurface': '#4B4A4A',
      'theme.borderOnSurface10': '#4B4A4A',
      'theme.focusRing.inset': '#000000',
      'theme.focusRing.outline': '#FFFFFF',
      'theme.onPrimary': '#ececec',
      'theme.onPrimaryInverse': '#1F1F1F',
      'theme.onSelection': '#ececec',
      'theme.onSurface': '#ececec',
      'theme.onSurface10': '#ececec'
    });
  });

  test('correctly create lookup table for palette base colors', () => {
    const lookup = getColorTokensLookupReferenceFromPalettes(
      SampleTestTheme.palette,
      'base'
    );
    expect(lookup).toEqual({
      'base.BLACK': 'rgba(31, 31, 31, 1)',
      'base.GREEN': 'rgba(0, 179, 36, 1)',
      'base.GREY': 'rgba(75, 74, 74, 1)',
      'base.GREY_ALT': 'rgba(198, 198, 198, 1)',
      'base.GREY_ALT_50': 'rgba(45, 45, 45, 1)',
      'base.LIGHT_BLUE': 'rgba(0, 113, 227, 1)',
      'base.LIGHT_GRAY': 'rgba(211, 211, 211, 1)',
      'base.MONTANA': 'rgba(57, 57, 59, 1)',
      'base.NERO': 'rgba(37, 37, 38, 1)',
      'base.PURE_BLACK': 'rgba(0, 0, 0, 1)',
      'base.PURE_WHITE': 'rgba(255, 255, 255, 1)',
      'base.RED': 'rgba(255, 0, 0, 1)',
      'base.WHITE': 'rgba(236, 236, 236, 1)',
      'base.WHITEISH': 'rgba(221, 221, 221, 1)',
      'base.YELLOW': 'rgba(255, 165, 0, 1)'
    });
  });

  test('correctly create lookup table for palette dark colors', () => {
    const lookup = getColorTokensLookupReferenceFromPalettes(
      SampleTestTheme.palette,
      'base',
      'dark'
    );
    expect(lookup).toEqual({
      'theme.border': 'rgba(75, 74, 74, 1)',
      'theme.brand': 'rgba(0, 113, 227, 1)',
      'theme.brandAlt': 'rgba(0, 113, 227, 1)',
      'theme.error': 'rgba(255, 0, 0, 1)',
      'theme.info': 'rgba(0, 113, 227, 1)',
      'theme.inset': 'rgba(0, 0, 0, 1)',
      'theme.onBrand': 'rgba(236, 236, 236, 1)',
      'theme.onError': 'rgba(236, 236, 236, 1)',
      'theme.onInfo': 'rgba(236, 236, 236, 1)',
      'theme.onPrimary': 'rgba(75, 74, 74, 1)',
      'theme.onPrimaryInverse': 'rgba(75, 74, 74, 1)',
      'theme.onSuccess': 'rgba(31, 31, 31, 1)',
      'theme.onSurface': 'rgba(75, 74, 74, 1)',
      'theme.onSurface10': 'rgba(75, 74, 74, 1)',
      'theme.onWarning': 'rgba(31, 31, 31, 1)',
      'theme.outline': 'rgba(255, 255, 255, 1)',
      'theme.primary': 'rgba(236, 236, 236, 1)',
      'theme.primaryInverse': 'rgba(31, 31, 31, 1)',
      'theme.required': 'rgba(255, 0, 0, 1)',
      'theme.selection': 'rgba(236, 236, 236, 1)',
      'theme.success': 'rgba(0, 179, 36, 1)',
      'theme.surface': 'rgba(236, 236, 236, 1)',
      'theme.surface10': 'rgba(236, 236, 236, 1)',
      'theme.toggleOn': 'rgba(0, 113, 227, 1)',
      'theme.warning': 'rgba(255, 165, 0, 1)'
    });
  });

  test('correctly create lookup table for palette light colors', () => {
    const lookup = getColorTokensLookupReferenceFromPalettes(
      SampleTestTheme.palette,
      'base',
      'light'
    );
    expect(lookup).toEqual({
      'theme.border': 'rgba(75, 74, 74, 1)',
      'theme.brand': 'rgba(0, 113, 227, 1)',
      'theme.brandAlt': 'rgba(0, 113, 227, 1)',
      'theme.error': 'rgba(255, 0, 0, 1)',
      'theme.info': 'rgba(0, 113, 227, 1)',
      'theme.inset': 'rgba(255, 255, 255, 1)',
      'theme.onBrand': 'rgba(236, 236, 236, 1)',
      'theme.onError': 'rgba(236, 236, 236, 1)',
      'theme.onInfo': 'rgba(236, 236, 236, 1)',
      'theme.onPrimary': 'rgba(198, 198, 198, 1)',
      'theme.onPrimaryInverse': 'rgba(198, 198, 198, 1)',
      'theme.onSuccess': 'rgba(31, 31, 31, 1)',
      'theme.onSurface': 'rgba(198, 198, 198, 1)',
      'theme.onSurface10': 'rgba(198, 198, 198, 1)',
      'theme.onWarning': 'rgba(31, 31, 31, 1)',
      'theme.outline': 'rgba(0, 0, 0, 1)',
      'theme.primary': 'rgba(31, 31, 31, 1)',
      'theme.primaryInverse': 'rgba(236, 236, 236, 1)',
      'theme.required': 'rgba(255, 0, 0, 1)',
      'theme.selection': 'rgba(31, 31, 31, 1)',
      'theme.success': 'rgba(0, 179, 36, 1)',
      'theme.surface': 'rgba(31, 31, 31, 1)',
      'theme.surface10': 'rgba(31, 31, 31, 1)',
      'theme.toggleOn': 'rgba(0, 113, 227, 1)',
      'theme.warning': 'rgba(255, 165, 0, 1)'
    });
  });
});

describe('Token unit conversion', () => {
  describe('toRems', () => {
    test('converts raw number to rems', () => {
      let fontSize = toRems(1);
      expect(fontSize).toEqual('0.0625rem');
    });

    test('converts string number to rems', () => {
      let fontSize = toRems('1');
      expect(fontSize).toEqual('0.0625rem');
    });
    test('converts string with px unit to rems', () => {
      let fontSize = toRems('1px');
      expect(fontSize).toEqual('0.0625rem');
    });
    test('converts string with rem unit to rems', () => {
      let fontSize = toRems('1rem');
      expect(fontSize).toEqual('1rem');
    });
    test('converts string with rem unit without any numbers to rems', () => {
      let fontSize = toRems('rem');
      expect(fontSize).toEqual('0rem');
    });
    test('converts garbage input to 0rem', () => {
      let fontSize = toRems('adadqweqweq');
      expect(fontSize).toEqual('0rem');
    });
  });

  test('toSp', () => {
    let fontSize = toSp(1);
    expect(fontSize).toEqual('1sp');

    fontSize = toSp('1');
    expect(fontSize).toEqual('1sp');

    fontSize = toSp('1px');
    expect(fontSize).toEqual('1sp');

    fontSize = toSp('1rem');
    expect(fontSize).toEqual('16sp');

    fontSize = toSp('1xp');
    expect(fontSize).toEqual('1sp');

    fontSize = toSp('aldkjalskdj');
    expect(fontSize).toEqual('1sp');
  });

  test('toPt', () => {
    let fontSize = toPt(1);
    expect(fontSize).toEqual('1pt');

    fontSize = toPt('1');
    expect(fontSize).toEqual('1pt');

    fontSize = toPt('1px');
    expect(fontSize).toEqual('1pt');

    fontSize = toPt('1rem');
    expect(fontSize).toEqual('16pt');

    fontSize = toPt('1xp');
    expect(fontSize).toEqual('1pt');
  });
});

describe('Font Units Validation', () => {
  test('hasFontSizeUnit', () => {
    let fontSize = hasFontSizeUnit(1);
    expect(fontSize).toEqual(false);

    fontSize = hasFontSizeUnit('1px');
    expect(fontSize).toEqual(true);

    fontSize = hasFontSizeUnit('1sp');
    expect(fontSize).toEqual(true);

    fontSize = hasFontSizeUnit('1rem');
    expect(fontSize).toEqual(true);

    fontSize = hasFontSizeUnit('1ps');
    expect(fontSize).toEqual(false);

    fontSize = hasFontSizeUnit('1psx');
    expect(fontSize).toEqual(false);

    //@ts-ignore
    fontSize = hasFontSizeUnit(undefined);
    expect(fontSize).toEqual(false);

    //@ts-ignore
    fontSize = hasFontSizeUnit({});
    expect(fontSize).toEqual(false);
  });
  test('isValidFontSizeUnit', () => {
    let fontSize = isValidFontSizeUnit(1);
    expect(fontSize).toEqual(false);

    fontSize = isValidFontSizeUnit('1px');
    expect(fontSize).toEqual(true);

    fontSize = isValidFontSizeUnit('1sp');
    expect(fontSize).toEqual(true);

    fontSize = isValidFontSizeUnit('1rem');
    expect(fontSize).toEqual(true);

    fontSize = isValidFontSizeUnit('1remremrem');
    expect(fontSize).toEqual(false);

    fontSize = isValidFontSizeUnit('1rempx');
    expect(fontSize).toEqual(false);

    fontSize = isValidFontSizeUnit('1pxpt');
    expect(fontSize).toEqual(false);

    fontSize = isValidFontSizeUnit('1ps');
    expect(fontSize).toEqual(false);

    fontSize = isValidFontSizeUnit('1psx');
    expect(fontSize).toEqual(false);

    //@ts-ignore
    fontSize = isValidFontSizeUnit(undefined);
    expect(fontSize).toEqual(false);

    //@ts-ignore
    fontSize = isValidFontSizeUnit({});
    expect(fontSize).toEqual(false);
  });
});
