/** @file
 * @author Edouard DUPIN
 * @copyright 2018, Edouard DUPIN, all right reserved
 * @license PROPRIETARY (see license file)
 */
import {
  isArray,
  isArrayOf,
  isNullOrUndefined,
  isString,
} from '@/utils/validator';

export enum TypeCheck {
  CONTAINS = 'C',
  EQUAL = '==',
  NOT_EQUAL = '!=',
  LESS = '<',
  LESS_EQUAL = '<=',
  GREATER = '>',
  GREATER_EQUAL = '>=',
  STARTS_WITH = 'START',
  STARTS_NOT_WITH = '!START',
}

export interface SelectModel {
  check: TypeCheck;
  key: string;
  value:
    | null
    | undefined
    | bigint
    | number
    | string
    | boolean
    | bigint[]
    | number[]
    | string[]
    | (bigint | number | string | boolean | undefined | null)[];
}
/*
{ check: TypeCheck.EQUAL, key: sss, value: null}
*/

/**
 * @brief Generic interface to access to the BDD (no BDD, direct file IO)
 */
export namespace DataTools {
  export function extractLimitOne<TYPE>(data: TYPE[], key: string): any[] {
    const out: TYPE[] = [];
    for (const item of data) {
      const value = item[key];
      if (isArray(value)) {
        for (const element of value) {
          if (DataTools.existIn(element, out) === false) {
            out.push(element);
          }
        }
      } else {
        if (!DataTools.existIn(value, out) && !isNullOrUndefined(value)) {
          out.push(value);
        }
      }
    }
    return out;
  }
  // Deprecated
  export function extractLimitOneList<TYPE>(data: TYPE[], key: string): any[] {
    return extractLimitOne(data, key);
  }

  export function getTableIndex<TYPE>(
    bdd: TYPE[],
    id: any,
    fieldName: string = 'id'
  ) {
    for (let iii = 0; iii < bdd.length; iii++) {
      if (bdd[iii][fieldName] === id) {
        return iii;
      }
    }
    return undefined;
  }

  export function getsWhere<TYPE>(
    bdd: TYPE[],
    select: SelectModel[],
    orderByData?: string[]
  ): TYPE[] {
    // console.log("[I] gets_where " + this.name + " select " + _select);
    let tmpList = getSubList(bdd, select);
    if (tmpList && orderByData) {
      tmpList = orderBy(tmpList, orderByData);
    }
    return tmpList;
  }

  export function get<TYPE>(
    bdd: TYPE[],
    id: any,
    fieldName: string = 'id'
  ): TYPE | undefined {
    // console.log("[I] get " + this.name + " " + _id);
    for (const item of bdd) {
      if (item[fieldName] === id) {
        return item;
      }
    }
    console.log(`[W] not found elements length=${bdd.length}`);
    return undefined;
  }

  export function getNameLikeAll<TYPE>(
    bdd: TYPE[],
    name: string
  ): TYPE[] | undefined {
    let out: TYPE[] = [];
    let nameLower = name.toLowerCase();
    for (const item of bdd) {
      if (item['name'].toLowerCase() === nameLower) {
        out.push(item);
      }
    }
    if (out.length === 0) {
      return undefined;
    }
    return out;
  }
  export function getNameLike<TYPE>(bdd: TYPE[], name: string): TYPE[] {
    let out: TYPE[] = [];
    let nameLower = name.toLowerCase();
    for (const item of bdd) {
      // console.log("compare '" + _name + "'       ???   '" + v['name'] + "'");
      if (item['name'].toLowerCase().includes(nameLower)) {
        out.push(item);
      }
    }
    return out;
  }

  export function find<TYPE>(bdd: TYPE[], listToken: any, values: any): TYPE[] {
    let out: TYPE[] = [];
    for (const item of bdd) {
      let find = true;
      for (const tokenItem of listToken) {
        if (item[tokenItem] !== values[tokenItem]) {
          find = false;
          break;
        }
      }
      if (find === true) {
        out.push(item);
      }
    }
    return out;
  }

  export function count<TYPE>(bdd: TYPE[], select?: SelectModel[]) {
    if (isNullOrUndefined(select)) {
      return bdd.length;
    }
    let tmp = getSubList(bdd, select);
    if (isNullOrUndefined(tmp)) {
      return 0;
    }
    return tmp.length;
  }
  export function startsWith<TYPE>(
    value: TYPE,
    listValues: TYPE | TYPE[]
  ): boolean {
    if (!isString(value)) {
      return false;
    }
    if (isArrayOf(listValues, isString)) {
      for (const item of listValues) {
        if (value.startsWith(item)) {
          return true;
        }
      }
      return false;
    }
    if (isString(listValues)) {
      return value.startsWith(listValues);
    }
    return false;
  }
  export function existIn<TYPE>(
    value: TYPE,
    listValues: TYPE | TYPE[]
  ): boolean {
    if (isArray(listValues)) {
      for (const item of listValues) {
        if (value === item) {
          return true;
        }
      }
      return false;
    }
    return value === listValues;
  }
  export function containsOneIn<TYPE>(
    value: TYPE | TYPE[],
    listValues: TYPE | TYPE[]
  ): boolean {
    if (isArray(value)) {
      for (const item of value) {
        if (existIn(item, listValues) === true) {
          return true;
        }
      }
      return false;
    }
    return existIn(value, listValues);
  }

