import { compose } from '../../helpers';
import {
  forwardAttrs,
  getComponentName,
  observeAttributes,
  observeChildren,
} from '../../helpers/componentHelpers';
import { resetInputLabelPosition } from '../../helpers/themeHelpers/resetHelpers';
import {
  componentNameValidationMixin,
  createProxy,
  createStyleMixin,
  draggableMixin,
  proxyInputMixin,
} from '../../mixins';
import textFieldMappings from '../descope-text-field/textFieldMappings';
import { componentName as childNodeName, RadioButtonClass } from './RadioButtonClass';

export const componentName = getComponentName('radio-group');

const RadioGroupMixin = (superclass) =>
  class RadioGroupMixinClass extends superclass {
    #renderItem = ({ value, label }) =>
      `<descope-radio-button value="${value}">${label}</descope-radio-button>`;

    #data;

    constructor() {
      super();

      // we are overriding vaadin children getter so it will run on our custom elements
      Object.defineProperty(this.baseElement, 'children', {
        get: () => this.querySelectorAll(childNodeName),
      });

      // we are overriding vaadin __filterRadioButtons so it will run on our custom elements
      this.baseElement.__filterRadioButtons = (nodes) =>
        nodes.filter((node) => node.localName === childNodeName);

      // vaadin radio group missing some input properties
      this.baseElement.setCustomValidity = () => {};
    }

    get items() {
      return this.shadowRoot.querySelector('slot').assignedElements();
    }

    get size() {
      return this.getAttribute('size');
    }

    get data() {
      if (this.#data) return this.#data;

      const dataAttr = this.getAttribute('data');

      if (dataAttr) {
        try {
          const data = JSON.parse(dataAttr);
          if (this.isValidDataType(data)) {
            return data;
          }
        } catch (e) {
          // eslint-disable-next-line no-console
          console.error('could not parse data string from attribute "data" - ', e.message);
        }
      }

      return [];
    }

    set data(data) {
      if (this.isValidDataType(data)) {
        this.#data = data;
        this.renderItems();
      }
    }

    get defaultValue() {
      return this.getAttribute('default-value');
    }

    isValidDataType(data) {
      const isValid = Array.isArray(data);
      if (!isValid) {
        // eslint-disable-next-line no-console
        console.error('data must be an array, received:', data);
      }

      return isValid;
    }

    getItemsTemplate() {
      return this.data?.reduce?.((acc, item) => acc + (this.#renderItem?.(item || {}) || ''), '');
    }

    renderItems() {
      const template = this.getItemsTemplate();
      if (template) this.innerHTML = template;
    }

    init() {
      super.init?.();

      this.inputElement = this.baseElement;

      this.renderItems();

      observeAttributes(this, this.renderItems.bind(this), { includeAttrs: ['data'] });

      Object.defineProperty(this.baseElement, 'validity', {
        get: () => ({ valueMissing: !this.baseElement.checkValidity() }),
      });

      forwardAttrs(this, this.baseElement, {
        includeAttrs: ['layout'],
        mapAttrs: { layout: 'theme' },
      });

      setTimeout(() => {
        if (this.defaultValue) {
          this.value = this.defaultValue;
        }
      });

      // we want new items to get the size
      observeChildren(this, ({ addedNodes }) => {
        addedNodes.forEach((node) => {
          node.setAttribute('size', this.size);
        });
      });

      observeAttributes(
        this,
        () => {
          this.items.forEach((item) => {
            item.setAttribute('size', this.size);
          });
        },
        {
          includeAttrs: ['size'],
        }
      );
    }
  };

export const RadioGroupClass = compose(
  createStyleMixin({
    mappings: {
      ...textFieldMappings,
      buttonsSpacing: { selector: '::part(group-field)', property: 'justify-content' },
      buttonsRowGap: { selector: '::part(group-field)', property: 'row-gap' },
      buttonsColumnGap: { selector: '::part(group-field)', property: 'column-gap' },
      itemsLabelColor: {
        selector: () => `::slotted(${RadioButtonClass.componentName})`,
        property: RadioButtonClass.cssVarList.labelTextColor,
      },
    },
  }),
  draggableMixin,
  proxyInputMixin({ proxyProps: ['setSelectionRange'] }),
  componentNameValidationMixin,
  RadioGroupMixin
)(
  createProxy({
    slots: ['', 'prefix'],
    wrappedEleName: 'vaadin-radio-group',
    style: () => `
    :host {
      display: inline-block;
      max-width: 100%;
      box-sizing: border-box;
    }

    vaadin-radio-group {
      padding: 0;
      width: 100%;
    }

    ${resetInputLabelPosition('vaadin-radio-group')}
    `,

    excludeAttrsSync: ['tabindex', 'size', 'data', 'direction', 'style'],
    componentName,
    includeForwardProps: ['value'],
  })
);
