var Wick = Wick || {};

Wick.multipleFiles = {
  valueFiles: [],
  count: 5,
  filesForm: {
    getPreviewTemplate (installerFileName) {
      var previewTemplate = $('.installer_question-file_result_wrapper[data-installer-file-name="' + installerFileName + '"]');
      return $(previewTemplate[Wick.multipleFiles.valueFiles.length])
    },
    setValue: function(installerFileName, value) {
      Wick.multipleFiles.valueFiles.push(value)
    },
    removeValue: function(installerFileName, order) {
      Wick.multipleFiles.valueFiles.splice(order, 1)
    },
    showAddFileTemplate: function (installerFileName) {
      if (Wick.multipleFiles.valueFiles.length < Wick.multipleFiles.count) {
        var addFileTemplate = $('label.installer_question-file_label[data-installer-file-name="' + installerFileName + '"]');
        addFileTemplate.removeClass('hide');
      }
    },
    hideAddFileTemplate: function (installerFileName) {
      if (Wick.multipleFiles.valueFiles.length >= Wick.multipleFiles.count) {
        var addFileTemplate = $('label.installer_question-file_label[data-installer-file-name="' + installerFileName + '"]');
        addFileTemplate.addClass('hide');
      }
    },
    showErrorNotification: function (installerFileName, error) {
      Wick.InstallerValidation.addErrorNotificationToElement(installerFileName, error, true)
    },
    hideErrorNotification: function (installerFileName) {
      Wick.InstallerValidation.removeErrorNotificationFromElement(installerFileName)
    },
    hideDefaultError: function (element) {
      Wick.InstallerValidation.removeErrorNotificationFromElement(element.attr('name'))
    },
    showImagePreviewTemplate: function (installerFileName) {
      var previewTemplate = Wick.multipleFiles.filesForm.getPreviewTemplate(installerFileName)
      previewTemplate.removeClass('hide');
    },
    hideImagePreviewTemplate: function (installerFileName, order) {
      var previewTemplates = $('.installer_question-file_result_wrapper[data-installer-file-name="' + installerFileName + '"]');

      var i = order + 1;
      while (Wick.multipleFiles.count > i) {
        $(previewTemplates[i]).data('file', i-1)
        i++;
      }
 
      $(previewTemplates[order]).addClass('hide');
      $(previewTemplates[order]).data('file', (Wick.multipleFiles.count-1));
      $(previewTemplates[order]).appendTo('.your-details__file-wrapper');
    },
    readFile: function (file, installerFileName) {
      var reader = new FileReader();
      var imagePreviewTemplate = Wick.multipleFiles.filesForm.getPreviewTemplate(installerFileName)

      reader.onload = function(e) {
        var imagePreviewElement = imagePreviewTemplate.find('img');

        imagePreviewElement.attr('src', e.target.result);
      };

      reader.readAsDataURL(file);
    },
    showSpinner: function () {
      var loader = $('.installer__loader');
      loader.addClass('show');
    },
    hideSpinner: function () {
      var loader = $('.installer__loader');
      loader.removeClass('show');
    },
    addFileHandler: function (file, installerFileName) {
      Wick.S3bucketUpload.uploadImage(file, installerFileName);
    },
    onSuccessFileAdd: function (file, installerFileName, fileLink) {
      Wick.multipleFiles.filesForm.hideErrorNotification(installerFileName);
      Wick.multipleFiles.filesForm.showImagePreviewTemplate(installerFileName);
      Wick.multipleFiles.filesForm.readFile(file, installerFileName);
      Wick.multipleFiles.filesForm.setValue(installerFileName, fileLink);
      Wick.multipleFiles.filesForm.hideAddFileTemplate(installerFileName);
    },
    removeFileHandler: function (installerFileName, order) {
      Wick.multipleFiles.filesForm.hideErrorNotification(installerFileName);
      Wick.multipleFiles.filesForm.removeValue(installerFileName, order);
      Wick.multipleFiles.filesForm.hideImagePreviewTemplate(installerFileName, order);
      Wick.multipleFiles.filesForm.showAddFileTemplate(installerFileName);
    }
  },
  bindFileInputs: function () {
    $('body').on('change', '.installer_question-file_input', function() {
      var closestLabel = $(this).closest('.installer_question-file_label');
      var files = $(this).get(0).files
      var delta = Wick.multipleFiles.count - Wick.multipleFiles.valueFiles.length;
      var i = (files.length < delta) 
        ? files.length 
        : delta;
      while(i) {
        i--
        var file = files[i]
        Wick.multipleFiles.filesForm.addFileHandler(file, closestLabel.attr('data-installer-file-name'));
      }
    });
  },
  bindImageTemplate: function() {
    var element = $('.your-details__file-wrapper .installer_question-file_result_wrapper')
    var i = 0;
    element.data('file', i)
    while ((Wick.multipleFiles.count-1) > i) {
      i++
      element
        .clone()
        .data('file', i)
        .appendTo('.your-details__file-wrapper');
    }
  },
  bindDeleteFileButtons: function () {
    $('body').on('click', '.delete_action', function() {
      var closestLabel = $(this).closest('.installer_question-file_result_wrapper');
      Wick.multipleFiles.filesForm.removeFileHandler(closestLabel.attr('data-installer-file-name'), closestLabel.data('file'));
    });
  },

  RECAPTCHA_PUBLIC_KEY: '6LdVZ74ZAAAAABiyCm6Szsv5T9wbrOCegkskM2iR',

  recaptchaDecorate: function (action, f) {
    return function() {
      const context = this
      const args = arguments
      grecaptcha.ready(function () {
        grecaptcha.execute(Wick.multipleFiles.RECAPTCHA_PUBLIC_KEY, {
          action: action
        }).then(function (token) {
          f.call(context, token, ...args)
        })
      })
    }
  }
};

