/**
 * @file vue-awesome-swiper
 * @module SwiperComponent
 * @author Surmon <https://github.com/surmon-china>
 */

import Vue, { PropType, VNode, CreateElement } from 'vue'
import Swiper, { SwiperOptions } from 'swiper'
import { DEFAULT_CLASSES, CoreNames, ComponentPropNames, ComponentEvents } from './constants'
import { handleClickSlideEvent, bindSwiperEvents } from './event'

enum SlotNames {
  ParallaxBg = 'parallax-bg',
  Pagination = 'pagination',
  Scrollbar = 'scrollbar',
  PrevButton = 'button-prev',
  NextButton = 'button-next'
}

export default function getSwiperComponent(SwiperClass: typeof Swiper) {
  return Vue.extend({
    name: CoreNames.SwiperComponent,
    props: {
      defaultOptions: {
        type: Object as PropType<SwiperOptions>,
        required: false,
        default: () => ({} as SwiperOptions)
      },
      // eslint-disable-next-line vue/require-default-prop
      options: {
        type: Object as PropType<SwiperOptions>,
        required: false
      },
      [ComponentPropNames.AutoUpdate]: {
        type: Boolean,
        default: true
      },
      // https://github.com/surmon-china/vue-awesome-swiper/pull/550/files
      [ComponentPropNames.AutoDestroy]: {
        type: Boolean,
        default: true
      },
      // https://github.com/surmon-china/vue-awesome-swiper/pull/388
      [ComponentPropNames.DeleteInstanceOnDestroy]: {
        type: Boolean,
        required: false,
        default: true
      },
      [ComponentPropNames.CleanupStylesOnDestroy]: {
        type: Boolean,
        required: false,
        default: true
      }
    },
    data() {
      return {
        [CoreNames.SwiperInstance as const]: null as Swiper | null
      }
    },
    computed: {
      swiperInstance: {
        cache: false,
        set(swiper: Swiper) {
          this[CoreNames.SwiperInstance] = swiper
        },
        get(): Swiper | null {
          return this[CoreNames.SwiperInstance]
        }
      },
      swiperOptions(): SwiperOptions {
        return this.options || this.defaultOptions
      },
      wrapperClass(): string {
        return this.swiperOptions.wrapperClass || DEFAULT_CLASSES.wrapperClass
      }
    },
    methods: {
      // Feature: click event
      handleSwiperClick(event: MouseEvent) {
        handleClickSlideEvent(
          this.swiperInstance,
          event,
          this.$emit.bind(this)
        )
      },
      autoReLoopSwiper() {
        if (this.swiperInstance && this.swiperOptions.loop) {
          // https://github.com/surmon-china/vue-awesome-swiper/issues/593
          // https://github.com/surmon-china/vue-awesome-swiper/issues/544
          // https://github.com/surmon-china/vue-awesome-swiper/pull/545/files
          const swiper = this.swiperInstance as any
          swiper?.loopDestroy?.()
          swiper?.loopCreate?.()
        }
      },
      updateSwiper() {
        if (this[ComponentPropNames.AutoUpdate] && this.swiperInstance) {
          this.autoReLoopSwiper()
          this.swiperInstance?.update?.()
          this.swiperInstance.navigation?.update?.()
          this.swiperInstance.pagination?.render?.()
          this.swiperInstance.pagination?.update?.()
        }
      },
      destroySwiper() {
        if (this[ComponentPropNames.AutoDestroy] && this.swiperInstance) {
          // https://github.com/surmon-china/vue-awesome-swiper/pull/341
          // https://github.com/surmon-china/vue-awesome-swiper/issues/340
          if ((this.swiperInstance as any).initialized) {
            this.swiperInstance?.destroy?.(
              this[ComponentPropNames.DeleteInstanceOnDestroy],
              this[ComponentPropNames.CleanupStylesOnDestroy]
            )
          }
        }
      },
      initSwiper() {
        this.swiperInstance = new SwiperClass(
          this.$el as HTMLElement,
          this.swiperOptions
        )
        bindSwiperEvents(
          this.swiperInstance,
          this.$emit.bind(this)
        )
        this.$emit(
          ComponentEvents.Ready,
          this.swiperInstance
        )
      }
    },
    mounted() {
      if (!this.swiperInstance) {
        this.initSwiper()
      }
    },
    // Update swiper when the parent component activated with `keep-alive`.
    activated() {
      this.updateSwiper()
    },
    updated() {
      this.updateSwiper()
    },
    beforeDestroy() {
      // https://github.com/surmon-china/vue-awesome-swiper/commit/2924a9d4d3d1cf51c0d46076410b1f804b2b8a43#diff-7f4e0261ac562c0f354cb91a1ca8864f
      this.$nextTick(this.destroySwiper)
    },
    render(createElement: CreateElement): VNode {
      return createElement('div',
        {
          staticClass: DEFAULT_CLASSES.containerClass,
          on: {
            click: this.handleSwiperClick
          }
        },
        [
          this.$slots[SlotNames.ParallaxBg],
          createElement('div', {
            class: this.wrapperClass
          }, this.$slots.default),
          this.$slots[SlotNames.Pagination],
          this.$slots[SlotNames.PrevButton],
          this.$slots[SlotNames.NextButton],
          this.$slots[SlotNames.Scrollbar]
        ]
      )
    }
  })
}
