import {
  getElementIndex,
  getUniqueIdWithPrefix,
  ElementAnimateUtil,
  EventHandlerUtil,
  DataUtil,
  DOMEventHandlerUtil,
  ElementStyleUtil,
} from '../_utils/index'

export interface IStepperOptions {
  startIndex: number
  animation: boolean
  animationSpeed: string
  animationNextClass: string
  animationPreviousClass: string
}

const defaultStepperOptions: IStepperOptions = {
  startIndex: 1,
  animation: false,
  animationSpeed: '0.3s',
  animationNextClass: 'animate__animated animate__slideInRight animate__fast',
  animationPreviousClass: 'animate__animated animate__slideInLeft animate__fast',
}

class StepperComponent {
  element: HTMLElement
  options: IStepperOptions
  instanceUid: string
  steps: NodeListOf<HTMLElement>
  btnNext: HTMLElement | null
  btnPrev: HTMLElement | null
  btnSubmit: HTMLElement | null
  totatStepsNumber: number = 0
  passedStepIndex: number = 0
  currentStepIndex: number = 1

  constructor(_element: HTMLElement, options: IStepperOptions) {
    this.element = _element
    this.options = Object.assign(defaultStepperOptions, options)
    this.instanceUid = getUniqueIdWithPrefix('stepper')

    // Elements
    this.steps = this.element.querySelectorAll('[data-kt-stepper-element="nav"]')
    this.btnNext = this.element.querySelector('[data-kt-stepper-action="next"]')
    this.btnPrev = this.element.querySelector('[data-kt-stepper-action="previous"]')
    this.btnSubmit = this.element.querySelector('[data-kt-stepper-action="submit"]')

    // Variables
    this.totatStepsNumber = this.steps?.length | 0
    this.passedStepIndex = 0
    this.currentStepIndex = 1

    // Set Current Step
    if (this.options.startIndex > 1) {
      this._goTo(this.options.startIndex)
    }

    // Event Handlers
    this.initHandlers()

    // Bind Instance
    DataUtil.set(this.element, 'stepper', this)
  }

  private _goTo = (index: number) => {
    EventHandlerUtil.trigger(this.element, 'kt.stepper.change')
    // Skip if this step is already shown
    if (index === this.currentStepIndex || index > this.totatStepsNumber || index < 0) {
      return
    }

    // Validate step number
    index = parseInt(index.toString())
    // Set current step
    this.passedStepIndex = this.currentStepIndex
    this.currentStepIndex = index

    // Refresh elements
    this.refreshUI()

    EventHandlerUtil.trigger(this.element, 'kt.stepper.changed')
  }

  private initHandlers = () => {
    this.btnNext?.addEventListener('click', (e: Event) => {
      e.preventDefault()

      EventHandlerUtil.trigger(this.element, 'kt.stepper.next', e)
    })

    this.btnPrev?.addEventListener('click', (e: Event) => {
      e.preventDefault()

      EventHandlerUtil.trigger(this.element, 'kt.stepper.previous', e)
    })

    DOMEventHandlerUtil.on(this.element, '[data-kt-stepper-action="step"]', 'click', (e: Event) => {
      e.preventDefault()

      if (this.steps && this.steps.length > 0) {
        for (let i = 0; i < this.steps.length; i++) {
          if ((this.steps[i] as HTMLElement) === this.element) {
            let index = i + 1

            const stepDirection = this._getStepDirection(index)
            EventHandlerUtil.trigger(this.element, `stepper.${stepDirection}`, e)
            return
          }
        }
      }
    })
  }

  private _getStepDirection = (index: number) => {
    return index > this.currentStepIndex ? 'next' : 'previous'
  }

  private getStepContent = (index: number) => {
    const content = this.element.querySelectorAll('[data-kt-stepper-element="content"]')
    if (!content) {
      return false
    }

    if (content[index - 1]) {
      return content[index - 1]
    }

    return false
  }

  private getLastStepIndex = () => {
    return this.totatStepsNumber
  }

  private getTotalStepsNumber = () => {
    return this.totatStepsNumber
  }