$(document).ready(function () {
  Wick.multipleFiles.bindImageTemplate();
  Wick.multipleFiles.bindFileInputs();
  Wick.multipleFiles.bindDeleteFileButtons();
});

// Polyfills
if (!String.prototype.startsWith) {
  String.prototype.startsWith = function(searchString, position) {
    position = position || 0;
    return this.indexOf(searchString, position) === position;
  };
}

Wick.S3bucketUpload = {
  validExtensionsList: ['jpg', 'jpeg', 'png', 'bmp', 'webp', 'tiff', 'heif', 'heic'],
  isFileExtensionValid: function (file) {
    var fileName = file.name.split('.')
    return Wick.S3bucketUpload.validExtensionsList.includes(fileName[fileName.length - 1].toLowerCase())
  },
  transformXmlError: function (error) {
    var parser = new DOMParser();
    var xmlDoc = parser.parseFromString(error.responseText, "text/xml");
    var errorMessage = xmlDoc.all[2].innerHTML;
    if (errorMessage === 'Your proposed upload exceeds the maximum allowed size') {
      return 'Unfortunately your document could not be uploaded, as the file exceeds the 5MB limit. Please try again with a file no greater than 5MB.';
    }
    return errorMessage;
  },
  transformError: function (error) {
    if (error.responseXML) {
      return Wick.S3bucketUpload.transformXmlError(error);
    }
    return error.statusText;
  },
  uploadImage: function(file, installerFileName) {
    var isExtesionValid = Wick.S3bucketUpload.isFileExtensionValid(file);

    if (!isExtesionValid) {
      Wick.multipleFiles.filesForm.showErrorNotification(installerFileName, [
        'Unable to attach file. Only JPG, JPEG, PNG, BMP, WEBP, TIFF, HEIC and HEIF image files are supported.'
      ]);

      return;
    }

    Wick.multipleFiles.filesForm.showSpinner();

    setTimeout(()=> {
      Wick.multipleFiles.filesForm.hideSpinner();
      Wick.multipleFiles.filesForm.onSuccessFileAdd(file, installerFileName, installerFileName);
    }, 1000)
  }
};


