const Diff = require('deep-diff');
const { get, set, find } = require('lodash');

const BARCODE_PATTERN = require('../constant/chemical/barcode-pattern.constant');
const HAZARD_PICTOGRAMS = require('../constant/chemical/hcode-to-pictogram.constant');
const TENANTS = require('../constant/core/tenants.constant');

module.exports.validateError = (validate) => {
  const errorMessages = validate.errors.map((err) => `${err.message}, ${err.schemaPath}`).join('\n');
  return module.exports.error({ message: `failed to validate model ${errorMessages}` });
};

module.exports.error = ({ message, status = 400 }) => Object.assign(new Error(message), { status });

module.exports.pictograms = (ghs) =>
  Array.from(
    ghs.reduce((acc, curr) => {
      if (curr && curr.hazardCode) {
        (HAZARD_PICTOGRAMS[curr.hazardCode] || []).forEach((pictogram) => acc.add(pictogram));
      }
      return acc;
    }, new Set()),
  ).sort();

module.exports.campusCodeToTenantCode = (campusCode) => {
  const result = TENANTS.find((tenant) => tenant.campusList.some((item) => item.campusCode === campusCode));
  return result ? result.tenantCode : null;
};

module.exports.getTenant = (tenantCode) => {
  if (!tenantCode) return null;
  return find(TENANTS, { tenantCode });
};

module.exports.getCampus = (campusCode) => {
  if (!campusCode) return null;
  const tenantCode = module.exports.campusCodeToTenantCode(campusCode);
  if (tenantCode) {
    const tenant = module.exports.getTenant(tenantCode);
    if (tenant) return find(tenant.campusList, { campusCode });
  }

  return null;
};

module.exports.toTitleCase = (item) => {
  const withSpaces = (item || '').replace(/_/g, ' ');
  return withSpaces.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
};

module.exports.getBarcodePattern = ({ tenantCode, campusCode }) => {
  let match = BARCODE_PATTERN.DEFAULT;
  if (tenantCode && BARCODE_PATTERN[tenantCode] && campusCode && BARCODE_PATTERN[tenantCode][campusCode]) {
    match = BARCODE_PATTERN[tenantCode][campusCode];
  }
  return match;
};

module.exports.getBarcodeFormats = (tenant, campus) => {
  const barcode = BARCODE_PATTERN.formats[tenant] ? BARCODE_PATTERN.formats[tenant][campus] : null;
  if (!barcode) {
    return BARCODE_PATTERN.formats.DEFAULT;
  }
  return barcode;
};

module.exports.getDiff = (original, latest) => {
  const diff = {};
  Diff.observableDiff(original, latest, (change) => {
    /* Apply all changes for change.kind of:
      - N: indicates a newly added property/element
      - D: indicates a property/element was deleted
      - E: indicates a property/element was edited
    */
    if (['N', 'E', 'A'].includes(change.kind)) {
      const path = change.path.join('.');
      set(diff, path, get(latest, path));
    }
  });
  return diff;
};

module.exports.validateLabel = (obj) => !Object.values(obj).some((v) => v === Object(v));

// @DEPREICATED
// use date helper instead
module.exports.validateDate = (date) => {
  const year = new Date(date).getFullYear();
  return year && year > 999 && year < 3000;
};

module.exports.notFoundError = () => Object.assign(new Error('Not Found'));

module.exports.invalidFormatError = () => Object.assign(new Error('Invalid Format'));

module.exports.setErrorCode = (err) => {
  const error = Object.assign(err);
  if (/(\w*Not Found\w*)/gi.test(error.message)) {
    error.status = 404;
  } else if (/(\w*Invalid\w*)/gi.test(error.message)) {
    error.status = 400;
  }
  return error;
};

module.exports.convertNormalizedAmount = (quantity, conversion) => {
  const literToGallon = 0.264172;
  const literToCubicFeet = 0.0353147;
  const gramToPound = 0.00220462;

  switch (conversion) {
    case 'gramToPound':
      return quantity * gramToPound;
    case 'literToCubicFeet':
      return quantity * literToCubicFeet;
    case 'literToGallon':
      return quantity * literToGallon;
    default:
  }
  return quantity;
};
