import React from 'react';
import ReactDOM from 'react-dom/client';
import { assign } from 'lodash';
import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import AutoCompleteFC from './components/AutoComplete';

export interface AutoCompleteOptions<Multi extends boolean = boolean> {
  multi?: Multi;
  defaultItems?: string;
  width?: string;
  height?: string;
  listStyle?: any;
  allowEmptyQuery?: boolean;
  freeSolo?: boolean;
  freeSoloConverter?: any;
  onChange?: (value) => any;
  items?: () => Promise<any[]>;
  render?: () => string;
  text?: () => string;
}

export default class AutoComplete extends DOMAttachedObject {
  static DOMAttachKey = 'ucwAutoCompleteInstance';
  ref = null;
  container = document.createElement('div');
  options: AutoCompleteOptions;
  changeListener = [
    (val) => this.$dom.val(val),
  ];

  constructor($dom, options = {}) {
    super($dom);
    this.options = {
      items: async () => [],
      render: () => '',
      text: () => null,
      multi: false,
      ...options,
    };
    this.clear = this.clear.bind(this);
    this.onChange = this.onChange.bind(this);
    this.attach = this.attach.bind(this);
    this.open = this.open.bind(this);
    this.close = this.close.bind(this);
    this.value = this.value.bind(this);
    this.detach = this.detach.bind(this);
    this.focus = this.focus.bind(this);
    this.$dom.addClass('autocomplete-dummy').after(this.container);
    // Note: use `setTimeout(fn, 0)` to ensure that code is executed after browser autofill
    // also see https://stackoverflow.com/a/779785/13553984
    setTimeout(() => this.attach(), 0);
  }

  clear(clearValue = true) {
    if (!this.ref) return;
    if (clearValue) this.ref.clear();
    else this.ref.closeList();
  }

  onChange(val: string | ((v: string) => any)) {
    if (typeof val === 'string') this.changeListener.forEach((f) => f(val));
    else this.changeListener.push(val);
  }

  attach() {
    const value = this.$dom.val();
    ReactDOM.createRoot(this.container).render(
      <AutoCompleteFC
        ref={(ref) => { this.ref = ref; }}
        height="34px"
        queryItems={this.options.items}
        renderItem={this.options.render}
        itemText={this.options.text}
        selectedKeys={value.split(',').map((i) => i.trim())}
        onChange={this.onChange}
        multi={this.options.multi}
        freeSolo={this.options.multi}
      />,
    );
  }

  open() {
    if (!this.ref) return;
    this.ref.triggerQuery();
  }

  close() {
    if (!this.ref) return;
    this.ref.closeList();
  }

  value(): any {
    if (this.options.multi) return this.$dom.val();
    return this.ref?.getSelectedItems()[0] ?? null;
  }

  detach() {
    if (this.detached) return;
    super.detach();
    ReactDOM.unmountComponentAtNode(this.container);
    this.$dom.removeClass('autocomplete-dummy');
    this.container.parentNode.removeChild(this.container);
  }

  focus() {
    this.ref.focus();
  }
}

assign(AutoComplete, DOMAttachedObject);
window.Hydro.components.autocomplete = AutoComplete;
