import { DateTime } from 'luxon';
import { parseHours, formatDate } from './formatters';

const URL_REGEXP = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=]+$/gm;
const LINK_URL_REGEXP = /(http[s]?:\/\/|www\.)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,5}[.]{0,1}/gi;
const HTML_MARKUP_REGEXP = /<\s*[a-z]+[^>]*>/gi;
const LINKED_IN_REGEXP = /(http(s)?:\/\/)?([\w]+\.)?linkedin\.com\/(pub|in|profile)?[^\s]*/gi;
const SPECIAL_CHARS_REGEXP = /^[\w_\-+~,/\\:'"().&*|[\]?# ]+$/i;
const GREETINGS_REGEXP = /\b(dr|mr|mister|mrs|ms|miss|sir|hello|hi)\b/i;
const PHONE_REGEXP = /(\+?\(?\+?[0-9]{1,3}\)?[-. ]+([0-9]{2,4})[-. ]?([0-9]{3,5}))|\+?[0-9]{7,}/gi;
const INTEGER_REGEXP = /^-?\d{0,11}$/;
const DOUBLE_REGEXP = /^-?\d{0,11}(\.\d{0,8}){0,1}$/;
const EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
const EMAIL_LIST_REGEXP = /^(\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]{2,4}\s*?,?\s*?)+$/g;
const HEX_REGEXP = /[0-9A-Fa-f]{6}/;
const PASSWORD_REGEX = /^(?=.*\p{Lu})(?=.*\p{Ll})(?=.*\d)(?=.*[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]).{8,}$/u;

const STANDART_DATE_FORMAT = 'yyyy-MM-dd';

export const MEDIUM_TEXT_LENGTH = 16777215;

function isEmpty(v) {
  return (typeof v === 'string' && v.trim() === '') || typeof v === 'undefined' || v === null;
}

export function differentValidation (value, message) {
  return (v, $t = (s) => s) => !v || !(v === value) || (message || $t('components.theValueMustBeDifferent'));
}

export function mediumTextValidation (message) {
  return (v, $t = (s) => s) => !v || ((v || '').toString().trim().length <= MEDIUM_TEXT_LENGTH) || (message || $t('components.mediumTextLength'));
}

export function lengthValidation (length, message) {
  return (v, $t = (s) => s) => !v || ((v || '').toString().trim().length <= length) || (message || $t('components.mustBeLessThanLengthCharacters', {length}));
}

export function minLengthValidation (length, message) {
  return (v, $t = (s) => s) => !v || ((v || '').toString().trim().length >= length) || (message || $t('components.mustBeMoreThanLengthCharacters', {length}));
}

export function emailValidation (message) {
  return (v, $t = (s) => s) => !v || !!v.toString().match(EMAIL_REGEXP) || (message || $t('components.pleaseEnterAValidEmail'));
}

export function emailListValidation (message) {
  return (v, $t = (s) => s) => !v || !!v.toString().match(EMAIL_LIST_REGEXP) || (message || $t('components.pleaseEnterValidCommaSeparatedEmailAddressesToSendInvoicing'));
}

export function urlValidation (message) {
  return (v, $t = (s) => s) => !v || !!v.toString().match(URL_REGEXP) || (message || $t('components.pleaseEnterValidURL'));
}

export function hexValidation (message) {
  return (v, $t = (s) => s) => !v || !!v.toString().match(HEX_REGEXP) || (message || $t('components.pleaseEnteraValidHex'));
}

export function passMatchValidation (password, message) {
  return (v, $t = (s) => s) => !v || !!v.toString().match(`^${password}$`) || (message || $t('components.passwordsDontMatch'));
}

export function emptyValidation (message) {
  return (v, $t = (s) => s) => !isEmpty(v) || (message || $t('components.thisFieldIsRequired'));
}

export function emptyArrayValidation (message) {
  return (v, $t = (s) => s) => (Array.isArray(v) && v.length > 0) || (message || $t('components.thisFieldIsRequired'));
}

export function integerValidation (message) {
  return (v, $t = (s) => s) => !v || !!(v + '').match(INTEGER_REGEXP) || (message || $t('components.thisFieldMustBeANumberFormatZero'));
}

export function doubleValidation (message) {
  return (v, $t = (s) => s) => !v || !!(v + '').match(DOUBLE_REGEXP) || (message || $t('components.thisFieldMustBeANumberFormatZero'));
}

export function minMaxValidation (min = 0, max = 100, message) {
  return (v, $t = (s) => s) => !v || (v >= min && v <= max) || (message || $t('components.theValueMustBeGreaterThanOrEqualToMinAndMax', {min, max}));
}

export function minValidation (min = 0, message) {
  return (v, $t = (s) => s) => (!isEmpty(v) && (Number(v) >= min)) || (message || $t('components.theValueMustBeGreaterThanOrEqualToMin', {min}));
}

export function maxValidation (max, message) {
  return (v, $t = (s) => s) => (!isEmpty(v) && (Number(v) <= max)) || (message || $t('components.mustBeLessThanMax'));
}

export function preventStuffingValidation (message) {
  return (v, $t = (s) => s) => !v || (v.replace(/(.{4,}?)(?:\1{3,})/g, (text, subtext) => /^(\s*(<(\/?[^>]+)>|&nbsp;|[^\s]{0,3})\s*|(.)\4+)$/.test(subtext) ? text : '') === v) || (message || $t('components.thisIsntABalidDescription'));
}

