import { LitElement } from 'lit'

export declare class WrappingMixinInterface {
  _child: HTMLElement
  find(): HTMLElement | null
}

/*
  #wrap an element with an instance of the provided Class and
  execute the callback with a reference to the new instance.
*/
export const wrap = (
  element: HTMLElement,
  ComponentClass: Class<HTMLElement>,
  createdCb?: (component: HTMLElement) => void,
) => {
  // Already wrapped?
  if (element.parentElement instanceof ComponentClass) return

  // Create the new component instance, run provided callback.
  const wc = new ComponentClass()
  if (createdCb) {
    createdCb(wc)
  }

  // Wrap.
  element.parentNode?.insertBefore(wc, element)
  wc.appendChild(element)
}

export const WrappingMixin = <T extends Constructor<LitElement>>(
  superClass: T,
) => {
  class WrappingMixinClass extends superClass {
    _child?: HTMLElement
    #observer?: MutationObserver

    protected get child() {
      return this._child
    }

    protected set child(_c) {
      this._child = _c
    }

    /*
      Must override this method in a subclass.
    */
    find(): HTMLElement | null {
      return null // No-Op
    }

    connectedCallback() {
      super.connectedCallback()
      const found = this.find()
      if (found) {
        this.child = found
      }
      this.#observer = new MutationObserver(async (mutations) => {
        const found = this.find()
        if (found) {
          this.child = found
        }
      })
      this.#observer.observe(this.renderRoot, {
        childList: true,
        subtree: false,
      })
    }

    disconnectedCallback() {
      super.disconnectedCallback()
      this.#observer?.disconnect()
    }
  }
  return WrappingMixinClass as Constructor<WrappingMixinInterface> & T
}