  private refreshUI = () => {
    let state = ''

    if (this.isLastStep()) {
      state = 'last'
    } else if (this.isFirstStep()) {
      state = 'first'
    } else {
      state = 'between'
    }

    // Set state class
    this.element.classList.remove('last')
    this.element.classList.remove('first')
    this.element.classList.remove('between')

    this.element.classList.add(state)

    // Step Items
    const elements = this.element.querySelectorAll(
      '[data-kt-stepper-element="nav"], [data-kt-stepper-element="content"], [data-kt-stepper-element="info"]'
    )

    if (!elements || elements.length <= 0) {
      return
    }

    for (var i = 0, len = elements.length; i < len; i++) {
      var element = elements[i] as HTMLElement
      var index = getElementIndex(element) + 1

      element.classList.remove('current')
      element.classList.remove('completed')
      element.classList.remove('pending')

      if (index === this.currentStepIndex) {
        element.classList.add('current')

        if (
          this.options.animation !== false &&
          element.getAttribute('data-kt-stepper-element') === 'content'
        ) {
          ElementStyleUtil.set(element, 'animationDuration', this.options.animationSpeed)

          const animation =
            this._getStepDirection(this.passedStepIndex) === 'previous'
              ? this.options.animationPreviousClass
              : this.options.animationNextClass
          ElementAnimateUtil.animateClass(element, animation)
        }
      } else {
        if (index < this.currentStepIndex) {
          element.classList.add('completed')
        } else {
          element.classList.add('pending')
        }
      }
    }
  }

  private isLastStep = () => {
    return this.currentStepIndex === this.totatStepsNumber
  }

  private isFirstStep = () => {
    return this.currentStepIndex === 1
  }

  private isBetweenStep = () => {
    return this.isLastStep() === false && this.isFirstStep() === false
  }

  //   ///////////////////////
  //   // ** Public API  ** //
  //   ///////////////////////

  //   // Plugin API
  public goto = (index: number) => {
    return this._goTo(index)
  }

  public goNext = () => {
    return this.goto(this.getNextStepIndex())
  }

  public goPrev = () => {
    return this.goto(this.getPrevStepIndex())
  }

  public goFirst = () => {
    return this.goto(1)
  }

  public goLast = () => {
    return this.goto(this.getLastStepIndex())
  }

  public getCurrentStepIndex = () => {
    return this.currentStepIndex
  }

  public getNextStepIndex = () => {
    if (this.totatStepsNumber >= this.currentStepIndex + 1) {
      return this.currentStepIndex + 1
    } else {
      return this.totatStepsNumber
    }
  }

  public getPassedStepIndex = () => {
    return this.passedStepIndex
  }

  public getPrevStepIndex = () => {
    if (this.currentStepIndex - 1 > 1) {
      return this.currentStepIndex - 1
    } else {
      return 1
    }
  }

  public getElement = (index: number) => {
    return this.element
  }

  // Event API
  public on = (name: string, handler: Function) => {
    return EventHandlerUtil.on(this.element, name, handler)
  }

  public one = (name: string, handler: Function) => {
    return EventHandlerUtil.one(this.element, name, handler)
  }

  public off = (name: string, handlerId: string) => {
    return EventHandlerUtil.off(this.element, name, handlerId)
  }

  public destroy = () => {
    console.log('destroy stepper')
  }

  public trigger = (name: string, event: Event) => {
    return EventHandlerUtil.trigger(this.element, name, event)
  }

  // Static methods
  public static hasInstace(element: HTMLElement): boolean {
    return DataUtil.has(element, 'stepper')
  }

  public static getInstance(element: HTMLElement): StepperComponent | undefined {
    if (element !== null && StepperComponent.hasInstace(element)) {
      const data = DataUtil.get(element, 'stepper')
      if (data) {
        return data as StepperComponent
      }
    }
  }

  // Create Instances
  public static createInstances(selector: string): void {
    const elements = document.body.querySelectorAll(selector)
    elements.forEach((element) => {
      const item = element as HTMLElement
      let stepper = StepperComponent.getInstance(item)
      if (!stepper) {
        stepper = new StepperComponent(item, defaultStepperOptions)
      }
    })
  }

  public static createInsance = (
    element: HTMLElement,
    options: IStepperOptions = defaultStepperOptions
  ): StepperComponent | null => {
    if (!element) {
      return null
    }
    let stepper = StepperComponent.getInstance(element)
    if (!stepper) {
      stepper = new StepperComponent(element, options)
    }
    return stepper
  }

  public static bootstrap(attr: string = '[data-kt-stepper]') {
    StepperComponent.createInstances(attr)
  }
}

export {StepperComponent, defaultStepperOptions}