Wick.InstallerValidation = {
  findRequirementsByName: function (elementName) {
    var step = $('[name="'+ elementName +'"]').closest('.installer-block').attr('data-step');
    return Wick.InstallerValidation.steps[step][elementName];
  },
  getValueByName: function (elementName) {
    var element = $('[name="'+ elementName +'"]');
    var type = element.attr("type");

    if (element.prop('tagName') === 'SELECT') {
      return element.val()
    }

    switch (type) {
      case 'radio':
        return $('[name="'+ elementName +'"]:checked').val();
      case 'checkbox':
        return $('[name="'+ elementName +'"]:checked').map(function(){
          return $(this).val();
        }).get();
      case 'file':
        return element.attr('data-value');
      default:
        return element.val().trim();
    }
  },
  isInputValueNotEmpty: function (value) {
    if (typeof value === 'string') return !!value;
    if (Array.isArray(value)) return value.length > 0;

    return false;
  },
  elemDependsCheck: function (dependentValue, requirements) {
    var isValueExist = Wick.InstallerValidation.isInputValueNotEmpty(dependentValue);
    if (isValueExist) {
      if (requirements.dependsOnValue) {
        if (typeof dependentValue === 'string' || Array.isArray(dependentValue)) {
          return Wick.InstallerValidation.elemPossibleValuesCheck(dependentValue, requirements.dependsOnValue);
        } else {
          return false;
        }
      }
    } else {
      return false;
    }

    return true;
  },
  elemRequiredCheck: function (value, requirements) {
    var isValueExist = Wick.InstallerValidation.isInputValueNotEmpty(value);
    if (requirements.required && !isValueExist) {
      return false;
    }

    return true;
  },
  elemPossibleValuesCheck: function (value, possibleValues) {
    if (Array.isArray(possibleValues) && possibleValues.length > 0) {
      if (typeof value === 'string') {
        if (possibleValues && possibleValues.indexOf(value) < 0) {
          return false;
        }
      }
      if (Array.isArray(value) && value.length > 0) {
        if (possibleValues) {
          var isValid = value
            .map(function (arrayValue) { return possibleValues.indexOf(arrayValue) >= 0 })
            .some(function (result) { return !!result });

          if (!isValid) {
            return false;
          }
        }
      }
    }
    return true;
  },
  elemRegexCheck: function (value, requirements) {
    if (requirements.regex && !RegExp(requirements.regex).test(value)) {
      return false;
    }

    return true;
  },
  elemMaxLengthCheck: function (value, requirements) {
    if (typeof value === 'string' && requirements.maxLength) {
      return value.length <= requirements.maxLength;
    }

    return true;
  },
  isElementValid: function (elementName) {
    var requirements = Wick.InstallerValidation.findRequirementsByName(elementName);
    var value = Wick.InstallerValidation.getValueByName(elementName);
    var error = requirements.error;

    if (value && typeof value === 'string') {
      value = value.trim();
    }

    if (requirements.required) {
      if (requirements.dependsOnInput) {
        var dependentValue = Wick.InstallerValidation.getValueByName(requirements.dependsOnInput);
        if (dependentValue) {
          if (requirements.dependsOnValue) {
            var elemDependsCheckResult = Wick.InstallerValidation.elemDependsCheck(dependentValue, requirements);
            if (elemDependsCheckResult) {
              var result = Wick.InstallerValidation.checkElementValidations(value, requirements);
              return { valid: result.valid, errorType: result.errorType, error  };
            }
          } else {
            var result = Wick.InstallerValidation.checkElementValidations(value, requirements);
            return { valid: result.valid, errorType: result.errorType, error  };
          }
        }
      } else {
        var result = Wick.InstallerValidation.checkElementValidations(value, requirements);
        return { valid: result.valid, errorType: result.errorType, error  };
      }
    } else {
      var isValueExist = Wick.InstallerValidation.isInputValueNotEmpty(value);
      if (isValueExist) {
        var result = Wick.InstallerValidation.checkElementValidations(value, requirements);
        return { valid: result.valid, errorType: result.errorType, error  };
      }
    }

    return { valid: true, error };
  },
  checkElementValidations: function (value, requirements) {
    var elemRequiredCheckResult = Wick.InstallerValidation.elemRequiredCheck(value, requirements);
    var elemPossibleValuesCheckResult = Wick.InstallerValidation.elemPossibleValuesCheck(value, requirements.possibleValues);
    var elemRegexCheckResult = Wick.InstallerValidation.elemRegexCheck(value, requirements);
    var elemMaxLengthCheckResult = Wick.InstallerValidation.elemMaxLengthCheck(value, requirements);

    if (!elemRequiredCheckResult) return { valid: false, errorType: 'required' };
    if (!elemPossibleValuesCheckResult || !elemRegexCheckResult || !elemMaxLengthCheckResult) return { valid: false, errorType: 'validation' };

    return { valid: true };
  },
  runElementCallback: function (elementName, changedElemValue) {
    var value = Wick.InstallerValidation.getValueByName(elementName);
    var requirements = Wick.InstallerValidation.findRequirementsByName(elementName);
    if (requirements.callback) {
      requirements.callback(value, changedElemValue);
    }
  },
  checkElement: function (elementName, onChange) {
    var result = Wick.InstallerValidation.isElementValid(elementName);
    if (result.valid) {
      if (onChange) {
        Wick.InstallerValidation.removeErrorNotificationFromElement(elementName);
        Wick.InstallerValidation.removeGlobalErrorNotification();
      }
      return result;
    }

    Wick.InstallerValidation.addErrorNotificationToElement(elementName, result.error, true);

    return result;
  },
  appendErrorMessage: function (wrapper, elementName, error) {
    var notification = '<div' +
      '            data-installer-input-name="' + elementName + '"' +
      '            class="form-row__error"' +
      '          >' + error.map(function (err) { return '<p>' + err + '</p>';}).join('') +
      '          </div>';
    wrapper.append(notification);
  },
  addErrorNotificationToElement: function (elementName, error, removePrevNotification) {
    if (!error) return;
    var element = $('[name="'+ elementName +'"]');
    var wrapper = element.closest('[data-form-field]');
    var prevNotification = wrapper.children('.form-row__error[data-installer-input-name="'+ elementName +'"]');
    if (prevNotification.length === 0) {
      Wick.InstallerValidation.appendErrorMessage(wrapper, elementName, error);
    } else {
      if (removePrevNotification) {
        prevNotification.remove();
        Wick.InstallerValidation.appendErrorMessage(wrapper, elementName, error);
      }
    }
  },
  removeErrorNotificationFromElement: function (elementName) {
    var element = $('[name="'+ elementName +'"]');
    var wrapper = element.closest('[data-form-field]');
    var error = wrapper.children('.installer_question-error_wrapper[data-installer-input-name="'+ elementName +'"]');
    var formError = wrapper.find('.form-row__error[data-installer-input-name="'+ elementName +'"]');
    error.remove();
    formError.remove();
  },
  removeAllErrorsFromStep: function (step) {
    var installerBlock = $('.installer-block[data-step="'+ step +'"]');
    installerBlock.find('.form-row__error').remove();
  },
  addGlobalErrorNotification: function (step, errorTypes) {
    var wrapper = $('.installer-step-errors');
    wrapper.attr('data-step', step);
    wrapper.attr('data-step-error', '');
    errorTypes.forEach(function (type) {
      wrapper.attr(`data-error-${type}`, '');
    });
  },
  removeGlobalErrorNotification: function () {
    var wrapper = $('.installer-step-errors');
    wrapper.removeAttr('data-step-error');
    wrapper.removeAttr('data-error-required');
    wrapper.removeAttr('data-error-validation');
  },
  validateStepForInstallerForm: function (step) {
    Wick.InstallerValidation.removeAllErrorsFromStep(step);

    var inputNames = Object.keys(Wick.InstallerValidation.steps[step]);
    var results = inputNames
      .map(function (elementName) {
        return {
          name: elementName,
          result: Wick.InstallerValidation.checkElement(elementName)
        };
      });
    var isStepInvalid = results.some(function (result) { return !result.result.valid; });
    var errorTypes = results.reduce(function (prev, next) {
      var result = next.result.errorType ? { [next.result.errorType]: true } : {};
      return Object.assign(
        prev,
        result
      )
    }, {});
    errorTypes = Object.keys(errorTypes);

    if (isStepInvalid) {
      var firstErrorField = results.find(function (result) { return !result.result.valid; });
      Wick.InstallerValidation.scrollToField(firstErrorField.name);
      Wick.InstallerValidation.addGlobalErrorNotification(step, errorTypes);
    }

    return !isStepInvalid;
  },
  scrollToField: function (name) {
    var element = $('[name="'+ name +'"]:first');
    var elementHeight = element.height();
    $([document.documentElement, document.body]).animate({
      scrollTop: element.offset().top - elementHeight
    }, 1500);
  },
  validatemultipleFiles: function () {
    var isStepsValid = Wick.multipleFiles.steps
      .map(function (step) { return Wick.InstallerValidation.validateStepFormultipleFiles(step); })
      .every(function (result) { return !!result; });

    return isStepsValid;
  }
};

