import { PktElement } from '@/base-elements/element'
import { classMap } from 'lit/directives/class-map.js'
import { customElement, property, state } from 'lit/decorators.js'
import { createRef, ref, Ref } from 'lit/directives/ref.js'
import { html, nothing } from 'lit'
import { PktSlotController } from '@/controllers/pkt-slot-controller'
import { TPktSize } from '@/types/size'
import '@/components/icon'

// Allow global override of animation assets path
window.pktAnimationPath =
  window.pktAnimationPath || 'https://punkt-cdn.oslo.kommune.no/latest/animations/'

export type TPktLoaderVariant = 'blue' | 'rainbow' | 'shapes'

export interface IPktLoader {
  /**
   * The `delay` prop controls how much time the loading should be given before the loader is displayed.
   * This is handy for situations where the load time might be so short that loader is not necessary.
   * Delay time is in milliseconds.
   */
  delay?: number
  /**
   * The `inline` prop decides whether the loader should be displayed inline or not.
   */
  inline?: boolean
  /**
   * The boolean 'isLoading' decides whether the loader or the children will be displayed.
   * If set to false, the children will be displayed.
   */
  isLoading?: boolean
  /**
   * The message to display when the loader is loading.
   */
  message?: string | null
  /**
   * The size of the loader. Default is "medium".
   */
  size?: TPktSize
  /**
   * The variant of the loader. Default is "shapes" which is the OSLO wave loader.
   * Other variants are "blue" and "rainbow" which are spinner variants.
   */
  variant?: TPktLoaderVariant
  /**
   * Override path to loading animations.
   */
  loadingAnimationPath?: string
}

@customElement('pkt-loader')
export class PktLoader extends PktElement implements IPktLoader {
  defaultSlot: Ref<HTMLElement> = createRef()

  constructor() {
    super()
    this.slotController = new PktSlotController(this, this.defaultSlot)
  }

  @property({ type: Number }) delay: number = 0
  @property({ type: Boolean }) inline: boolean = false
  @property({ type: Boolean }) isLoading: boolean = true
  @property({ type: String }) message: string | null = null
  @property({ type: String }) size: TPktSize = 'medium'
  @property({ type: String }) variant: TPktLoaderVariant = 'shapes'
  @property({ type: String }) loadingAnimationPath: string | undefined = window.pktAnimationPath

  @state() private _shouldDisplayLoader: boolean = false

  connectedCallback() {
    super.connectedCallback()
    this._shouldDisplayLoader = this.delay === 0

    if (this.delay > 0) {
      this.setupLoader()
    }
  }

  updated(changedProperties: Map<string, unknown>) {
    if (changedProperties.has('delay')) {
      this.setupLoader()
    }
  }

  render() {
    const classes = classMap({
      'pkt-loader': true,
      [`pkt-loader--${this.inline ? 'inline' : 'box'}`]: true,
      [`pkt-loader--${this.size}`]: true,
    })

    const slotClasses = classMap({
      'pkt-contents': true,
      'pkt-hide': this.isLoading,
    })

    return html`<div role="status" aria-live="polite" .aria-busy=${this.isLoading} class=${classes}>
      ${this.isLoading && this._shouldDisplayLoader
        ? html`<div class="pkt-loader__spinner">
            <pkt-icon
              name=${this.getVariant(this.variant)}
              path=${this.loadingAnimationPath}
              class="pkt-loader__svg pkt-loader__${this.variant}"
            ></pkt-icon>
            ${this.message && html`<p>${this.message}</p>`}
          </div>`
        : nothing}
      <div class=${slotClasses} ${ref(this.defaultSlot)}></div>
    </div>`
  }

  private getVariant(variant: TPktLoaderVariant): string {
    switch (variant) {
      case 'blue':
        return 'spinner-blue'
      case 'rainbow':
        return 'spinner'
      default:
        return 'loader'
    }
  }

  private setupLoader() {
    if (this.delay > 0) {
      this._shouldDisplayLoader = false
      setTimeout(() => {
        this._shouldDisplayLoader = true
        this.requestUpdate()
      }, this.delay)
    }
  }
}

export default PktLoader
