import { MailPoet } from 'mailpoet';
import jQuery from 'jquery';
import Cookies from 'js-cookie';
import Parsley from 'parsleyjs';
import { Hooks } from 'wp-js-hooks';
import { Response } from './ajax';

const exitIntentEvent = 'mouseleave.mailpoet.form-exit-intent';
const startingClassName = 'starting-to-show';

jQuery(($) => {
  // Initialize Ajax Error message
  MailPoet.I18n.add(
    'ajaxFailedErrorMessage',
    window.MailPoetForm.ajax_common_error_message,
  );

  // Initialize Parsley validation
  Parsley.addValidator('names', {
    requirementType: ['string', 'string'],
    validateString: (value, errorBrackets, errorURL) => {
      // Name can't contain angle brackets - https://mailpoet.atlassian.net/browse/MAILPOET-3408
      const bracketsExpression = /[><]+/gi;
      const bracketsRegex = new RegExp(bracketsExpression);
      if (value.match(bracketsRegex)) {
        return $.Deferred().reject(errorBrackets);
      }
      // Name can't contain URL - https://mailpoet.atlassian.net/browse/MAILPOET-3786
      const urlExpression = /https?:\/\/(www\.)?(.+)\.(.+)/gi;
      const urlRegex = new RegExp(urlExpression);
      if (value.match(urlRegex)) {
        return $.Deferred().reject(errorURL);
      }

      return true;
    },
    messages: {
      en: 'Please specify a valid name',
    },
  });

  /**
   * @param  {object} form jQuery object of MailPoet form
   * @return {string} The name of the cookie for the form
   */
  function getFormCookieName(form) {
    const formId = form.find('input[name="data[form_id]"]').val() as string;
    return `popup_form_dismissed_${formId}`;
  }

  /**
   * Sets the cookie for the form after successful subscription
   * Uses fixed cookie expiration time of 182 days
   *
   * @param  {object} form jQuery object of MailPoet form
   */
  function setFormCookieAfterSubscription(form) {
    const formDiv = form.parent('.mailpoet_form');
    if (formDiv.data('is-preview')) return;
    const formCookieName = getFormCookieName(form);
    Cookies.set(formCookieName, '1', { expires: 182, path: '/' });
  }

  function playCaptcha(e?: Event) {
    e.preventDefault();
    const audioSelector = '.mailpoet_captcha_player';
    const audio = document.querySelector<HTMLAudioElement>(audioSelector);
    if (!audio) {
      return;
    }
    audio.play().catch(() => {});
  }

  function updateCaptcha(e?: Event) {
    const imgSelector = 'img.mailpoet_captcha';
    const audioSelector = '.mailpoet_captcha_player';
    const captcha = document.querySelector(imgSelector);
    const audioCaptcha =
      document.querySelector<HTMLAudioElement>(audioSelector);
    if (!captcha || !audioCaptcha) {
      return false;
    }
    const audioCaptchaSource = audioCaptcha.querySelector('source');
    let captchaSrc = captcha.getAttribute('src');
    let hashPos = captchaSrc.indexOf('&cachebust=');
    let newSrc = hashPos > 0 ? captchaSrc.substring(0, hashPos) : captchaSrc;
    captcha.setAttribute('src', `${newSrc}&cachebust=${new Date().getTime()}`);

    captchaSrc = audioCaptchaSource.getAttribute('src');
    hashPos = captchaSrc.indexOf('&cachebust=');
    newSrc = hashPos > 0 ? captchaSrc.substring(0, hashPos) : captchaSrc;
    audioCaptchaSource.setAttribute(
      'src',
      `${newSrc}&cachebust=${new Date().getTime()}`,
    );
    audioCaptcha.load();

    if (e) e.preventDefault();
    return true;
  }

  function displaySuccessMessage(form) {
    setFormCookieAfterSubscription(form);
    // hide all form elements instead of .mailpoet_message
    form.children().not('.mailpoet_message').css('visibility', 'hidden');
    // add class that form was successfully send
    form.toggleClass('mailpoet_form_successfully_send');
    // display success message
    form.find('.mailpoet_validate_success').show();
    // hide elements marked with a class
    form.find('.mailpoet_form_hide_on_success').each(function hideOnSuccess() {
      $(this).hide();
    });
  }

  function submitSubscribeForm(
    form,
    formData: ReturnType<JQuery['mailpoetSerializeObject']>,
    parsley,
  ) {
    form.addClass('mailpoet_form_sending');
    // ajax request
    // eslint-disable-next-line @typescript-eslint/no-floating-promises
    MailPoet.Ajax.post<
      Response,
      {
        meta: { redirect_url: string; refresh_captcha: boolean };
      } & ErrorResponse
    >({
      url: window.MailPoetForm.ajax_url,
      token: formData.token,
      api_version: formData.api_version,
      endpoint: 'subscribers',
      action: 'subscribe',
      data: formData.data,
    })
      .fail((response) => {
        if (
          response.meta !== undefined &&
          response.meta.redirect_url !== undefined
        ) {
          // go to page
          window.top.location.href = response.meta.redirect_url;
        } else {
          if (response.meta && response.meta.refresh_captcha) {
            updateCaptcha();
          }
          if (window.grecaptcha && formData.recaptchaWidgetId) {
            window.grecaptcha.reset(formData.recaptchaWidgetId);
          }
          form
            .find('.mailpoet_validate_error')
            .html(response.errors.map((error) => error.message).join('<br />'))
            .show();
        }
      })
      .done((response) => {
        if (window.grecaptcha && formData.recaptchaWidgetId) {
          window.grecaptcha.reset(formData.recaptchaWidgetId);
        }
        return response;
      })
      .done((response) => {
        // successfully subscribed
        if (
          response.meta !== undefined &&
          response.meta.redirect_url !== undefined
        ) {
          setFormCookieAfterSubscription(form);
          // go to page
          window.location.href = response.meta.redirect_url;
        } else {
          displaySuccessMessage(form);
        }

        // reset form
        form.trigger('reset');
        // reset validation
        parsley.reset();
        // reset captcha
        if (window.grecaptcha && formData.recaptchaWidgetId) {
          window.grecaptcha.reset(formData.recaptchaWidgetId);
        }

        // resize iframe
        if (
          window.frameElement !== null &&
          MailPoet !== undefined &&
          MailPoet.Iframe
        ) {
          MailPoet.Iframe.autoSize(window.frameElement);
        }
      })
      .always(() => {
        form.removeClass('mailpoet_form_sending');
      });
  }

  function renderCaptcha(element, iteration: number) {
    if (!window.recaptcha || !window.grecaptcha.ready) {
      if (iteration < 20) {
        setTimeout(renderCaptcha, 400, element, iteration + 1);
      }
      return;
    }
    const recaptcha = $(element);
    const form = $(recaptcha).closest('form');
    const sitekey = recaptcha.attr('data-sitekey');
    let size = recaptcha.attr('data-size') as ReCaptchaV2.Size;

    // Users should not be able to change the size if it is equal to 'invisible' as this would
    // change the type of the ReCaptcha.
    if (size !== 'invisible') {
      size = Hooks.applyFilters('mailpoet_re_captcha_size', 'compact');
    }

    const container = recaptcha.find('> .mailpoet_recaptcha_container').get(0);
    const field = recaptcha.find('> .mailpoet_recaptcha_field');
    if (sitekey) {
      const params: ReCaptchaV2.Parameters = { sitekey, size };

      if (size === 'invisible') {
        params.callback = function invisibleReCaptchaCallback(
          recaptchaResponseToken,
        ) {
          const formData =
            form.mailpoetSerializeObject() ||
            ({} as ReturnType<JQuery['mailpoetSerializeObject']>);
          formData.data.recaptchaResponseToken = recaptchaResponseToken;

          submitSubscribeForm(form, formData, form.parsley());
        };
      }

      const widgetId = window.grecaptcha.render(container, params);
      field.val(widgetId as string);
    }
  }

  $('.mailpoet_recaptcha').each((_, element) => {
    setTimeout(renderCaptcha, 400, element, 1);
  });

  /**
   * Sets the cookie for the form after dismissing the form
   * Uses cookie expiration time defined on the form
   *
   * @param  {object} formDiv jQuery object of MailPoet form div
   */
  function setFormCookieOnClose(formDiv) {
    if (formDiv.data('is-preview')) return;
    const formCookieName = getFormCookieName(formDiv);
    if (Cookies.get(formCookieName) === '1') return;
    const cookieExpirationTime = formDiv
      .find('form')
      .data('cookie-expiration-time');
    Cookies.set(formCookieName, '1', {
      ...(cookieExpirationTime && { expires: cookieExpirationTime }),
      path: '/',
    });
  }

  function isSameDomain(url) {
    const link = document.createElement('a');
    link.href = url;
    return window.location.hostname === link.hostname;
  }

  function renderFontFamily(
    fontName: string,
    formDiv: JQuery<HTMLFormElement>,
  ) {
    const originalFontFamily = formDiv.css('font-family');
    const newFontFamily = `"${fontName}", ${originalFontFamily}`;
    formDiv.css('font-family', newFontFamily);
    formDiv.find('input, option').css('font-family', 'inherit');
    formDiv
      .find('input[type=text], textarea, input[type=email], select')
      .css('font-family', newFontFamily);
    formDiv.find(':header').css('font-family', 'inherit');

    formDiv
      .find('input[data-font-family]')
      .each(function applyFontFamilyToInput() {
        const element = $(this as HTMLFormElement);
        const inputFontFamily = element.data('font-family') as string;
        const inputOriginalFontFamily = element.css('font-family');
        const inputNewFontFamily = `"${inputFontFamily}", ${inputOriginalFontFamily}`;
        element.css('font-family', inputNewFontFamily);
      });

    formDiv
      .find('.mailpoet-has-font')
      .each(function applyFontFamilyToRichText() {
        const element = $(this);
        const spanOriginalFontFamily = element.css('font-family');
        const spanNewFontFamily = `"${spanOriginalFontFamily}", ${originalFontFamily}`;
        element.css('font-family', spanNewFontFamily);
      });
  }

  function doDisplayForm(formDiv, showOverlay) {
    formDiv.addClass('active');

    if (showOverlay) {
      formDiv.prev('.mailpoet_form_popup_overlay').addClass('active');
    }
  }

  function hideSucessMessage(form) {
    // hide success message
    form.find('.mailpoet_validate_success').hide();
    // show all form elements
    form.children().css('visibility', '');
    // remove class that form was successfully send
    form.removeClass('mailpoet_form_successfully_send');
    // show elements marked with a class
    form.find('.mailpoet_form_hide_on_success').each(function hideOnSuccess() {
      $(this).show();
    });
  }

  const isFormAlreadyEnqueued = (formDiv: JQuery<HTMLElement>) => {
    const id = formDiv.attr('id');
    return id
      ? Array.from(document.querySelectorAll(`#${id}`)).find((el) =>
          el.classList.contains(startingClassName),
        )
      : false;
  };

  function showForm(formDiv: JQuery<HTMLElement>, showOverlay = false) {
    if (isFormAlreadyEnqueued(formDiv)) {
      return;
    }
    formDiv.addClass(startingClassName);
    const form = formDiv.find('form');
    let delay = form.data('delay');
    delay = parseInt(delay as string, 10);
    if (Number.isNaN(delay)) {
      delay = 0;
    }
    const timeout = setTimeout(() => {
      $(document).off(exitIntentEvent);
      doDisplayForm(formDiv, showOverlay);
    }, delay * 1000);

    const exitIntentEnabled = form.data('exit-intent-enabled');
    if (exitIntentEnabled) {
      $(document).on(exitIntentEvent, () => {
        $(document).off(exitIntentEvent);
        clearTimeout(timeout);
        doDisplayForm(formDiv, showOverlay);
      });
    }
  }

  const closeForm = (formDiv) => {
    formDiv.removeClass('active');
    formDiv.prev('.mailpoet_form_popup_overlay').removeClass('active');
    setFormCookieOnClose(formDiv);
  };

  $(document).on('keyup', (e) => {
    if (e.key === 'Escape') {
      $('div.mailpoet_form').each((_, element: HTMLFormElement) => {
        if ($(element).children('.mailpoet_form_close_icon').length !== 0) {
          closeForm($(element));
        }
      });
    }
  });

  (() => {
    $('.mailpoet_form').each((_, element) => {
      $(element)
        .children(
          '.mailpoet_paragraph, .mailpoet_form_image, .mailpoet_form_paragraph',
        )
        .last()
        .addClass('last');
    });
    $('form.mailpoet_form').each((_, element: HTMLFormElement) => {
      const form = $(element) as JQuery<HTMLFormElement>;
      if (form.data('font-family')) {
        renderFontFamily(form.data('font-family') as string, form.parent());
      }
    });
    $('.mailpoet_form_close_icon').on('click', (event) => {
      const closeIcon = $(event.target);
      const formDiv = closeIcon.parent();
      if (formDiv.data('is-preview')) return; // Do not close popup in preview
      closeForm(formDiv);
    });

    $('div.mailpoet_form_fixed_bar, div.mailpoet_form_slide_in').each(
      (_, element) => {
        const formDiv = $(element);
        const formCookieName = getFormCookieName(formDiv);
        const cookieValue = Cookies.get(formCookieName);
        if (cookieValue === '1' && !formDiv.data('is-preview')) return;
        showForm(formDiv);
      },
    );

    $('div.mailpoet_form_popup').each((_, element) => {
      const formDiv = $(element);
      const formCookieName = getFormCookieName(formDiv);
      const cookieValue = Cookies.get(formCookieName);
      if (cookieValue === '1' && !formDiv.data('is-preview')) return;
      const showOverlay = true;
      showForm(formDiv, showOverlay);
    });

    // setup form validation
    $('form.mailpoet_form').each((_, element) => {
      const form = $(element);
      // Detect form is placed in tight container
      form.parsley().on('form:validated', () => {
        // clear messages
        form.find('.mailpoet_message > p').hide();

        // resize iframe
        if (window.frameElement !== null) {
          MailPoet.Iframe.autoSize(window.frameElement);
        }
      });

      form.parsley().on('form:submit', (parsley) => {
        // Disable form submit in preview mode
        const formDiv = form.parent('.mailpoet_form');
        if (formDiv && formDiv.data('is-preview')) {
          displaySuccessMessage(form);
          setTimeout(() => {
            hideSucessMessage(form);
          }, 2500);
          return false;
        }

        // check if we're on the same domain
        if (isSameDomain(window.MailPoetForm.ajax_url) === false) {
          // non ajax post request
          return true;
        }

        const formData =
          form.mailpoetSerializeObject() ||
          ({} as ReturnType<JQuery['mailpoetSerializeObject']>);
        const size = form
          .find('.mailpoet_recaptcha')
          .attr('data-size') as ReCaptchaV2.Size;

        if (window.grecaptcha && formData.recaptchaWidgetId) {
          // The API for the invisible and checkbox ReCaptchas is slightly different. For the
          // former, we need to call execute() and then the ReCaptcha API calls the callback set
          // inside renderCaptcha() with a token if the captcha was solved successfully. The
          // callback then calls submitSubscribeForm() with the token. For the latter, we get the
          // token here after calling getResponse() and then we can call submitSubscribeForm()
          // directly.
          if (size === 'invisible') {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            window.grecaptcha.execute(formData.recaptchaWidgetId);
          } else {
            formData.data.recaptchaResponseToken =
              window.grecaptcha.getResponse(formData.recaptchaWidgetId);
          }
        }

        if (size !== 'invisible') {
          submitSubscribeForm(form, formData, parsley);
        }

        return false;
      });
    });

    $('.mailpoet_captcha_update').on('click', updateCaptcha);
    $('.mailpoet_captcha_audio').on('click', playCaptcha);

    // Manage subscription form
    $('.mailpoet-manage-subscription').on('submit', (event) => {
      if (!$(event.target).parsley().isValid()) {
        event.preventDefault();
        $(event.target).parsley().validate();
        return;
      }
      $('.mailpoet-manage-subscription .mailpoet-submit-success').hide();
    });
  })();
});