// Polyfills
if (!Array.prototype.some) {
  Array.prototype.some = function(fun, thisArg) {
    'use strict';

    if (this == null) {
      throw new TypeError('Array.prototype.some called on null or undefined');
    }

    if (typeof fun !== 'function') {
      throw new TypeError();
    }

    var t = Object(this);
    var len = t.length >>> 0;

    for (var i = 0; i < len; i++) {
      if (i in t && fun.call(thisArg, t[i], i, t)) {
        return true;
      }
    }

    return false;
  };
}

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}

if (!Array.prototype.every) {
  Array.prototype.every = function(callbackfn, thisArg) {
    'use strict';
    var T, k;

    if (this == null) {
      throw new TypeError('this is null or not defined');
    }

    // 1. Let O be the result of calling ToObject passing the this
    //    value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get internal method
    //    of O with the argument "length".
    // 3. Let len be ToUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
    if (typeof callbackfn !== 'function' && Object.prototype.toString.call(callbackfn) !== '[object Function]') {
      throw new TypeError();
    }

    // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
    if (arguments.length > 1) {
      T = thisArg;
    }

    // 6. Let k be 0.
    k = 0;

    // 7. Repeat, while k < len
    while (k < len) {

      var kValue;

      // a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty internal
      //    method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {
        var testResult;
        // i. Let kValue be the result of calling the Get internal method
        //    of O with argument Pk.
        kValue = O[k];

        // ii. Let testResult be the result of calling the Call internal method
        // of callbackfn with T as the this value if T is not undefined
        // else is the result of calling callbackfn
        // and argument list containing kValue, k, and O.
        if(T) testResult = callbackfn.call(T, kValue, k, O);
        else testResult = callbackfn(kValue,k,O)

        // iii. If ToBoolean(testResult) is false, return false.
        if (!testResult) {
          return false;
        }
      }
      k++;
    }
    return true;
  };
}