  export function getSubList<TYPE>(
    values: TYPE[],
    select?: SelectModel[]
  ): TYPE[] {
    let out = [] as TYPE[];
    for (let iiiElem = 0; iiiElem < values.length; iiiElem++) {
      let find = true;
      if (isNullOrUndefined(select) || select.length === 0) {
        find = false;
      } else {
        //console.log("Check : " + JSON.stringify(values[iiiElem], null, 2));
        for (let iiiSelect = 0; iiiSelect < select.length; iiiSelect++) {
          let control = select[iiiSelect];
          let valueElement = values[iiiElem][control.key];
          //console.log("    valueElement : " + JSON.stringify(valueElement, null, 2));
          if (isArray(control.value)) {
            //console.log(`check ${control.check} ... ${valueElement} in ${control.value}`);
            if (control.check === TypeCheck.CONTAINS) {
              if (
                DataTools.containsOneIn(valueElement, control.value) === false
              ) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.EQUAL) {
              //console.log("check Equals ... " + valueElement + " === " + control.value);
              if (DataTools.existIn(valueElement, control.value) === false) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.NOT_EQUAL) {
              if (DataTools.existIn(valueElement, control.value) === false) {
                find = true;
                break;
              }
            } else if (control.check === TypeCheck.STARTS_WITH) {
              if (DataTools.startsWith(valueElement, control.value) === false) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.STARTS_NOT_WITH) {
              if (DataTools.startsWith(valueElement, control.value) === true) {
                find = false;
                break;
              }
            } else {
              console.log(
                '[ERROR] Internal Server Error{ unknown comparing type ...'
              );
              return [];
            }
          } else {
            //console.log("    [" + control.key + "] = " + valueElement);
            if (control.check === TypeCheck.CONTAINS) {
              //console.log("check Equals ... " + valueElement + " === " + control.value + "   #2");
              if (
                DataTools.containsOneIn(valueElement, control.value) === false
              ) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.EQUAL) {
              if (valueElement !== control.value) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.NOT_EQUAL) {
              if (valueElement === control.value) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.LESS) {
              if (control && control.value && valueElement >= control.value) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.LESS_EQUAL) {
              if (control && control.value && valueElement > control.value) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.GREATER) {
              if (control && control.value && valueElement <= control.value) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.GREATER_EQUAL) {
              if (control && control.value && valueElement < control.value) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.STARTS_WITH) {
              console.log(
                `AA start with : ${valueElement} with ${control.value}`
              );
              if (DataTools.startsWith(valueElement, control.value) === false) {
                find = false;
                break;
              }
            } else if (control.check === TypeCheck.STARTS_NOT_WITH) {
              if (DataTools.startsWith(valueElement, control.value) === true) {
                find = false;
                break;
              }
            } else {
              console.log(
                '[ERROR] Internal Server Error{ unknown comparing type ...'
              );
              return [];
            }
          }
        }
      }
      if (find === true) {
        // console.log("            ==> SELECTED");
        out.push(values[iiiElem]);
      } else {
        // console.log("            ==> NOT SELECTED");
      }
    }
    return out;
  }

  export function orderBy<TYPE>(values: TYPE[], order: string[]): TYPE[] {
    if (isNullOrUndefined(order)) {
      return values;
    }
    if (order.length === 0) {
      return values;
    }
    let valueOrder = order[0];
    let out: TYPE[] = [];
    let outUnordered: TYPE[] = [];
    for (let iii = 0; iii < values.length; iii++) {
      if (values[iii][valueOrder] === undefined) {
        outUnordered.push(values[iii]);
        continue;
      }
      if (values[iii][valueOrder] === null) {
        outUnordered.push(values[iii]);
        continue;
      }
      out.push(values[iii]);
    }
    // console.log("order in list by : " + value_order);
    // out = sorted(out, key=lambda x{ x[value_order])
    if (valueOrder === 'name') {
      out.sort((aaa, bbb) => {
        const name1 = aaa[valueOrder].toLowerCase();
        const name2 = bbb[valueOrder].toLowerCase();
        if (name1 > name2) {
          return 1;
        }
        if (name1 < name2) {
          return -1;
        }
        return 0;
      });
    } else {
      out.sort((aaa, bbb) => {
        if (aaa[valueOrder] > bbb[valueOrder]) {
          return 1;
        }
        if (aaa[valueOrder] < bbb[valueOrder]) {
          return -1;
        }
        return 0;
      });
    }
    if (order.length > 1) {
      outUnordered = orderBy(outUnordered, order.slice(1));
    }
    for (let jjj = 0; jjj < outUnordered.length; jjj++) {
      out.push(outUnordered[jjj]);
    }
    return out;
  }
}
