import { LitElement } from 'lit'

interface CustomEventMap {
  'btu-brightspot-element-connected': CustomEvent<unknown>
  'btu-brightspot-element-disconnected': CustomEvent<unknown>
  'btu-brightspot-element-error': CustomEvent<{ error: Error }>
  'btu-cig-cluster-ready': CustomEvent<unknown>
  'btu-copy-to-clipboard-copied': CustomEvent<unknown>
  'btu-dialog-closed': CustomEvent<unknown>
  'btu-dialog-ready': CustomEvent<unknown>
  'btu-eig-ready': CustomEvent<unknown>
  'btu-input-password-hide': CustomEvent<unknown>
  'btu-input-password-show': CustomEvent<unknown>
  'btu-table-checked-all': CustomEvent<unknown>
  'btu-widget-ready': CustomEvent<unknown>
  'btu-widget-toggle': CustomEvent<unknown>
}

interface CombinedEventMap extends HTMLElementEventMap, CustomEventMap {}

export declare class EventEmitterMixinInterface {
  emit: <K extends keyof CustomEventMap>(
    type: K,
    detail?: UnpackCustomEvent<CustomEventMap[K]>,
  ) => void
  addEventListener<K extends keyof CombinedEventMap>(
    type: K,
    listener: (this: HTMLElement, ev: CombinedEventMap[K]) => void,
    options?: boolean | AddEventListenerOptions,
  ): void
  removeEventListener<K extends keyof CombinedEventMap>(
    type: K,
    listener: (this: HTMLElement, ev: CombinedEventMap[K]) => void,
    options?: boolean | EventListenerOptions,
  ): void
}

export const EventEmitterMixin = <T extends Constructor<LitElement>>(
  superClass: T,
) => {
  class EventEmitterMixinClass extends superClass {
    connectedCallback() {
      super.connectedCallback()
      Promise.resolve().then(
        () => this.emit('btu-brightspot-element-connected', {}),
        (error) => this.emit('btu-brightspot-element-error', { error }),
      )
    }

    disconnectedCallback() {
      super.disconnectedCallback()
      this.emit('btu-brightspot-element-disconnected')
    }

    // Dispatch a CustomEvent with defaults.
    emit<K extends keyof CustomEventMap>(
      type: K,
      detail?: UnpackCustomEvent<CustomEventMap[K]>,
    ) {
      return this.dispatchEvent(
        new CustomEvent(type, {
          bubbles: true,
          cancelable: false,
          composed: true,
          detail,
        }),
      )
    }
  }
  return EventEmitterMixinClass as Constructor<EventEmitterMixinInterface> & T
}