if (!Array.prototype.reduce) {
  Object.defineProperty(Array.prototype, 'reduce', {
    value: function(callback /*, initialValue*/) {
      if (this === null) {
        throw new TypeError( 'Array.prototype.reduce ' +
          'called on null or undefined' );
      }
      if (typeof callback !== 'function') {
        throw new TypeError( callback +
          ' is not a function');
      }

      // 1. Let O be ? ToObject(this value).
      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // Steps 3, 4, 5, 6, 7
      var k = 0;
      var value;

      if (arguments.length >= 2) {
        value = arguments[1];
      } else {
        while (k < len && !(k in o)) {
          k++;
        }

        // 3. If len is 0 and initialValue is not present,
        //    throw a TypeError exception.
        if (k >= len) {
          throw new TypeError( 'Reduce of empty array ' +
            'with no initial value' );
        }
        value = o[k++];
      }

      // 8. Repeat, while k < len
      while (k < len) {
        // a. Let Pk be ! ToString(k).
        // b. Let kPresent be ? HasProperty(O, Pk).
        // c. If kPresent is true, then
        //    i.  Let kValue be ? Get(O, Pk).
        //    ii. Let accumulator be ? Call(
        //          callbackfn, undefined,
        //          « accumulator, kValue, k, O »).
        if (k in o) {
          value = callback(value, o[k], k, o);
        }

        // d. Increase k by 1.
        k++;
      }

      // 9. Return accumulator.
      return value;
    }
  });
}


