import { createBaseInputClass } from '../../../../baseClasses/createBaseInputClass';
import {
  getComponentName,
  forwardAttrs,
  observeAttributes,
} from '../../../../helpers/componentHelpers';

export const componentName = getComponentName('saml-group-mappings-internal');

const BaseInputClass = createBaseInputClass({ componentName, baseSelector: '' });

class SamlGroupMappingsInternal extends BaseInputClass {
  static get observedAttributes() {
    return ['invalid'].concat(BaseInputClass.observedAttributes || []);
  }

  constructor() {
    super();

    this.innerHTML = `
      <descope-text-field variant="body2" bordered="true"></descope-text-field>
      <descope-mappings-field></descope-mappings-field>
    `;

    this.groupInputElement = this.querySelector('descope-text-field');
    this.mappingsElement = this.querySelector('descope-mappings-field');
  }

  resetInvalidIndication() {
    this.removeAttribute('invalid');
  }

  handleMappingsInvalidChange(changedAttributes) {
    if (changedAttributes.includes('invalid')) {
      if (!this.mappingsElement.hasAttribute('invalid')) {
        this.resetInvalidIndication();
      }
    }
  }

  initFocusHandler() {
    // This event listener needs to be placed before the super.init() call
    this.addEventListener('focus', (e) => {
      // we want to ignore focus events we are dispatching
      if (e.isTrusted) {
        const focusedElement = this.mappingsElement.checkValidity()
          ? this.groupInputElement
          : this.mappingsElement;
        focusedElement.focus();
      }
    });
  }

  init() {
    // This needs to be placed before the super.init() call to work
    this.initFocusHandler();

    super.init?.();

    forwardAttrs(this, this.groupInputElement, {
      mapAttrs: { 'label-group': 'label' },
      includeAttrs: ['size', 'label-group', 'readonly', 'disabled'],
    });

    forwardAttrs(this, this.mappingsElement, {
      includeAttrs: [
        'size',
        'full-width',
        'label-value',
        'label-attr',
        'button-label',
        'separator',
        'options',
        'readonly',
        'disabled',
        'data-errormessage-pattern-mismatch',
      ],
    });

    // Observing the invalid attribute of the mappings field to reset the invalid state for this component.
    // When an invalid item turns valid, the mappings field will remove the invalid attribute, and at this component
    // level, we need to remove the invalid attribute as well to be able to mark the component as invalid the next time
    observeAttributes(this.mappingsElement, this.handleMappingsInvalidChange.bind(this), {
      includeAttrs: ['invalid'],
    });
  }

  get value() {
    return {
      group: this.groupInputElement.value,
      mappings: this.mappingsElement.value,
    };
  }

  set value(value) {
    if (value?.group && typeof value.group === 'string') {
      this.groupInputElement.value = value.group;
    }
    if (Array.isArray(value?.mappings)) {
      this.mappingsElement.value = value.mappings;
    }
  }

  getValidity() {
    if (!this.groupInputElement.checkValidity()) {
      return this.groupInputElement.validity;
    }
    if (!this.mappingsElement.checkValidity()) {
      return this.mappingsElement.validity;
    }

    return {};
  }

  #handleInvalidState(isInvalid) {
    if (isInvalid) {
      if (!this.groupInputElement.checkValidity()) {
        this.groupInputElement.setAttribute('invalid', 'true');
        return;
      }

      if (!this.mappingsElement.checkValidity()) {
        this.mappingsElement.setAttribute('invalid', 'true');
      }
    }
  }

  attributeChangedCallback(attrName, oldValue, newValue) {
    super.attributeChangedCallback?.(attrName, oldValue, newValue);
    if (attrName === 'invalid') {
      this.#handleInvalidState(newValue === 'true');
    }
  }
}

export default SamlGroupMappingsInternal;
