import {vlElement, define} from '/node_modules/vl-ui-core/dist/vl-core.js';

/**
 * VlCheckbox
 * @class
 * @classdesc De checkbox laat de gebruiker toe om een of meerdere opties te selecteren uit een lijst. Gebruik de checkbox in formulieren.
 *
 * @extends HTMLElement
 * @mixesvlElement
 *
 * @property {boolean} data-vl-block - Attribuut wordt gebruikt om ervoor te zorgen dat de checkbox getoond wordt als een block element en bijgevol de breedte van de parent zal aannemen.
 * @property {boolean} data-vl-disabled - Attribuut wordt gebruikt om te voorkomen dat de gebruiker de checkbox kan selecteren.
 * @property {boolean} data-vl-error - Attribuut wordt gebruikt om aan te duiden dat de checkbox verplicht is.
 * @property {boolean} data-vl-label - Attribuut wordt gebruikt om label te definiƫren via een attribuut ter vervanging van een slot element.
 * @property {boolean} data-vl-name - Attribuut wordt gebruikt om checkbox te identificeren.
 * @property {boolean} data-vl-single - Attribuut wordt gebruikt om alleen een checkbox te tonen zonder label.
 * @property {boolean} data-vl-switch - Attribuut wordt gebruikt om een checkbox variant te genereren met de stijl van een switch.
 * @property {boolean} data-vl-value - Attribuut wordt gebruikt om de checkbox waarde te bepalen.
 *
 * @see {@link https://www.github.com/milieuinfo/webcomponent-vl-ui-checkbox/releases/latest|Release notes}
 * @see {@link https://www.github.com/milieuinfo/webcomponent-vl-ui-checkbox/issues|Issues}
 * @see {@link https://webcomponenten.omgeving.vlaanderen.be/demo/vl-checkbox.html|Demo}
 */
export class VlCheckbox extends vlElement(HTMLElement) {
  static get formAssociated() {
    return true;
  }

  static get _observedAttributes() {
    return ['label', 'name', 'value', 'checked', 'switch'];
  }

  static get _observedChildClassAttributes() {
    return ['block', 'single', 'disabled', 'error'];
  }

  constructor() {
    super(`
      <style>
        @import '/src/style.css';
      </style>
    `);

    if (this.dataset.vlSwitch !== undefined) {
      this._shadow.append(this._template(`
        <div class="vl-checkbox--switch__wrapper">
          <input class="vl-checkbox--switch" type="checkbox" id="checkbox" name="checkbox" value="1" />
          <label class="vl-checkbox__label" for="checkbox">
            <span class="vl-checkbox--switch__label">
              <span aria-hidden="true"></span>
            </span>
            <span>
              <slot></slot>
            </span>
          </label>
        </div>
      `));
    } else {
      this._shadow.append(this._template(`
        <label id="label" class="vl-checkbox" for="checkbox">
          <input class="vl-checkbox__toggle" type="checkbox" id="checkbox" name="checkbox"/>
          <div class="vl-checkbox__label">
            <i class="vl-checkbox__box" aria-hidden="true"></i>
            <span>
              <slot></slot>
            </span>
          </div>
        </label>
      `));
    }

    if (this.attachInternals) {
      this._internals = this.attachInternals();
    } else {
      this._internals = null;
    }
  }

  connectedCallback() {
    this._inputElement.onchange = this._toggle;
    this._inputElement.oninput = (event) => event.stopPropagation();
    this._registerChangeEvent();
  }

  /**
   * Callback called when the form is reset.
   */
  formResetCallback() {
    this.checked = this.hasAttribute('checked');
  }

  /**
   * Returns a reference to the parent <form> element.
   *
   * @return {HTMLFormElement}
   */
  get form() {
    return this._internals?.form;
  }

  /**
   * Returns the element's current validity state.
   *
   * @return {ValidityState}
   */
  get validity() {
    return this._internals?.validity;
  }

  /**
   * Returns a localized message that describes the validation constraints that the control does not satisfy (if any). This is the empty string if the control is not a candidate for constraint validation (willvalidate is false), or it satisfies its constraints. This value can be set by the setCustomValidity method.
   *
   * @return {string}
   */
  get validationMessage() {
    return this._internals?.validationMessage;
  }

  /**
   * Returns whether the element is a candidate for constraint validation.
   *
   * @return {boolean}
   */
  get willValidate() {
    return this._internals?.willValidate;
  }

  /**
   * Geeft de waarde van het checkbox input element.
   *
   * @return {boolean}
   */
  get checked() {
    return this._inputElement.checked;
  }

  /**
   * Zet de waarde van het checkbox input element.
   *
   * @param {boolean} value
   */
  set checked(value) {
    this._inputElement.checked = value;
  }

  /**
   * Triggert een toggle van de checkbox zonder te klikken op de checkbox.
   *
   * @return {void}
   */
  toggle() {
    this._inputElement.click();
  }

  get _isSingle() {
    return this.hasAttribute('single');
  }

  get _classPrefix() {
    return 'vl-checkbox--';
  }

  get _inputElement() {
    return this._element.querySelector('input');
  }

  get _labelElement() {
    return this._element.querySelector('.vl-checkbox__label > span:not(.vl-checkbox--switch__label)');
  }

  get _labelSlotElement() {
    return this._element.querySelector('slot');
  }

  _toggle() {
    let checked;
    const parent = this.getRootNode().host;
    if (parent._checked && Array.isArray(parent._checked)) {
      const value = JSON.parse(this.value);
      if (parent._checked.indexOf(value) > -1) {
        parent._checked.splice(parent._checked.indexOf(value), 1);
      } else {
        parent._checked.push(value);
      }
      checked = parent._checked;
      parent.setAttribute('data-vl-checked', JSON.stringify(checked));
    } else {
      checked = this.checked;
    }
    parent.dispatchEvent(new CustomEvent('input', {detail: this.checked, bubbles: true, composed: true}));
  }

  _labelChangedCallback(oldValue, newValue) {
    this._labelSlotElement.remove();
    this._labelElement.textContent = newValue;
  }

  _nameChangedCallback(oldValue, newValue) {
    if (this._inputElement.name != newValue) {
      this._inputElement.name = newValue;
      this.setAttribute('name', newValue);
    }
  }

  _valueChangedCallback(oldValue, newValue) {
    this._inputElement.value = newValue;
  }

  _checkedChangedCallback(oldValue, newValue) {
    try {
      this._checked = JSON.parse(newValue);
    } catch (error) {
      this._checked = newValue != undefined;
    }

    if (!Array.isArray(this._checked)) {
      this._inputElement.checked = this._checked;
    } else if (this._checked.indexOf(JSON.parse(this._inputElement.value)) > -1) {
      this._inputElement.checked = true;
    }
  }

  _disabledChangedCallback(oldValue, newValue) {
    this._inputElement.disabled = newValue != undefined;
  }

  _singleChangedCallback(oldValue, newValue) {
    this._toggleClass(this._labelElement, newValue, 'vl-u-visually-hidden');
  }

  _isTextNode(node) {
    return node.nodeType === Node.TEXT_NODE;
  }

  _removeNode(node) {
    node.remove();
  }

  _registerChangeEvent() {
    this._inputElement.addEventListener('change', () => this.dispatchEvent(new Event('change')));
  }
}

define('vl-checkbox', VlCheckbox);