import { PktElement } from '@/base-elements/element'
import { ElementProps } from '@/types/typeUtils'
import { PktIconName } from '@oslokommune/punkt-assets/dist/icons/icon'
import { html, PropertyValues } from 'lit'
import { customElement, property } from 'lit/decorators.js'
import { unsafeSVG } from 'lit/directives/unsafe-svg.js'

// Allow global override of icon fetch
window.pktFetch = window.pktFetch === undefined ? fetch : window.pktFetch
window.pktIconPath = window.pktIconPath || 'https://punkt-cdn.oslo.kommune.no/latest/icons/'

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
const errorSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"></svg>'
const dlStatus: { [key: string]: string } = {}

const downloadIconOrGetFromCache = async (
  name: PktIconName,
  path: string | undefined,
): Promise<string | null> => {
  let i = 0
  while (dlStatus[path + name + '.svg'] === 'fetching') {
    i++
    if (i > 50) break
    await sleep(50)
  }
  if (localStorage.getItem(path + name + '.svg')) {
    return Promise.resolve(localStorage.getItem(path + name + '.svg'))
  } else if (typeof window.pktFetch === 'function') {
    dlStatus[path + name + '.svg'] = 'fetching'
    return Promise.resolve(
      window
        .pktFetch(path + name + '.svg')
        .then((response) => {
          if (!response.ok) {
            // eslint-disable-next-line no-console
            console.error('Missing icon: ' + path + name + '.svg')
            return errorSvg
          } else {
            return response.text()
          }
        })
        .then((text) => {
          if (text !== errorSvg) {
            localStorage.setItem(path + name + '.svg', text)
          }
          dlStatus[path + name + '.svg'] = 'fetched'
          return text
        }),
    )
  }
  return Promise.resolve(errorSvg)
}

type Props = ElementProps<PktIcon, 'path' | 'name'>

@customElement('pkt-icon')
export class PktIcon extends PktElement<Props> {
  @property({ type: String, reflect: false })
  path: string | undefined = window.pktIconPath

  @property({ type: String, reflect: true })
  name: PktIconName = ''

  @property({ type: SVGElement })
  private icon: any = unsafeSVG(errorSvg)

  @property({ type: Array, noAccessor: true })
  private _updatedProps: string[] = []

  connectedCallback(): void {
    super.connectedCallback()
    this.classList.add('pkt-icon')
  }
  async attributeChangedCallback(name: string, _old: string | null, value: string | null) {
    super.attributeChangedCallback(name, _old, value)
    if (name === 'name' || name === 'path') this.getIcon(this.name)
  }

  protected async updated(_changedProperties: PropertyValues) {
    super.updated(_changedProperties)
    if (_changedProperties.has('name') || _changedProperties.has('path')) {
      this.getIcon(this.name)
    }
  }

  protected async getIcon(name: PktIconName = '') {
    if (this._updatedProps.length > 0) {
      if (!this.path) this.path === window.pktIconPath
      this.icon = unsafeSVG(
        await downloadIconOrGetFromCache(this.name || '', this.path).then((res) => res),
      ) as SVGElement
      this._updatedProps = []
    } else {
      if (!this._updatedProps.includes(name)) {
        this._updatedProps.push(name)
      }
    }
  }

  render() {
    return html`${this.name && this.icon}`
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'pkt-icon': PktIcon
  }
}
