import {
  createStyleMixin,
  draggableMixin,
  createProxy,
  proxyInputMixin,
  componentNameValidationMixin,
} from '../../mixins';
import textFieldMappings from './textFieldMappings';
import { compose } from '../../helpers';
import { getComponentName } from '../../helpers/componentHelpers';
import {
  inputFloatingLabelStyle,
  resetInputLabelPosition,
  resetInputOverrides,
  useHostExternalPadding,
} from '../../helpers/themeHelpers/resetHelpers';

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

const observedAttrs = ['type', 'label-type', 'copy-to-clipboard'];

const customMixin = (superclass) =>
  class TextFieldClass extends superclass {
    static get observedAttributes() {
      return observedAttrs.concat(superclass.observedAttributes || []);
    }

    icon;

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

    renderCopyToClipboard(shouldRender) {
      if (!shouldRender) {
        this.icon?.remove();
        return;
      }

      const iconInitConfig = {
        icon: 'vaadin:copy-o',
        title: 'Copy',
        style: 'cursor: pointer',
      };

      const iconCopiedConfig = {
        icon: 'vaadin:check-circle-o',
        title: 'Copied',
        style: 'cursor: initial',
      };

      this.icon = Object.assign(document.createElement('vaadin-icon'), {
        slot: 'suffix',
        ...iconInitConfig,
      });

      this.baseElement.appendChild(this.icon);
      this.icon.addEventListener('click', () => {
        navigator.clipboard.writeText(this.value);
        Object.assign(this.icon, iconCopiedConfig);

        // we want the icon to go back to the initial state after 5 seconds
        setTimeout(() => {
          Object.assign(this.icon, iconInitConfig);
        }, 5000);
      });
    }

    onLabelClick() {
      this.focus();
    }

    attributeChangedCallback(attrName, oldVal, newVal) {
      super.attributeChangeCallback?.(attrName, oldVal, newVal);

      // Vaadin doesn't allow to change the input type attribute.
      // We need the ability to do that, so we're overriding their
      // behavior with their private API.
      // When receiving a `type` attribute, we use their private API
      // to set it on the input.
      if (attrName === 'type') {
        this.baseElement._setType(newVal);
      }

      if (oldVal !== newVal) {
        if (attrName === 'label-type') {
          if (newVal === 'floating') {
            this.addEventListener('click', this.onLabelClick);
          } else {
            this.removeEventListener('click', this.onLabelClick);
          }
        } else if (attrName === 'copy-to-clipboard') {
          this.renderCopyToClipboard(newVal === 'true');
        }
      }
    }
  };

export const TextFieldClass = compose(
  createStyleMixin({
    mappings: textFieldMappings,
  }),
  draggableMixin,
  proxyInputMixin({ proxyProps: ['value', 'selectionStart'], useProxyTargets: true }),
  componentNameValidationMixin,
  customMixin
)(
  createProxy({
    slots: ['prefix', 'suffix'],
    wrappedEleName: 'vaadin-text-field',
    style: () => `
			:host {
				display: inline-block;
				max-width: 100%;
				padding: calc(var(${TextFieldClass.cssVarList.inputOutlineWidth}) + var(${
          TextFieldClass.cssVarList.inputOutlineOffset
        }));
        box-sizing: border-box;
			}
      :host(:is([readonly], [disabled])) ::slotted(:is(input, textarea):placeholder-shown) {
        opacity: 1;
      }

      vaadin-text-field[label-type="floating"]:not([focused])[readonly] > input:placeholder-shown {
        opacity: 0;
      }
      vaadin-text-field[label-type="floating"]:not([focused])[disabled] > input:placeholder-shown {
        opacity: 0;
      }
      ${resetInputLabelPosition('vaadin-text-field')}
			${useHostExternalPadding(TextFieldClass.cssVarList)}
			${resetInputOverrides('vaadin-text-field', TextFieldClass.cssVarList)}
      ${inputFloatingLabelStyle()}

      vaadin-text-field vaadin-icon {
        align-self: center;
      }
		`,
    excludeAttrsSync: ['tabindex', 'style'],
    componentName,
  })
);
