import { injectStyle } from '@descope-ui/common/components-helpers';
import { createBaseInputClass } from '../../../../baseClasses/createBaseInputClass';
import { forwardAttrs, getComponentName } from '../../../../helpers/componentHelpers';

export const componentName = getComponentName('mapping-item');

const inputAttrs = [
  'size',
  'bordered',
  'readonly',
  'full-width',
  'disabled',
  'st-error-message-icon',
  'st-error-message-icon-size',
  'st-error-message-icon-padding',
];

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

class MappingItem extends BaseInputClass {
  static get observedAttributes() {
    return [].concat(BaseInputClass.observedAttributes || [], inputAttrs, [
      'separator',
      'options',
      'invalid',
      'disabled',
    ]);
  }

  get separator() {
    return this.getAttribute('separator') || 'map to';
  }

  get value() {
    const attributeValue = this.attributeInput.value;
    const inputValue = this.valueInput.value;
    if (attributeValue && inputValue) {
      return { [attributeValue]: inputValue };
    }
    return null;
  }

  set value(mapping) {
    if (Object.entries(mapping).length !== 1) {
      // eslint-disable-next-line no-console
      console.error(
        'descope-mapping item expected expects only one key-value pair in the value but received: ',
        mapping
      );
      return;
    }
    const [attribute, value] = Object.entries(mapping)[0];
    this.valueInput.value = value;
    this.attributeInput.value = attribute;
    // The event needs to be dispatched to trigger the validation if setting the value externally
    this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
  }

  constructor() {
    super();

    this.attachShadow({ mode: 'open' }).innerHTML = `
      <div class="wrapper" part="wrapper">
        <descope-text-field required="true"></descope-text-field>
        <div class="separator" part="separator">${this.separator}</div>
        <descope-combo-box
          item-label-path="data-name"
          item-value-path="data-id"
          required="true"
        >
        </descope-combo-box>
        <descope-button variant="link" mode="primary">
          <vaadin-icon icon="vaadin:minus"></vaadin-icon>
        </descope-button>
      </div>
	`;

    injectStyle(
      `
        .wrapper {
          display: flex;
        }
        .separator {
          text-align: center;
          flex-shrink: 0;
        }
    `,
      this
    );

    this.valueInput = this.shadowRoot.querySelector('descope-text-field');
    this.attributeInput = this.shadowRoot.querySelector('descope-combo-box');
    this.inputs = [this.valueInput, this.attributeInput];
    this.removeButton = this.shadowRoot.querySelector('descope-button');

    forwardAttrs(this, this.valueInput, {
      includeAttrs: inputAttrs,
    });
    forwardAttrs(this, this.attributeInput, {
      includeAttrs: [...inputAttrs, 'options'],
      mapAttrs: { options: 'data' },
    });
    forwardAttrs(this, this.removeButton, {
      includeAttrs: ['disabled'],
    });
  }

  initRemoveButton() {
    this.removeButton.addEventListener('click', () =>
      this.dispatchEvent(new CustomEvent('mapping-item-removed', { bubbles: true, composed: true }))
    );
  }

  init() {
    super.init?.();
    this.initRemoveButton();
  }

  getValidity() {
    const attributeValue = this.attributeInput.value;
    const inputValue = this.valueInput.value;
    if (!attributeValue || !inputValue) {
      return { badInput: true };
    }
    return {};
  }

  reportValidity() {
    this.inputs.forEach((input) => input.reportValidity());
  }

  focus() {
    const focusedElement =
      this.checkValidity() || !this.valueInput.checkValidity()
        ? this.valueInput
        : this.attributeInput;
    focusedElement.focus();
  }

  handleSeparatorChange() {
    this.shadowRoot.querySelector('.separator').textContent = this.separator;
  }

  #handleInvalidMapping(invalid) {
    if (invalid === 'true') {
      const inputValue = this.valueInput.value;
      if (!inputValue) {
        this.valueInput.setAttribute('invalid', 'true');
        this.valueInput.setAttribute('error-message', this.defaultErrorMsgValueMissing);
      }

      const attributeValue = this.attributeInput.value;
      if (!attributeValue) {
        this.attributeInput.setAttribute('invalid', 'true');
        this.attributeInput.setAttribute('error-message', this.defaultErrorMsgValueMissing);
      }
    }
  }

  attributeChangedCallback(attrName, oldValue, newValue) {
    super.attributeChangedCallback?.(attrName, oldValue, newValue);

    if (attrName === 'separator') {
      this.handleSeparatorChange();
    }

    if (attrName === 'invalid') {
      this.#handleInvalidMapping(newValue);
    }
  }
}

export default MappingItem;
