import { get } from 'lodash';

export class WeightedSearch {
  static search<T>(
    term: string,
    fields: Array<string>,
    values: Array<T>,
    minWeight: number = 0
  ): Array<T> {
    var normalizedTerm: string = term.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
    var lowerCaseTerm: string = term.toLowerCase();
    var lowerCaseNormalizedTerm: string = normalizedTerm.toLowerCase();

    var weightedValues: Array<{ weight: number; value: T }> = [];

    for (let i = 0; i < values.length; i++) {
      const value = values[i];
      var weight: number = 0;

      //weighting score rules
      //+1: starts with normalized form
      //+1: contains normalized form
      //+1: whole word match with normalized form
      //+1: case insensitive starts with
      //+1: case insensitive contains
      //+1: case insensitive whole word match
      //+1: starts with original form
      //+1: contains original form
      //+1: whole word match with original form
      //+1: case sensitive starts with
      //+1: case sensitive contains
      //+1: case sensitive whole word match

      //+1: normalize value.field starts with
      //+1: normalize value.field contains
      //+1: normalize value.field whole word match
      //+1: normalize value.field lowercase starts with
      //+1: normalize value.field lowercase contains
      //+1: normalize value.field lowercase whole word match

      //+1: all normalized characters can be found in normalized value
      //+1: all normalized characters can be found in original value
      //+1: all characters can be found in normalized value
      //+1: all characters can be found in original value

      for (var field of fields) {
        var fieldValue = get(value, field);

        if (fieldValue === undefined || fieldValue === null) {
          continue;
        }

        var normalizedValue: string = fieldValue
          .toString()
          .normalize('NFD')
          .replace(/[\u0300-\u036f]/g, '');

        if (fieldValue.toString().startsWith(normalizedTerm)) weight++;
        if (fieldValue.toString().includes(normalizedTerm)) weight++;
        if (fieldValue.toString() == normalizedTerm) weight++;

        if (fieldValue.toString().toLowerCase().startsWith(lowerCaseNormalizedTerm)) weight++;
        if (fieldValue.toString().toLowerCase().includes(lowerCaseNormalizedTerm)) weight++;
        if (fieldValue.toString().toLowerCase() == lowerCaseNormalizedTerm) weight++;

        if (fieldValue.toString().toLowerCase().startsWith(lowerCaseTerm)) weight++;
        if (fieldValue.toString().toLowerCase().includes(lowerCaseTerm)) weight++;
        if (fieldValue.toString().toLowerCase() == lowerCaseTerm) weight++;

        if (fieldValue.toString().startsWith(term)) weight++;
        if (fieldValue.toString().includes(term)) weight++;
        if (fieldValue.toString() == term) weight++;

        if (normalizedValue.toLowerCase().startsWith(term)) weight++;
        if (normalizedValue.toLowerCase().includes(term)) weight++;
        if (normalizedValue.toLowerCase() == term) weight++;

        if (normalizedValue.startsWith(term)) weight++;
        if (normalizedValue.includes(term)) weight++;
        if (normalizedValue == term) weight++;

        if (normalizedValue.toLowerCase().startsWith(normalizedTerm)) weight++;
        if (normalizedValue.toLowerCase().includes(normalizedTerm)) weight++;
        if (normalizedValue.toLowerCase() == normalizedTerm) weight++;

        if (normalizedValue.startsWith(normalizedTerm)) weight++;
        if (normalizedValue.includes(normalizedTerm)) weight++;
        if (normalizedValue == normalizedTerm) weight++;

        // if(Array.from(normalizedTerm).every(c => normalizedValue.includes(c))) weight++;
        // if(Array.from(normalizedTerm).every(c => fieldValue.includes(c))) weight++;
        // if(Array.from(term).every(c => normalizedValue.includes(c))) weight++;
        if (Array.from(term).every((c) => fieldValue.includes(c))) weight++;
      }

      if (weight > minWeight) weightedValues.push({ weight, value });
    }

    weightedValues.sort((a, b) => b.weight - a.weight);

    return weightedValues.map((wv) => wv.value);
  }
}
