import { values } from '../../utils/obj/values';
import { isObject } from '../../utils/obj/isObject';
import { isEmpty } from '../../utils/obj/isEmpty';
import { Palette } from '../Palette';
import { Token } from '../Token';
import { ensureAllLowerCase } from './string.utils';
import ColorUtils from './color.utils';
import { ThemeTokens } from '../../styles/defaults/themes.interface';
import { ColorType } from './color.interface';
import { COLOR, StyleTokenReference, TOKEN } from '../style.interface';
import { TOKEN_KEY_SEPARATOR } from '../token.interface';
import { ThemeModeType } from '../palette.interface';

/**
 * Convert Tokens [Record<string, Token>] into object
 * @param value Record<string, Token> Tokens to be transformed
 * @returns object
 */
export const toTokensObject = (value: Record<string, Token>): object => {
  // TODO: update from any to ThemeTokens
  const obj: any = {};

  values(value).forEach((token) => {
    if (!obj[token.type]) {
      obj[token.type] = {};
    }
    if (!obj[token.type][token.key]) {
      obj[token.type][token.key] = token.value;
    }
  });

  return obj;
};

/**
 * Convert Tokens [Record<string, Token>] into Themes Things Tokens
 * @param value Record<string, Token> Tokens to be transformed
 * @returns object
 */
export const toApphouseTokens = (value: Record<string, Token>): ThemeTokens => {
  // TODO: update from any to ThemeTokens
  const obj: any = toTokensObject(value);

  return obj as ThemeTokens;
};

const toUpperCaseifHex = (value: any) => {
  if (typeof value === 'string') {
    if (value.startsWith('#')) {
      return value.toLocaleUpperCase();
    }
  }
  return value;
};
/**
 * Find the first match for this value in the lookup table
 * @param value string value to be matched
 * @param lookup lookup table
 * @param namespace string if namespace is provided it will help
 * finding a value when there are multiple keys with the same value
 * in the lookup table
 * @returns
 */
export const getValueReferenceStringFromObject = (
  value: string | number,
  lookup: { [value: string]: string },
  namespace?: string
) => {
  let v = toUpperCaseifHex(value);
  const hasLookupValues = !isEmpty(lookup);
  if (!hasLookupValues) {
    return v;
  }
  if (hasLookupValues) {
    let found = v;
    Object.keys(lookup).forEach((key) => {
      if (namespace) {
        if (key.includes(namespace)) {
          if (toUpperCaseifHex(lookup[key]) === v) {
            found = key;
            return;
          }
        }
      }

      if (toUpperCaseifHex(lookup[key]) === v) {
        found = key;
        return;
      }
    });

    return found;
  }
};
/**
 * The reference here is key colors.onBase
 * @param obj object, usually referring to styles or tokens
 */
export const getReferenceTokenFromObject = (
  obj: object,
  path?: string
): StyleTokenReference | undefined => {
  const keys = Object.keys(obj);
  if (keys.length > 1) {
    console.warn('only one key can be referenced at a time');
    return undefined;
  }
  if (keys.length === 1) {
    const key = keys[0];
    const value = Object.values(obj)[0];
    const k = path ? `${path}.${key}` : key;

    const reference: StyleTokenReference | undefined = {
      type: getStyleTokenReferenceType(key),
      value: value,
      key: k
    };
    return reference;
  }
  console.warn('no key was found in this object');
  return undefined;
};

/**
 * Discover is a key has the word 'color' in it
 * @param key string
 * @returns if the key contains 'color' it will return true, otherwise it will be false
 */
export const hasColorReference = (key: string): boolean => {
  return (
    `${key}`.toLocaleLowerCase().indexOf('color') >= 0 ||
    `${key}`.toLocaleLowerCase().indexOf('theme') >= 0
  );
};

/**
 *
 * @param key string
 * @returns if the key contains 'color' it will return PALETTE, otherwise it will be TOKEN
 */
export const getStyleTokenReferenceType = (key: string): 'color' | 'token' => {
  return hasColorReference(key) ? COLOR : TOKEN;
};

/**
 * Create lookup table for value reference
 * It only keeps unique objects
 * @param obj
 * @param root
 * @param lookup
 * @returns
 */
export const getLookupTable = (
  obj: {
    [id: string]: any;
  },
  lookup: { [id: string]: string } = {},
  root?: string
): { [id: string]: string } => {
  const lookupCopy = lookup;
  if (!obj) {
    return {};
  }

  Object.keys(obj).map((key) => {
    const value = obj[key];
    const id = root ? `${root}.${key}` : key;

    // only create lookup values for unique values
    if (!isObject(value)) {
      if (!lookupCopy[id]) {
        lookupCopy[id] = value;
      } else {
        delete lookupCopy[id];
      }
    } else {
      return getLookupTable(value, lookupCopy, id);
    }
  });
  return lookupCopy;
};

export const getTokenListId = (token: any) => {
  const type = token?.type;
  const key = token?.key;
  if (type && key && typeof key === 'string' && typeof type === 'string') {
    const nKey = ensureAllLowerCase(key).trim();
    const ntype = type.trim();
    return `${ntype}${TOKEN_KEY_SEPARATOR}${nKey}`.trim();
  }
  console.warn('token is not valid when trying to get token list id');
  return 'unknownTokenId';
};

export const getColorTokensLookupReferenceFromPalettes = (
  palette: Record<string, Palette>,
  basePaletteId: string,
  colorPaletteId?: string | undefined
): { [id: string]: string } => {
  let baseColorsLookup;
  if (palette[basePaletteId]) {
    const baseColors = palette[basePaletteId].objectify.colors;
    baseColorsLookup = getLookupTableFromColorType(
      baseColors || [],
      palette[basePaletteId].mode
    );
  }

  if (!colorPaletteId && baseColorsLookup) {
    return baseColorsLookup;
  }

  if (colorPaletteId) {
    if (palette[colorPaletteId] && palette[colorPaletteId].mode === 'theme') {
      const colors = palette[colorPaletteId].objectify.colors;
      const colorsLookup = getLookupTableFromColorType(
        colors || [],
        palette[colorPaletteId].mode,
        colorPaletteId
      );
      return colorsLookup;
    }
  }
  return {};
};

export const getLookupTableFromColorType = (
  colors: ColorType[],
  mode: ThemeModeType,
  paletteId?: string
) => {
  const lookup: { [id: string]: string } = {};
  colors.forEach((color) => {
    if (!color) {
      return;
    }
    const id = paletteId
      ? `${mode}.${paletteId}.${color.id}`
      : `${mode}.${color.id}`;
    const hex = color.color.hex;
    if (!lookup[id]) {
      lookup[id] = hex;
    }

    const rgbaString = ColorUtils.toRgbaStringFromRgbaObject(color.color.rgb);
    if (rgbaString && !lookup[rgbaString]) {
      lookup[id] = rgbaString;
    }
  });

  return lookup;
};
export interface hashedObjectReturnType {
  lookup: { [id: string]: any };
  hashedObject: { [id: string]: any };
}

// Letter spacing
export const toRemsLetterSpacing = (value: string | number): string => {
  return `${value}rem`;
};

export const toEmLetterSpacing = () => {
  return 'em';
};

// eslint-disable-next-line @typescript-eslint/no-empty-function
export const toPtLettersSpacing = () => {};
