import {
  createStyleMixin,
  draggableMixin,
  createProxy,
  proxyInputMixin,
  componentNameValidationMixin,
} from '../../../mixins';
import { TextClass } from '@descope-ui/descope-text/class';
import { compose } from '../../../helpers';
import { forwardAttrs, getComponentName, syncAttrs } from '../../../helpers/componentHelpers';
import { componentName as descopeInternalComponentName } from './descope-mappings-field-internal/MappingsFieldInternal';

export const componentName = getComponentName('mappings-field');

const customMixin = (superclass) =>
  class MappingsFieldMixinClass extends superclass {
    get defaultValues() {
      const defaultValuesAttr = this.getAttribute('default-values');
      if (defaultValuesAttr) {
        try {
          return JSON.parse(defaultValuesAttr);
        } catch (e) {
          // eslint-disable-next-line no-console
          console.error('could not parse data string from attribute "default-values" -', e.message);
        }
      }
      return [];
    }

    setDefaultValues() {
      const initialDefaultValues = this.defaultValues;
      if (Object.keys(initialDefaultValues).length > 0) {
        this.inputElement.value = initialDefaultValues;
      }
    }

    init() {
      super.init?.();
      const template = document.createElement('template');

      template.innerHTML = `
      <${descopeInternalComponentName}
        tabindex="-1"
      ></${descopeInternalComponentName}>
      `;

      this.baseElement.appendChild(template.content.cloneNode(true));

      this.inputElement = this.shadowRoot.querySelector(descopeInternalComponentName);

      forwardAttrs(this, this.inputElement, {
        includeAttrs: [
          'size',
          'full-width',
          'label-value',
          'label-attr',
          'button-label',
          'separator',
          'options',
          'default-values',
          'invalid',
          'readonly',
          'disabled',
          'st-error-message-icon',
          'st-error-message-icon-size',
          'st-error-message-icon-padding',
        ],
      });

      // This is required since when we remove the invalid attribute from the internal mappings field,
      // we want to reflect the change in the parent component
      syncAttrs(this, this.inputElement, { includeAttrs: ['invalid'] });

      this.setDefaultValues();
    }
  };

const {
  host,
  helperText,
  errorMessage,
  mappingItem,
  labels,
  labelsText,
  valueLabel,
  attrLabel,
  separator,
  labelsContainer,
} = {
  host: { selector: () => ':host' },
  helperText: { selector: '::part(helper-text)' },
  errorMessage: { selector: '::part(error-message)' },
  mappingItem: { selector: 'descope-mapping-item::part(wrapper)' },
  labels: { selector: 'descope-mappings-field-internal [part="labels"] descope-text' },
  labelsText: {
    selector: 'descope-mappings-field-internal [part="labels"] descope-text::part(text-wrapper)',
  },
  valueLabel: { selector: 'descope-mappings-field-internal [part="labels"] [part="value-label"]' },
  attrLabel: { selector: 'descope-mappings-field-internal [part="labels"] [part="attr-label"]' },
  separator: { selector: 'descope-mapping-item::part(separator)' },
  labelsContainer: { selector: 'descope-mappings-field-internal [part="labels"]' },
};

export const MappingsFieldClass = compose(
  createStyleMixin({
    mappings: {
      hostWidth: { ...host, property: 'width' },
      hostDirection: { ...host, property: 'direction' },
      // we apply font-size also on the host so we can set its width with em
      fontSize: [{}, host, { ...separator, property: 'margin-top' }],
      fontFamily: [helperText, errorMessage, labels],
      separatorFontSize: { ...separator, property: 'font-size' },
      labelsFontSize: { ...labelsText, property: 'font-size' },
      labelsLineHeight: [
        { ...labelsText, property: 'line-height' },
        { ...labels, property: 'line-height' },
      ],
      labelTextColor: [
        { ...labels, property: TextClass.cssVarList.textColor },
        { ...separator, property: 'color' },
      ],
      itemMarginBottom: { ...mappingItem, property: 'margin-bottom' },
      valueLabelMinWidth: { ...valueLabel, property: 'min-width' },
      attrLabelMinWidth: { ...attrLabel, property: 'min-width' },
      labelsMarginBottom: { ...labelsContainer, property: 'margin-bottom' },
      separatorWidth: {},
      removeButtonWidth: {},
    },
  }),
  draggableMixin,
  proxyInputMixin({
    proxyProps: ['value', 'selectionStart'],
    inputEvent: 'input',
    proxyParentValidation: true,
  }),
  componentNameValidationMixin,
  customMixin
)(
  createProxy({
    slots: [],
    wrappedEleName: 'vaadin-custom-field',
    style: () => `
      :host {
        display: inline-flex;
        max-width: 100%;
        direction: ltr;
      }
      vaadin-custom-field {
        line-height: unset;
        width: 100%;
      }

      descope-mappings-field-internal [part="labels"] {
        display: none;
        grid-template-columns: 1fr var(${MappingsFieldClass.cssVarList.separatorWidth}) 1fr var(${MappingsFieldClass.cssVarList.removeButtonWidth});
      }

      descope-mappings-field-internal [part="labels"] [part="value-label"],
      descope-mappings-field-internal [part="labels"] [part="attr-label"] {
        ${TextClass.cssVarList.fontWeight}: 500;
      }

      descope-mappings-field-internal [part="labels"] [part="value-label"] {
        grid-column: 1 / span 1;
      }

      descope-mappings-field-internal [part="labels"] [part="attr-label"] {
        grid-column: 3 / span 1;
      }

      descope-mapping-item::part(wrapper) {
        display: grid;
        grid-template-columns: 1fr var(${MappingsFieldClass.cssVarList.separatorWidth}) 1fr var(${MappingsFieldClass.cssVarList.removeButtonWidth});
      }
    `,
    excludeAttrsSync: [
      'tabindex',
      'label-value',
      'label-attr',
      'button-label',
      'options',
      'error-message',
      'style',
    ],
    componentName,
  })
);

export default MappingsFieldClass;
