const { endOfDay, isAfter, isBefore, isValid: isValidDate, startOfDay } = require('date-fns');
const { cloneDeep, isEmpty } = require('lodash');
const { number, string } = require('yup');

const FqType = require('../../constant/flexible-questionnaire/fq-type.constant');

const isValidEmail = (str) => string().email().isValidSync(str);

const isNumber = (str) => number().isValidSync(str);

const isNumberInRange = (num, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY) =>
  number().min(min).max(max).isValidSync(num);

const isValidNumber = (question, answer) => {
  const { min, max } = question.inputProps;
  return isNumber(answer) && isNumberInRange(answer, min, max);
};

const Helper = {
  hasValue: (value) => {
    if (typeof value === 'string') {
      return value.trim() !== '';
    }

    if (typeof value === 'object') {
      return !isEmpty(value);
    }

    return ![null, undefined].includes(value);
  },

  hasError: (question, answer) => {
    const inputPropsType =
      question && question.inputProps && question.inputProps.type ? question.inputProps.type : null;

    if (
      !question.required &&
      ![FqType.GROUP, FqType.ADD_ANOTHER, FqType.DATEPICKER].includes(question.type) &&
      !['email', 'number'].includes(inputPropsType) &&
      isEmpty(question.validationProps)
    ) {
      return false;
    }

    const questionAnswer =
      answer && Object.prototype.hasOwnProperty.call(answer, question.key) ? answer[question.key] : answer;

    switch (question.type) {
      case FqType.GROUP:
        return Helper.hasGroupError(question, questionAnswer);
      case FqType.LIBRARYTYPE:
        return Helper.hasLibraryError(question, questionAnswer);
      case FqType.ADD_ANOTHER:
        return Helper.hasAddAnotherError(question, questionAnswer);
      case FqType.PERMIT_SITE:
        return Helper.hasAddAnotherError(question, questionAnswer);
      case FqType.LABEL:
        return false;
      case FqType.PEOPLE:
      case FqType.LOCATION:
      case FqType.ATTACHMENT:
        return isEmpty(answer) || !Object.keys(answer).filter((key) => answer[key]).length;
      case FqType.DATETIMEPICKER:
      case FqType.DATEPICKER: {
        if (question.required && !Helper.hasValue(questionAnswer)) {
          return true;
        }

        if (questionAnswer && !isValidDate(new Date(questionAnswer))) {
          return true;
        }

        const { disableFuture, disablePast, minDate, maxDate, validationProps } = question;

        if (!isEmpty(validationProps)) {
          const { maxDateKey, minDateKey } = validationProps;

          if (maxDateKey && answer && answer[maxDateKey]) {
            if (isAfter(new Date(questionAnswer), new Date(answer[maxDateKey]))) {
              return true;
            }
          }

          if (minDateKey && answer && answer[minDateKey]) {
            if (isBefore(new Date(questionAnswer), new Date(answer[minDateKey]))) {
              return true;
            }
          }
        }

        if (maxDate) {
          if (isAfter(new Date(questionAnswer), new Date(maxDate))) {
            return true;
          }
        }

        if (disableFuture) {
          if (isAfter(new Date(questionAnswer), endOfDay(new Date()))) {
            return true;
          }
        }

        if (minDate) {
          if (isBefore(new Date(questionAnswer), new Date(minDate))) {
            return true;
          }
        }

        if (disablePast) {
          if (isBefore(new Date(questionAnswer), startOfDay(new Date()))) {
            return true;
          }
        }

        return false;
      }
      case FqType.RECIPIENTS: {
        if (question.required && !Helper.hasValue(questionAnswer)) {
          return true;
        }

        if (question.valueType === 'email' && Helper.hasValue(questionAnswer)) {
          return questionAnswer.some(({ email }) => {
            try {
              string().email().validateSync(email);
            } catch (yupError) {
              return true;
            }

            return false;
          });
        }

        return false;
      }
      case FqType.RICHTEXT: {
        return Helper.hasRichTextError(question, questionAnswer);
      }
      default: {
        if (question.required && !Helper.hasValue(questionAnswer)) {
          return true;
        }

        switch (inputPropsType) {
          case 'email':
            return !isValidEmail(questionAnswer);
          case 'number': {
            if (Helper.hasValue(questionAnswer) && !isValidNumber(question, questionAnswer)) {
              return true;
            }

            const { inputProps, validationProps } = question;
            if (!isEmpty(validationProps)) {
              const { maxNumberKey, minNumberKey } = validationProps;

              if (maxNumberKey && answer && Helper.hasValue(answer[maxNumberKey]) && isNumber(answer[maxNumberKey])) {
                if (Number(questionAnswer) > Number(answer[maxNumberKey])) {
                  return true;
                }
              }

              if (minNumberKey && answer && Helper.hasValue(answer[minNumberKey]) && isNumber(answer[minNumberKey])) {
                if (Number(questionAnswer) < Number(answer[minNumberKey])) {
                  return true;
                }
              }
            }

            if (!isEmpty(inputProps)) {
              const { max, min } = inputProps;
              if (Helper.hasValue(max) && isNumber(max)) {
                if (Number(questionAnswer) > Number(max)) {
                  return true;
                }
              }

              if (Helper.hasValue(min) && isNumber(min)) {
                if (Number(questionAnswer) < Number(min)) {
                  return true;
                }
              }
            }

            return false;
          }
          default:
            return false;
        }
      }
    }
  },

  hasRichTextError: (question, answer) => {
    if (question.required) {
      if (answer) {
        const replaceTag = answer.replace('<p>', '');
        const answerValue = replaceTag.replace('</p>', '');
        if ((answerValue && answerValue.trim().length === 0) || ['<p></p>', '<p></p>\n'].includes(answer)) {
          return true;
        }
      } else {
        return true;
      }
    }
    return false;
  },

  // for follow up question, answers are in array
  // since there could be multiple times answer
  // (eg. multiple materials) for same set of question
  // and whenever there is an answer,
  // check against corresponding question with answer is valid
  hasFollowUpError: (question, answer) =>
    answer &&
    answer.some(
      (ans) =>
        question &&
        !isEmpty(question.questions) &&
        !question.questions.some((q) => ans[q.key] && !Helper.hasGroupError(q, ans[q.key])),
    ),

  hasLibraryError: (question, answer) => {
    if (question.required && (!answer || isEmpty(answer))) {
      return true;
    }

    return (
      answer &&
      !answer.every((a) => {
        const libraryQuestion =
          question.showTagFilter === false
            ? question.questions[0]
            : question.questions.find((q) => a.tags.some((tag) => q.parentType.includes(tag)));
        // Fixes issue where item would show as invalid if type didn't have follow up questions
        if (!libraryQuestion) {
          return true;
        }
        return !Helper.hasGroupError(libraryQuestion, a[libraryQuestion.key] || {});
      })
    );
  },

  hasAddAnotherError: function hasAddAnotherError(question, answers) {
    if (question.required && !Helper.hasValue(answers)) {
      return true;
    }

    return question.questions.some(
      (q) =>
        answers &&
        answers.some((ans) => {
          if (question.require && isEmpty(ans.item)) {
            return true;
          }
          if (q.type === FqType.GROUP) {
            return Helper.hasGroupError(q, ans.item[q.key]);
          }
          if (q.type === FqType.LIBRARYTYPE) {
            return Helper.hasLibraryError(q, ans.item[q.key]);
          }
          if (!isEmpty(q.validationProps)) {
            return Helper.hasError(q, ans.item);
          }

          return Helper.hasError(q, ans.item[q.key]);
        }),
    );
  },

  hasGroupError: (questionGroup, answers) =>
    questionGroup.questions.some((q) => {
      if (answers) {
        if (q.type === FqType.GROUP) {
          return Helper.hasGroupError(q, answers[q.key]);
        }
        if (q.type === FqType.LIBRARYTYPE) {
          return Helper.hasLibraryError(q, answers[q.key]);
        }
        if (q.type === FqType.ADD_ANOTHER) {
          return Helper.hasAddAnotherError(q, answers[q.key]);
        }
        if (q.type === FqType.DEPENDENT) {
          return Helper.hasDependentError(q, answers);
        }
        if (!isEmpty(q.validationProps)) {
          return Helper.hasError(q, answers);
        }
        return !!q.required && Helper.hasError(q, answers[q.key]);
      }
      return !!q.required;
    }),

  hasDependentError: (q, answers) => {
    if (Array.isArray(q.parentValue)) {
      // eslint-disable-next-line
      if (Helper._multipleAnswersHaveMatchedParents(q, answers)) {
        return Helper.hasGroupError(q, answers[q.key]);
        // eslint-disable-next-line
      } else if (Helper._singleAnswerHasMatchedParnts(q, answers)) {
        return Helper.hasGroupError(q, answers[q.key]);
      }
    } else {
      // eslint-disable-next-line
      if (Helper._answerHasMatchedParent(q, answers)) {
        return Helper.hasGroupError(q, answers[q.key]);
        // eslint-disable-next-line
      } else if (Helper._multipleAnswersHaveMatchedParent(q, answers)) {
        return Helper.hasGroupError(q, answers[q.key]);
        // eslint-disable-next-line
      } else {
        return Helper.hasError(q, answers);
      }
    }
    return false;
  },

  _multipleAnswersHaveMatchedParent: (q, answers) =>
    Array.isArray(answers[q.parentKey]) && answers[q.parentKey].some((ans) => ans.value === q.parentValue),

  _multipleAnswersHaveMatchedParents: (q, answers) =>
    Array.isArray(answers[q.parentKey]) &&
    answers[q.parentKey].some((ans) => q.parentValue.some((pValue) => ans.value === pValue)),

  _answerHasMatchedParent: (q, answers) =>
    answers[q.parentKey] && (answers[q.parentKey].value === q.parentValue || answers[q.parentKey] === q.parentValue),

  _singleAnswerHasMatchedParnts: (q, answers) =>
    q.parentValue.some((pValue) => answers[q.parentKey] && pValue === answers[q.parentKey].value),

  isFormValid(questions, answers) {
    return !questions.some((question) => Helper.hasError(question, answers));
  },

  setDefaultValueToResponses: (responses, questions = []) => {
    if (!questions || isEmpty(questions)) {
      return responses;
    }

    const transformed = cloneDeep(responses || {});
    questions.forEach((q) => {
      if (transformed[q.key] !== undefined || q.defaultValue) {
        switch (q.type) {
          case FqType.LABEL:
            break;
          case FqType.DEPENDENT:
          case FqType.GROUP:
            transformed[q.key] = Helper.setDefaultValueToResponses(transformed[q.key], q.questions);
            break;
          case FqType.ADD_ANOTHER:
            transformed[q.key] = (transformed[q.key] || []).map((answer) =>
              Helper.setDefaultValueToResponses(answer, q.questions),
            );
            break;
          default:
            if ((!transformed[q.key] || isEmpty(transformed[q.key])) && q.defaultValue) {
              transformed[q.key] = q.defaultValue;
            }
            break;
        }
      }
    });
    return transformed;
  },
};

module.exports = Helper;
