import { ComboBoxClass } from '@descope-ui/descope-combo-box/class';
import { injectStyle } from '@descope-ui/common/components-helpers';
import { createBaseClass } from '../../baseClasses/createBaseClass';
import { compose } from '../../helpers';
import { getComponentName, observeAttributes } from '../../helpers/componentHelpers';
import { componentNameValidationMixin, createStyleMixin, draggableMixin } from '../../mixins';
import { TextFieldClass } from '../descope-text-field/TextFieldClass';

export const componentName = getComponentName('security-questions-setup');

const attrsToSync = [
  'full-width',
  'readonly',
  'size',
  'label-type',
  'question-label',
  'question-placeholder',
  'question-data-errormessage-value-missing',
  'answer-label',
  'answer-placeholder',
  'answer-data-errormessage-value-missing',
  'st-error-message-icon',
  'st-error-message-icon-size',
  'st-error-message-icon-padding',
];

const attrsToReRender = ['count', 'questions'];
class RawSecurityQuestionsSetup extends createBaseClass({ componentName, baseSelector: 'div' }) {
  constructor() {
    super();

    this.attachShadow({ mode: 'open' }).innerHTML = `
    <div></div>
		`;

    injectStyle(
      `
      :host {
        display: inline-flex;
      }

      div {
        display: flex;
        flex-direction: column;
      }
    `,
      this
    );
  }

  get count() {
    return Number(this.getAttribute('count')) || 0;
  }

  #renderQuestions() {
    const res = Array.from({ length: this.count }).map(
      (_, index) =>
        // <!--html-->
        `
      <div class="question-wrapper">
      <descope-combo-box
        data-id="${index}"
        item-label-path="data-name"
        item-value-path="data-id"
        bordered="true"
        required="true"
        clear-button-visible="true"
      ></descope-combo-box>

      <descope-text-field
        data-id="${index}"
        required="true"
        bordered="true"
        minlength="2"
      ></descope-text-field>
    </div>
      `
      // <!--!html-->
    );

    this.baseElement.innerHTML = res.join(
      '<spacer></spacer><descope-divider></descope-divider><spacer></spacer>'
    );

    this.comboBoxes.forEach((el) => {
      el.addEventListener('change', (e) => {
        this.updateRemainingCombosData(e.target);
        const AttachedTextField = this.getAttachedTextField(e.target);

        AttachedTextField.value = '';
      });

      el.data = this.data;
    });

    this.#syncAttrs(attrsToSync);
  }

  getAttachedTextField(combo) {
    const selector = `descope-text-field[data-id="${combo.getAttribute('data-id')}"]`;

    return this.baseElement.querySelector(selector);
  }

  filterData(comboValue) {
    const res = this.data.filter(
      (item) => item.value === comboValue || !this.selectedQuestionIds.includes(item.value)
    );

    return res;
  }

  reportValidity() {
    this.inputs.reverse().forEach((el) => el.reportValidity());

    return this.checkValidity();
  }

  checkValidity() {
    return [...this.comboBoxes, ...this.textFields].every((el) => el.checkValidity());
  }

  updateRemainingCombosData(sourceCombo) {
    this.comboBoxes
      .filter((combo) => combo !== sourceCombo)
      .forEach((combo) => {
        combo.data = this.filterData(combo.value);
      });
  }

  get questions() {
    try {
      return JSON.parse(this.getAttribute('questions')) || [];
    } catch (e) {
      // eslint-disable-next-line no-console
      console.error(componentName, 'Error parsing questions attribute', e);
      return [];
    }
  }

  // this returns the structure expected by the combo box
  get data() {
    return this.questions.map(({ id, text }) => ({ value: id, label: text }));
  }

  get comboBoxes() {
    return Array.from(this.baseElement.querySelectorAll('descope-combo-box'));
  }

  get textFields() {
    return Array.from(this.baseElement.querySelectorAll('descope-text-field'));
  }

  get inputs() {
    return Array.from(this.baseElement.querySelectorAll('descope-combo-box, descope-text-field'));
  }

  get selectedQuestionIds() {
    return this.comboBoxes.map((el) => el.value);
  }

  get value() {
    return this.comboBoxes.map((combo) => {
      const id = combo.value;
      const answer = this.getAttachedTextField(combo).value;

      return { id, answer };
    });
  }

  set value(val = []) {
    val.forEach((item, index) => {
      const combo = this.comboBoxes[index];
      const textField = this.getAttachedTextField(combo);

      combo.value = item.id;
      textField.value = item.answer;
    });
  }

  #updateAttribute(ele, attrName, value) {
    if (value === null) ele.removeAttribute(attrName);
    else ele.setAttribute(attrName, value);
  }

  #syncAttrs(attrs) {
    const componentMap = {
      question: this.comboBoxes,
      answer: this.textFields,
    };

    attrs.forEach((attr) => {
      const [maybeComponentType, ...rest] = attr.split('-');
      const elements = componentMap[maybeComponentType] || this.inputs;
      const attrName = componentMap[maybeComponentType] ? rest.join('-') : attr;

      elements.forEach((el) => {
        this.#updateAttribute(el, attrName, this.getAttribute(attr));
      });
    });
  }

  init() {
    super.init?.();
    // render new components
    observeAttributes(
      this,
      () => {
        this.#renderQuestions();
      },
      { includeAttrs: attrsToReRender }
    );

    // update existing components
    observeAttributes(this, this.#syncAttrs.bind(this), {
      includeAttrs: attrsToSync,
    });
  }
}

export const SecurityQuestionsSetupClass = compose(
  createStyleMixin({
    mappings: {
      hostWidth: [{ selector: () => ':host', property: 'width' }, { property: 'width' }],
      hostDirection: [
        { selector: () => ':host', property: 'direction' },
        {
          selector: () => ComboBoxClass.componentName,
          property: ComboBoxClass.cssVarList.hostDirection,
        },
        {
          selector: () => TextFieldClass.componentName,
          property: TextFieldClass.cssVarList.hostDirection,
        },
      ],
      gap: { selector: () => 'div', property: 'gap' },
    },
  }),
  draggableMixin,
  componentNameValidationMixin
)(RawSecurityQuestionsSetup);
