// SPDX-FileCopyrightText: 2022 Felix Hamann <felix.hamann@campus.lmu.de>,Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

import { Utility } from '../../core/utility';
import { EventManager, EventWrapper, EVENT_TYPE } from '../../lib/event-manager/event-manager';

const INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR = '.interactive-fieldset__target';

const INTERACTIVE_FIELDSET_INITIALIZED_CLASS = 'interactive-fieldset--initialized';
const INTERACTIVE_FIELDSET_CHILD_SELECTOR = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled])';

let fieldsetCounter = 0;

@Utility({
  selector: '[uw-interactive-fieldset]',
})
export class InteractiveFieldset {
  fieldsetIdent = (fieldsetCounter++).toString();

  _element;

  _eventManager;

  conditionalInput;
  conditionalValue;
  target;
  childInputs;
  negated;

  constructor(element) {
    if (!element) {
      throw new Error('Interactive Fieldset utility cannot be setup without an element!');
    }

    this._element = element;

    this._eventManager = new EventManager();

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

    // if (this._element.querySelector('[uw-interactive-fieldset]')) {
    //   return false;
    // }

    // param conditionalInput
    if (!this._element.dataset.conditionalInput) {
      throw new Error('Interactive Fieldset needs a selector for a conditional input!');
    }
    this.conditionalInput = document.querySelector('#' + this._element.dataset.conditionalInput);
    if (!this.conditionalInput) {
      // abort if form has no required inputs
      throw new Error('Couldn\'t find the conditional input. Aborting setup for interactive fieldset.');
    }

    // param conditionalValue
    if (!('conditionalValue' in this._element.dataset) && !this._isCheckbox()) {
      throw new Error('Interactive Fieldset needs a conditional value!');
    }
    this.conditionalValue = this._element.dataset.conditionalValue;

    this.negated = 'conditionalNegated' in this._element.dataset;

    this.target = this._element.closest(INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR);
    if (!this.target || this._element.matches(INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR)) {
      this.target = this._element;
    }

    this.childInputs = Array.from(this._element.querySelectorAll(INTERACTIVE_FIELDSET_CHILD_SELECTOR)).filter(child => child.closest('[uw-interactive-fieldset]') === this._element);

    // add event listener
    this._eventManager.registerNewMutationObserver(this._updateVisibility.bind(this), this.conditionalInput, { attributes: true, attributeFilter: ['data-interactive-fieldset-hidden'] });
    const inputEvent = new EventWrapper(EVENT_TYPE.INPUT, this._updateVisibility.bind(this), this.conditionalInput);
    this._eventManager.registerNewListener(inputEvent);
    // mark as initialized
    this._element.classList.add(INTERACTIVE_FIELDSET_INITIALIZED_CLASS);
  }

  start() {
    // initial visibility update
    this._updateVisibility();
  }

  destroy() {
    this._eventManager.cleanUp();
    this._element.classList.remove(INTERACTIVE_FIELDSET_INITIALIZED_CLASS);
  }

  _updateVisibility() {
    const active = this._matchesConditionalValue() && !this.conditionalInput.dataset.interactiveFieldsetHidden;

    let hiddenBy = (this.target.dataset.interactiveFieldsetHiddenBy || '').split(',').filter(str => str.length !== 0);

    if (active)
      hiddenBy = hiddenBy.filter(ident => ident !== this.fieldsetIdent);
    else if (hiddenBy.every(ident => ident !== this.fieldsetIdent))
      hiddenBy = [ ...hiddenBy, this.fieldsetIdent ];

    if (hiddenBy.length !== 0) {
      this.target.dataset.interactiveFieldsetHiddenBy = hiddenBy.join(',');
      this.target.classList.add('hidden');
    } else {
      delete this.target.dataset['interactiveFieldsetHiddenBy'];
      this.target.classList.remove('hidden');
    }

    this.childInputs.forEach((el) => this._updateChildVisibility(el, active));
  }

  _updateChildVisibility(el, active) {
    el.disabled = !active;
    
    if (active) {
      delete el.dataset.interactiveFieldsetHidden;
    } else {
      el.dataset.interactiveFieldsetHidden = true;
    }
  }

  _matchesConditionalValue() {
    let matches;

    if (this._isCheckbox()) {
      matches = this.conditionalInput.checked === true;
    } else if (this._isRadio()) {
      const radios = Array.from(this.conditionalInput.querySelectorAll('input[type=radio]'));
      matches = radios.some(radio => radio.checked && radio.value === this.conditionalValue);
    } else {
      matches = this.conditionalInput.value === this.conditionalValue;
    }

    if (this.negated) {
      return !matches;
    } else {
      return matches;
    }
  }

  _isCheckbox() {
    return this.conditionalInput.getAttribute('type') === 'checkbox';
  }
  
  _isRadio() {
    return !!this.conditionalInput.querySelector('input[type=radio]');
  }
}
