import { get, set, isEmpty } from 'lodash';
import moment from 'moment';

export function toMoment(dateString) {
  return moment(dateString || null, ["DD.MM.YY", "D/MM/YY", "DD/MM/YY", "DD.MM.YYYY", "D/MM/YYYY", "DD/MM/YYYY", "YYYY-MM-DD"], true);
}

type ValuesMap = { [key: string] : string | boolean | number };
type MappedValue = { target?: string, valueMapping: ValuesMap };
type MappingDateOptions = { target?: string, type: "date", outputFormat: string };
type Mapping = { [key: string]: string | MappedValue | MappingDateOptions };
type Extract = {
  propertyPath: string,
  values: Array<string>,
  mapping: Mapping,
}
type ExpandExtract = {
  propertyPath: string,
  expand: string,
  filter?: string,
  values: Array<string>,
  mapping?: Mapping,
}
type Extracts = Array<(Extract | ExpandExtract)>;
type ExtractResult = [ ValuesMap, string ];

export function defaultProps(propertyPath, entityType: string): Extracts {
  switch (entityType) {
    case 'ФЛ':
      return [
        {
          propertyPath,
          values: ["firstName", "lastName", "middleName", "birthday", "snils", "inn"],
          mapping: {
            firstName: "firstname",
            lastName: "lastname",
            middleName: "middlename",
            birthday: {
              target: "dateBirth",
              type: "date",
              outputFormat: "DD.MM.YYYY"
            },
            gender: {
              valueMapping: {
                "MALE": 1,
                "FEMALE": 2,
              }
            }
          }
        },
        {
          propertyPath,
          expand: "contactdata",
          filter: "contactType=Email",
          values: ["contact"],
          mapping: {
            "contact": "Email"
          }
        },
        {
          propertyPath,
          expand: "contactdata",
          filter: "contactType=Phone",
          values: ["contact"],
          mapping: {
            "contact": "Phone"
          },
        },
        {
          propertyPath,
          expand: "personaddress",
          filter: "addressType=RegistrationAddress",
          values: ["address"],
        },
        {
          propertyPath: `${propertyPath}.DocumentPersonal`,
          expand: "persondocument",
          filter: "documentType=PasportRF",
          values: ["series", "number", "issuer", "dateFrom", "departmentCode"],
          mapping: {
            "series": "docseries",
            "number": "docnumber",
            "issuer": "issueorg",
            "dateFrom": {
              target: "issuedate",
              type: "date",
              outputFormat: "DD.MM.YYYY",
            },
            "departmentCode": "issueidPassportRF"
          }
        }
      ];
    case 'ИП':
      return [
        {
          propertyPath,
          values: ["fullName", "ogrnip", "inn"],
          mapping: {
            fullName: "orgFullname",
            ogrnip: "orgOgrn",
            inn: "orgInn"
          }
        },
        {
          propertyPath,
          expand: "contactdata",
          filter: "contactType=Email",
          values: ["contact"],
          mapping: {
            "contact": "orgEmail"
          }
        },
        {
          propertyPath,
          expand: "contactdata",
          filter: "contactType=Phone",
          values: ["contact"],
          mapping: {
            "contact": "orgPhone"
          },
        },
        {
          propertyPath,
          expand: "personaddress",
          filter: "addressType=RegistrationAddress",
          values: ["address"],
        },
        {
          propertyPath: `${propertyPath}.DocumentPersonal`,
          expand: "persondocument",
          filter: "documentType=PasportRF",
          values: ["series", "number", "issuer", "dateFrom", "departmentCode"],
          mapping: {
            "series": "docseries",
            "number": "docnumber",
            "issuer": "issueorg",
            "dateFrom": {
              target: "issuedate",
              type: "date",
              outputFormat: "DD.MM.YYYY",
            },
            "departmentCode": "issueidPassportRF"
          }
        }
      ];
    case 'ЮЛ':
      return [
        {
          propertyPath,
          values: ["fullName", "ogrn", "inn"],
          mapping: {
            fullName: "orgFullname",
            ogrn: "orgOgrn",
            inn: "orgInn"
          }
        },
        {
          propertyPath,
          expand: "contactdata",
          filter: "contactType=Email",
          values: ["contact"],
          mapping: {
            "contact": "orgEmail"
          }
        },
        {
          propertyPath,
          expand: "contactdata",
          filter: "contactType=Phone",
          values: ["contact"],
          mapping: {
            "contact": "orgPhone"
          },
        },
        {
          propertyPath,
          expand: "personaddress",
          filter: "addressType=RegistrationAddress",
          values: ["address"],
        },
        {
          propertyPath: `${propertyPath}.DocumentPersonal`,
          expand: "persondocument",
          filter: "documentType=PasportRF",
          values: ["series", "number", "issuer", "dateFrom", "departmentCode"],
          mapping: {
            "series": "docseries",
            "number": "docnumber",
            "issuer": "issueorg",
            "dateFrom": {
              target: "issuedate",
              type: "date",
              outputFormat: "DD.MM.YYYY",
            },
            "departmentCode": "issueidPassportRF"
          }
        }
      ];
    default:
      return [
        {
          propertyPath,
          values: ["firstName", "lastName", "middleName", "birthday", "snils", "inn"],
          mapping: {
            firstName: "firstname",
            lastName: "lastname",
            middleName: "middlename",
            birthday: {
              target: "dateBirth",
              type: "date",
              outputFormat: "DD.MM.YYYY"
            },
            gender: {
              valueMapping: {
                "MALE": 1,
                "FEMALE": 2,
              }
            }
          }
        },
        {
          propertyPath,
          expand: "contactdata",
          filter: "contactType=Email",
          values: ["contact"],
          mapping: {
            "contact": "Email"
          }
        },
        {
          propertyPath,
          expand: "contactdata",
          filter: "contactType=Phone",
          values: ["contact"],
          mapping: {
            "contact": "Phone"
          },
        },
        {
          propertyPath,
          expand: "personaddress",
          filter: "addressType=RegistrationAddress",
          values: ["address"],
        },
        {
          propertyPath: `${propertyPath}.DocumentPersonal`,
          expand: "persondocument",
          filter: "documentType=PasportRF",
          values: ["series", "number", "issuer", "dateFrom", "departmentCode"],
          mapping: {
            "series": "docseries",
            "number": "docnumber",
            "issuer": "issueorg",
            "dateFrom": {
              target: "issuedate",
              type: "date",
              outputFormat: "DD.MM.YYYY",
            },
            "departmentCode": "issueidPassportRF"
          }
        }
      ];
  }
}

