/*
 * HSToggleCount
 * @version: 3.0.1
 * @author: Preline Labs Ltd.
 * @license: Licensed under MIT and Preline UI Fair Use License (https://preline.co/docs/license.html)
 * Copyright 2024 Preline Labs Ltd.
 */

import { IToggleCountOptions, IToggleCount } from './interfaces'

import HSBasePlugin from '../base-plugin'
import { ICollectionItem } from '../../interfaces'

class HSToggleCount extends HSBasePlugin<IToggleCountOptions> implements IToggleCount {
  private readonly target: HTMLInputElement | null
  private readonly min: number | null
  private readonly max: number | null
  private readonly duration: number | null
  private isChecked: boolean

  private onToggleChangeListener: () => void

  constructor(el: HTMLElement, options?: IToggleCountOptions) {
    super(el, options)

    const data = el.getAttribute('data-toggle-count')
    const dataOptions: IToggleCountOptions = data ? JSON.parse(data) : {}
    const concatOptions = {
      ...dataOptions,
      ...options
    }

    this.target = concatOptions?.target
      ? typeof concatOptions?.target === 'string'
        ? (document.querySelector(concatOptions.target) as HTMLInputElement)
        : concatOptions.target
      : null
    this.min = concatOptions?.min || 0
    this.max = concatOptions?.max || 0
    this.duration = concatOptions?.duration || 700

    this.isChecked = (this.target as HTMLInputElement).checked || false

    if (this.target) this.init()
  }

  private toggleChange() {
    this.isChecked = !this.isChecked

    this.toggle()
  }

  private init() {
    this.createCollection(window.$hsToggleCountCollection, this)

    if (this.isChecked) this.el.innerText = String(this.max)

    this.onToggleChangeListener = () => this.toggleChange()

    this.target.addEventListener('change', this.onToggleChangeListener)
  }

  private toggle() {
    if (this.isChecked) this.countUp()
    else this.countDown()
  }

  private animate(from: number, to: number) {
    let startTimestamp = 0

    const step = (timestamp: number) => {
      if (!startTimestamp) startTimestamp = timestamp
      const progress = Math.min((timestamp - startTimestamp) / this.duration, 1)

      this.el.innerText = String(Math.floor(progress * (to - from) + from))

      if (progress < 1) window.requestAnimationFrame(step)
    }

    window.requestAnimationFrame(step)
  }

  // Public methods
  public countUp() {
    this.animate(this.min, this.max)
  }

  public countDown() {
    this.animate(this.max, this.min)
  }

  public destroy() {
    // Remove listeners
    this.target.removeEventListener('change', this.onToggleChangeListener)

    window.$hsToggleCountCollection = window.$hsToggleCountCollection.filter(({ element }) => element.el !== this.el)
  }

  // Static methods
  static getInstance(target: HTMLElement | string, isInstance?: boolean) {
    const elInCollection = window.$hsToggleCountCollection.find(
      el => el.element.el === (typeof target === 'string' ? document.querySelector(target) : target)
    )

    return elInCollection ? (isInstance ? elInCollection : elInCollection.element) : null
  }

  static autoInit() {
    if (!window.$hsToggleCountCollection) window.$hsToggleCountCollection = []

    if (window.$hsToggleCountCollection)
      window.$hsToggleCountCollection = window.$hsToggleCountCollection.filter(({ element }) =>
        document.contains(element.el)
      )

    document.querySelectorAll('[data-toggle-count]:not(.--prevent-on-load-init)').forEach((el: HTMLElement) => {
      if (!window.$hsToggleCountCollection.find(elC => (elC?.element?.el as HTMLElement) === el)) new HSToggleCount(el)
    })
  }
}

declare global {
  interface Window {
    HSToggleCount: Function
    $hsToggleCountCollection: ICollectionItem<HSToggleCount>[]
  }
}

window.addEventListener('load', () => {
  HSToggleCount.autoInit()

  // Uncomment for debug
  // console.log('Toggle count collection:', window.$hsToggleCountCollection);
})

if (typeof window !== 'undefined') {
  window.HSToggleCount = HSToggleCount
}

export default HSToggleCount
