import get from 'lodash/get';
import isBuffer from 'lodash/isBuffer';
import isObject from 'lodash/isObject';
import isString from 'lodash/isString';
import reduce from 'lodash/reduce';
import set from 'lodash/set';

function mapField(
  inObject: any,
  outObject: any,
  inKey: string,
  outKey: string
) {
  const value = get(inObject, inKey);

  if (typeof value !== 'undefined') {
    set(outObject, outKey, value);
  }

  return outObject;
}

function mapFields(result: any, fieldMap: any) {
  if (Array.isArray(fieldMap)) {
    return fieldMap.reduce(
      (o, key) => mapField(result, o, key, key),
      Object.create(null)
    );
  } else if (isObject(fieldMap)) {
    return reduce(
      fieldMap,
      (o, inKey, outKey) => mapField(result, o, inKey, outKey),
      Object.create(null)
    );
  } else if (isString(fieldMap)) {
    return get(result, fieldMap);
  }

  return result;
}

/**
 * бёрёт колонки, выбраные значения, и мапит в новый объект
 * @param columns [{
 *   name: 'foo',
 *   field: 'bar.buz',
 *   fieldMap: ['id', 'name']
 * }]
 * @param results {
 *   foo: {
 *     id: 1,
 *     name: 'some',
 *     comment: '123
 *   }
 * }
 * @returns {
 *   bar: {
 *     buz: {
 *        id: 1,
 *        name: 'some',
 *     }
 *   }
 * }
 */
export function mapOptionsResults(columns: any[], results: any) {
  return columns.reduce((o, { name, field, fieldMap }) => {
    let result = results[name];

    if (result !== null) {
      if (fieldMap) {
        if (Array.isArray(result)) {
          result = result.map((e) => mapFields(e, fieldMap));
        } else if (isObject(result)) {
          result = mapFields(result, fieldMap);
        }
      }
      set(o, field || name, result);
    }

    return o;
  }, Object.create(null));
}

export function flatten(target: any) {
  const delimiter = '.';
  const output = {} as any;

  function step(object: any, prev?: any, currentDepth?: any) {
    currentDepth = currentDepth || 1;
    Object.keys(object).forEach(function (key) {
      const value = object[key];
      const isarray = Array.isArray(value);
      const type = Object.prototype.toString.call(value);
      const isbuffer = isBuffer(value);
      const isobject = type === '[object Object]' || type === '[object Array]';

      const newKey = prev ? prev + delimiter + key : key;

      if (!isarray && !isbuffer && isobject && Object.keys(value).length) {
        return step(value, newKey, currentDepth + 1);
      }

      output[newKey] = value;
    });
  }

  step(target);

  return output;
}

export function getNameStrings(
  firstname?: string,
  middlename?: string,
  surname?: string
) {
  const names = [surname, firstname, middlename].filter(Boolean) as string[];
  let fullname;
  let shortname;

  if (names.length) {
    fullname = names.join(' ');

    for (let i = names.length; --i > 0; ) {
      names[i] = `${names[i][0]}.`;
    }

    shortname = names.join(' ');
  } else {
    fullname = shortname = 'Неизвестный';
  }

  return {
    fullname,
    shortname,
  };
}
