import { useStore } from 'vuex'
import { on, off, isPromise } from '@/utils/index'
import { useRoute } from 'vue-router'
import { watch, toRaw } from 'vue'
import { useIntervalFn } from '@vueuse/core'
import { FormatEnum, PayloadType, IActionsReturn } from '@/interface'
import { isEqual } from 'lodash-es'
import { cloneDeep } from 'lodash-es'
import store from '@/store/index'

interface InternalHook {
  startInternal: () => void
  stopInternal: () => void
}

type Noop = (x: number, y: number) => void

interface DragOptions {
  startCB?: Noop
  stopCB?: Noop
  moveCB: (x: number, y: number) => void
  horizontal?: boolean
  vertical?: boolean
}

export const useInternal = (ms: number, cb: () => unknown): InternalHook => {
  const { start, stop } = useIntervalFn(cb, ms)
  return {
    startInternal: start,
    stopInternal: stop
  }
}

export const useDrag = (
  container: HTMLElement,
  target: HTMLElement,
  options: DragOptions
) => {
  const cache = {
    x: 0,
    y: 0,
    left: 0,
    top: 0
  }

  let clickPosition = {
    x: 0,
    y: 0
  }

  let canMove = false

  const mousedown = (e: MouseEvent) => {
    canMove = true
    const { clientX, clientY } = e
    clickPosition = {
      x: clientX,
      y: clientY
    }
    const computedStyle = window.getComputedStyle(container)
    const width = computedStyle.width.match(/\d+/)
    const height = computedStyle.height.match(/\d+/)
    const matrix = computedStyle.transform.match(/-?\d+/g)
    if (options.horizontal || options.vertical) {
      if (width && height) {
        cache.x = +width[0]
        cache.y = +height[0]
      }
    } else if (matrix) {
      cache.x = +matrix[4]
      cache.y = +matrix[5]
    }
    target.style.cursor = 'grabbing'
    options.startCB && options.startCB(cache.left, cache.top)
  }
  const mouseup = () => {
    target.style.cursor = 'grab'
    if (canMove) {
      options.stopCB && options.stopCB(cache.left, cache.top)
    }
    canMove = false
  }
  const mousemove = (e: MouseEvent) => {
    if (canMove) {
      const { clientX, clientY } = e
      const left = clientX - clickPosition.x + cache.x
      const top = clientY - clickPosition.y + cache.y
      cache.left = left
      cache.top = top
      options.moveCB && options.moveCB(left, top)
    }
  }

  const stop = () => {
    off(target, 'mousedown', mousedown)
    off(document.documentElement, 'mouseup', mouseup)
    off(document.documentElement, 'mousemove', mousemove)
  }

  const start = () => {
    on(target, 'mousedown', mousedown)
    on(document.documentElement, 'mouseup', mouseup)
    on(document.documentElement, 'mousemove', mousemove)
  }

  return {
    start,
    stop
  }
}

export function uesModuleStore<
  S,
  G = Record<string, string>,
  A = unknown,
  M = unknown
>(NAMESPACED: string) {
  const useState = (): S => {
    return store.state[NAMESPACED]
  }
  const useGetter = <key extends keyof G>(value: key): G[key] => {
    return store.getters[NAMESPACED + '/' + value]
  }
  const useActions = <K extends keyof FormatEnum<A, A>>(
    type: K,
    payload?: PayloadType<K, A>
  ) => {
    return store.dispatch(NAMESPACED + '/' + type, payload) as IActionsReturn<
      K,
      A
    >
  }
  const useMutations = <K extends keyof FormatEnum<M, M>>(
    type: K,
    payload?: PayloadType<K, M>
  ): void => {
    store.commit(NAMESPACED + '/' + type, payload)
  }

  return {
    useActions: useActions,
    useMutations: useMutations,
    useState: useState,
    useGetter: useGetter
  }
}

export const useThemeColor = () => {
  const $store = useStore()
  return $store.state.Header.themeColor
}

export const useUrlParams = (
  key: string,
  cb: (...args: unknown[]) => void,
  args: unknown
) => {
  const route = useRoute()

  return watch(
    [() => route.query[key] as string, () => cloneDeep(args)] as unknown[],
    (v, ov) => {
      if (!isEqual(v, ov)) {
        cb(...v)
      }
    },
    {
      immediate: true
    }
  )
}