import { IOption, isObject } from '@websolutespa/bom-core';

function escapeRegexChars(text: string) {
  return text.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
}

function safeToString(text: string) {
  return text === undefined || text === null ? '' : text.toString().trim();
}

function encodeHTML(text: string) {
  return safeToString(text)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
}

export function highligthText(text: string, query: string | string[] | undefined): string {
  if (!query) {
    return text;
  }
  if (!Array.isArray(query)) {
    query = query.split(' ');
  }
  text = encodeHTML(text);
  const escapedQuery = query.map(x => escapeRegexChars(x));
  const regExp = new RegExp('&[^;]+;|' + escapedQuery.join('|'), 'gi');
  // console.log(regExp, query);
  return text.replace(regExp, function (match, i, b) {
    return '<strong>' + match + '</strong>';
    // return match.toLowerCase() === x.toLowerCase() ? '<strong>' + match + '</strong>' : match;
  });
}

export function isEmptyValue(value: string | string[] | undefined) {
  if (Array.isArray(value)) {
    return value.length === 0;
  }
  return Boolean(value) === false;
}

export function outerToInnerSelectValue(
  value: string | IOption | string[] | IOption[] | null | undefined
): string | string[] | undefined {
  if (Array.isArray(value)) {
    return value.map(x => outerToInnerSelectValue(x)) as string[];
  }
  if (isObject(value)) {
    return String(value.id);
  }
  if (value == null) {
    return undefined;
  }
  return value;
}

export function innerToOuterSelectValue(
  value: string | string[] | undefined,
  options?: IOption[]
): string | IOption | string[] | IOption[] | null {
  if (Array.isArray(value)) {
    return value.map(x => innerToOuterSelectValue(x, options)) as (IOption[] | string[]);
  }
  if (value == null) {
    return null;
  }
  if (Array.isArray(options)) {
    return options.find(x => String(x.id) === value) || null;
  }
  return value;
}

export function outerToInnerValue(
  value: string | IOption | string[] | IOption[] | null | undefined
): string | string[] | undefined {
  if (Array.isArray(value)) {
    return value.map(x => outerToInnerValue(x)) as string[];
  }
  if (isObject(value)) {
    return value.name;
  }
  if (value == null) {
    return undefined;
  }
  return value;
}

export function innerToOuterValue(
  value: string | string[] | undefined,
  options?: IOption[]
): string | IOption | string[] | IOption[] | null {
  if (Array.isArray(value)) {
    return value.map(x => innerToOuterValue(x, options)) as (IOption[] | string[]);
  }
  if (value == null) {
    return null;
  }
  if (Array.isArray(options)) {
    return options.find(x => x.name === value) || null;
  }
  return value;
}

export function outerToInnerSingleValue(
  value: string | IOption | null | undefined
): string | undefined {
  if (isObject(value)) {
    return value.name;
  }
  if (value == null) {
    return undefined;
  }
  return value;
}

export function innerToOuterSingleValue(
  value: string | undefined,
  options?: IOption[]
): string | IOption | null {
  if (value == null) {
    return null;
  }
  if (Array.isArray(options)) {
    return options.find(x => x.name === value) || null;
  }
  return value;
}

export type IMatch = { index: number, option: IOption };

export function filterOptions(options: IOption[], value?: string, limit: number = 0) {
  if (!value) {
    return [...options];
    /*
    if (limit > 0) {
      return options.slice(0, limit);
    } else {
      return [...options];
    }
    */
  }
  const lowerValue = value.toLowerCase();
  const matches = options.reduce<IMatch[]>((p, x) => {
    const index = x.name.toLowerCase().indexOf(lowerValue);
    if (index !== -1) {
      p.push({ index, option: x });
    }
    return p;
  }, []);
  matches.sort((a, b) => {
    const order = a.index - b.index;
    if (order === 0) {
      return a.option.name.localeCompare(b.option.name);
    }
    return order;
  });
  const filteredOptions = matches.map(x => x.option);
  if (limit > 0) {
    return filteredOptions.slice(0, limit);
  }
  return filteredOptions;
}