export function preventUrlsValidation (message) {
  return (v, $t = (s) => s) => !v || !v.match(LINK_URL_REGEXP) || (message || $t('components.linksAreNotAllowedInThisField'));
}

export function financeAccountCodeValidation (message) {
  return (v, $t = (s) => s) => !v || v.match(/^\d+(\.\d+)*$/g) || (message || $t('components.theValueMustBeginAndEndWithANumberAndContainOnlyNumbersOrDots'));
}

export function preventHtmlValidation (message) {
  return (v, $t = (s) => s) => !v || !v.match(HTML_MARKUP_REGEXP) || (message || $t('components.pleaseDontUseHTMLMarkup'));
}

export function preventLinkedinValidation (message) {
  return (v, $t = (s) => s) => !v || !v.match(LINKED_IN_REGEXP) || (message || $t('components.beforeYouContinueMakeSureYourProfileDoesNotIncludeYourLinkedInContactInformation'));
}

export function preventCapitalsValidation (message) {
  return (v, $t = (s) => s) => !v || !(v.replace(/[A-Z]/g, '').length < v.length / 2) || (message || $t('components.tooManyCapitalLetters'));
}

export function preventSpecialCharsValidation (message) {
  return (v, $t = (s) => s) => !v || !!v.match(SPECIAL_CHARS_REGEXP) || (message || $t('components.yourtitleCannotIncludeSpecialCharactersLike'));
}

export function preventGreetingsValidation (message) {
  return (v, $t = (s) => s) => !v || !v.match(GREETINGS_REGEXP) || (message || $t('components.yourTitleShouldDescribeTheWorkYouDo'));
}

export function preventPhoneValidation (message) {
  return (v, $t = (s) => s) => !v || !v.match(PHONE_REGEXP) || (message || $t('components.pleaseRemoveAnyPhoneNumberFromThisField'));
}

export function allowDigitsOnlyValidation (message) {
  return (v, $t = (s) => s) => !v || !!v.match(/^\d+$/) || (message || $t('components.thisFieldMustContainsDigits'));
}

export function dateSameOrBeforeValidationLuxon (dateCompare, message) {
  return (v, $t = (s) => s) => !v || DateTime.fromISO(v) <= DateTime.fromISO(dateCompare) || (message || $t('components.thedateShouldBeSameOrBefore', {date: DateTime.fromISO(dateCompare).toFormat('dd-MM-yyyy')}));
}

export function dateAfterValidationLuxon (dateCompare, message) {
  return (v, $t = (s) => s) => !v || DateTime.fromISO(v) > DateTime.fromISO(dateCompare) || (message || $t('components.thedateShouldBeSameOrAfter', {date: DateTime.fromISO(dateCompare).toFormat('yyyy-MM-dd')}));
}

export function hoursValidation (message) {
  return (v, $t = (s) => s) => !v || parseHours(v) !== false || (message || $t('components.invalidTimeValueShouldBeInFormat'));
}


export function dateBeforeValidation (dateCompare, message) {
  return (v, $t = (s) => s) => {
    if (!v || !dateCompare || typeof dateCompare !== 'string') {
      return true;
    }
    const d1 = DateTime.fromFormat(v, STANDART_DATE_FORMAT).startOf('day');
    const d2 = DateTime.fromFormat(dateCompare, STANDART_DATE_FORMAT).startOf('day');

    return d1 < d2 || (message || $t('components.dateBefore', { date: formatDate(dateCompare) }));
  };
}

export function dateAfterValidation (dateCompare, message) {
  return (v, $t = (s) => s) => {
    if (!v || !dateCompare || typeof dateCompare !== 'string') {
      return true;
    }
    const d1 = DateTime.fromFormat(v, STANDART_DATE_FORMAT).startOf('day');
    const d2 = DateTime.fromFormat(dateCompare, STANDART_DATE_FORMAT).startOf('day');

    return d1 > d2 || (message || $t('components.dateAfter', { date: formatDate(dateCompare) }));
  };
}

export function dateSameOrAfterValidation (dateCompare, message) {
  return (v, $t = (s) => s) => {
    if (!v || !dateCompare || typeof dateCompare !== 'string') {
      return true;
    }
    const d1 = DateTime.fromFormat(v, STANDART_DATE_FORMAT).startOf('day');
    const d2 = DateTime.fromFormat(dateCompare, STANDART_DATE_FORMAT).startOf('day');

    return d1 >= d2 || (message || $t('components.dateSameOrAfter', { date: formatDate(dateCompare) }));
  };
}

export function dateSameOrBeforeValidation (dateCompare, message) {
  return (v, $t = (s) => s) => {
    if (!v || !dateCompare || typeof dateCompare !== 'string') {
      return true;
    }
    const d1 = DateTime.fromFormat(v, STANDART_DATE_FORMAT).startOf('day');
    const d2 = DateTime.fromFormat(dateCompare, STANDART_DATE_FORMAT).startOf('day');

    return d1 <= d2 || (message || $t('components.dateSameOrBefore', { date: formatDate(dateCompare) }));
  };
}

export function passwordValidation(message) {
  return (v, $t) => {
    return !isEmpty(v) && PASSWORD_REGEX.test(v.trim() + '') || (message || $t('components.passwordValidation'));
  };
}