export function getExpands(extracts: Extracts): Array<string> {
  return Object.keys(extracts.reduce((acc, next) => {
    if (("expand" in next) && next.expand) {
      acc[next.expand] = true;
    }
    return acc;
  }, {}));
}

export function extractValues(selectedRow, extracts: Extracts) : Array<ExtractResult> {

  function sortByModified(data) {
    return data.sort((a, b) => a.modified - b.modified);
  }

  function getMostRelevant(data) {
    if (data && data.length > 0) {
      let relevant = sortByModified(data.filter(d => d.status === "RELEVANT"));
      let notConfirmed = sortByModified(data.filter(d => d.status === "NOT_CONFIRMED"));
      return relevant.pop() || notConfirmed.pop();
    }
    return null;
  }

  function selectRowFromExpand(source, expandName, filter) {
    let expandRows = get(source, expandName);
    if (expandRows && Array.isArray(expandRows) && expandRows.length > 0) {
      if (filter && typeof(filter) === "string") {
        const splitPair = filter.split('=');
        let propName = splitPair[0];
        let targetValue = splitPair[1];
        if (propName && targetValue) {
          expandRows = expandRows.filter(row => get(row, propName) == targetValue);
        }
      }
      return getMostRelevant(expandRows);
    }
    return null;
  }

  /**
   * сливает в один все повторяющиеся значения на определённый propertyPath
   * @param extractResults 
   */
  function mergeExtracts(extractResults: Array<ExtractResult>) : Array<ExtractResult> {
    const valuesMap : { [key : string] : ValuesMap } = extractResults.reduce((acc, [valuesMap, path]) => {
      const propertyPathSplit = path.split('.');
      const primaryPath: string = propertyPathSplit.shift()!;
      if (acc[primaryPath] === undefined) {
        acc[primaryPath] = {};
      }
      for (const [key, value] of Object.entries(valuesMap)) {
        set(acc[primaryPath], [...propertyPathSplit, key].join('.'), value);
      }
      return acc;
    }, {});
    return Object.entries(valuesMap).map(([key, valuesObj]) => [valuesObj, key]);
  }

  const extractResultSets: Array<ExtractResult> = [];

  extracts.forEach(extract => {
    const propertyPath: string = extract.propertyPath;
    const source = ("expand" in extract) ? selectRowFromExpand(selectedRow, extract.expand, extract.filter) : selectedRow;
    const values = extract.values;
    const fieldMapping = extract.mapping || {};
    const propertyAccumulator = {};
    if (source) {
      values.forEach(fieldName => {
        let value = get(source, fieldName, '');
        let targetPropertyName = fieldName;
        let targetMapping = fieldMapping[fieldName];
        let targetValueMapping = {};
        if (targetMapping) {
          if (typeof(targetMapping) === "string") {
            targetPropertyName = targetMapping;
          } else if ("type" in targetMapping && targetMapping.type == "date") {
            if (targetMapping.target != undefined) {
              targetPropertyName = targetMapping.target;
            }
            const dateValue = toMoment(value);
            value = dateValue.isValid() ? dateValue.format(targetMapping.outputFormat) : undefined;
          } else if ("valueMapping" in targetMapping) {
            if (targetMapping.target != undefined) {
              targetPropertyName = targetMapping.target;
            }
            targetValueMapping = targetMapping.valueMapping
          }
        }
        // Например если значения 'MALE' или 'FEMALE' транслируются в значения 1 или 2
        if (targetValueMapping[value]) {
          value = targetValueMapping[value];
        }
        if (value) { // Не обращаем внимание на null-ы, undefined-ы и пустые строки
          propertyAccumulator[targetPropertyName] = value;
        }
      });
    }
    extractResultSets.push([propertyAccumulator, propertyPath]);
  });

  return mergeExtracts(
    // отбросить записи в которых на самом деле нет значений
    extractResultSets.filter(([values]) => !isEmpty(values))
  );
}

  