import { Utility } from '../../core/utility';

const MASS_INPUT_CELL_SELECTOR = '.massinput__cell';
const MASS_INPUT_ADD_CELL_SELECTOR = '.massinput__cell--add';
const MASS_INPUT_SUBMIT_BUTTON_CLASS = 'massinput__submit-button';
const MASS_INPUT_INITIALIZED_CLASS = 'mass-input--initialized';

@Utility({
  selector: '[uw-mass-input]',
})
export class MassInput {

  _element;
  _app;

  _massInputId;
  _massInputFormSubmitHandler;
  _massInputForm;

  constructor(element, app) {
    if (!element) {
      throw new Error('Mass Input utility cannot be setup without an element!');
    }

    this._element = element;
    this._app = app;

    if (this._element.classList.contains(MASS_INPUT_INITIALIZED_CLASS)) {
      return false;
    }

    this._massInputId = this._element.dataset.massInputIdent || '_';
    this._massInputForm = this._element.closest('form');

    if (!this._massInputForm) {
      throw new Error('Mass Input utility cannot be setup without being wrapped in a <form>!');
    }

    this._massInputFormSubmitHandler = this._makeSubmitHandler();

    // setup submit buttons inside this massinput so browser
    // uses correct submit button for form submission.
    const buttons = this._getMassInputSubmitButtons();
    buttons.forEach((button) => {
      this._setupSubmitButton(button);
    });

    this._massInputForm.addEventListener('submit', this._massInputFormSubmitHandler);
    this._massInputForm.addEventListener('keypress', this._keypressHandler);

    // mark initialized
    this._element.classList.add(MASS_INPUT_INITIALIZED_CLASS);
  }

  destroy() {
    this._reset();
  }

  _makeSubmitHandler() {
    const method = this._massInputForm.getAttribute('method') || 'POST';
    const url = this._massInputForm.getAttribute('action') || window.location.href;
    const enctype = this._massInputForm.getAttribute('enctype') || 'application/json';

    let requestFn;
    if (this._app.httpClient[method.toLowerCase()]) {
      requestFn = this._app.httpClient[method.toLowerCase()].bind(this._app.httpClient);
    }

    return (event) => {
      let activeElement;

      // check if event occured from either a mass input add/delete button or
      // from inside one of massinput's inputs (i.e. a child is focused/active)
      activeElement = this._element.querySelector(':focus, :active');

      if (!activeElement) {
        return false;
      }

      // find the according massinput cell thats hosts the element that triggered the submit
      const massInputCell = activeElement.closest(MASS_INPUT_CELL_SELECTOR);
      if (!massInputCell) {
        return false;
      }

      const submitButton = massInputCell.querySelector('.' + MASS_INPUT_SUBMIT_BUTTON_CLASS);
      if (!submitButton) {
        return false;
      }

      const isAddCell = massInputCell.matches(MASS_INPUT_ADD_CELL_SELECTOR);
      const submitButtonIsActive = submitButton.matches(':focus, :active');
      // if the cell is not an add cell the active element must at least be the cells submit button
      if (!isAddCell && !submitButtonIsActive) {
        return false;
      }

      event.preventDefault();
      const requestBody = this._serializeForm(submitButton, enctype);

      if (requestFn && requestBody) {
        const headers = {'Mass-Input-Shortcircuit': this._massInputId};

        if (enctype !== 'multipart/form-data') {
          headers['Content-Type'] = enctype;
        }

        requestFn({
          url: url,
          headers: headers,
          body: requestBody,
        }).then((response) => {
          return this._app.htmlHelpers.parseResponse(response);
        }).then((response) => {
            this._processResponse(response.element);
            if (isAddCell) {
              this._reFocusAddCell();
            }
          });
      }
    };
  }

  _keypressHandler = (event) => {
    if (event.keyCode !== 13) {
      return false;
    }

    if (this._massInputFormSubmitHandler) {
      return this._massInputFormSubmitHandler(event);
    }
  }

  _getMassInputSubmitButtons() {
    return Array.from(this._element.querySelectorAll('button[type="submit"][name][value], .' + MASS_INPUT_SUBMIT_BUTTON_CLASS));
  }

  _setupSubmitButton(button) {
    button.setAttribute('type', 'button');
    button.classList.add(MASS_INPUT_SUBMIT_BUTTON_CLASS);
    button.addEventListener('click', this._massInputFormSubmitHandler);
  }

  _resetSubmitButton(button) {
    button.setAttribute('type', 'submit');
    button.classList.remove(MASS_INPUT_SUBMIT_BUTTON_CLASS);
    button.removeEventListener('click', this._massInputFormSubmitHandler);
  }

  _processResponse(responseElement) {
    this._element.innerHTML = '';
    this._element.appendChild(responseElement);

    this._reset();

    this._app.utilRegistry.setupAll(this._element);
  }

  _serializeForm(submitButton, enctype) {
    const formData = new FormData(this._massInputForm);

    // manually add name and value of submit button to formData
    formData.append(submitButton.name, submitButton.value);

    if (enctype === 'application/x-www-form-urlencoded') {
      return new URLSearchParams(formData);
    } else if (enctype === 'multipart/form-data') {
      return formData;
    } else {
      throw new Error('Unsupported form enctype: ' + enctype);
    }
  }

  _reFocusAddCell() {
    const addCell = this._element.querySelector(MASS_INPUT_ADD_CELL_SELECTOR);
    if (!addCell) {
      return false;
    }

    const addCellInput = addCell.querySelector('input:not([type="hidden"])');
    if (addCellInput) {
      // Clearing of add-inputs is done in the backend
      addCellInput.focus();
    }
  }

  _reset() {
    this._element.classList.remove(MASS_INPUT_INITIALIZED_CLASS);
    this._massInputForm.removeEventListener('submit', this._massInputFormSubmitHandler);
    this._massInputForm.removeEventListener('keypress', this._keypressHandler);

    const buttons = this._getMassInputSubmitButtons();
    buttons.forEach((button) => {
      this._resetSubmitButton(button);
    });
  }
}
