import { reactive, ref, computed, watch, unref } from 'vue'
import { typeOf } from '@/utils/common'
import useFetch from '@/hooks/useFetch'
import useSection from '@/hooks/useSection'
import type { Ref } from 'vue'
import type {
  ApiKeys,
  BaseMultiplePageQuery,
  MultipleResult,
  ExtractApiParams,
  ExtractApiResponse,
  ExtractMultipleApiResponse,
} from '@/api/api'
import type { PaginationProps } from 'element-plus'
import type { InstanceTable, InstanceElTable } from 'global-type'

type P = Parameters<typeof useFetch>

type ReturnTable<T extends ApiKeys, D extends boolean | OptionalPage = true> = {
  tableRef: Ref<InstanceTable | undefined>
  elTableRef: Ref<InstanceElTable | undefined>
  pagination: Ref<Partial<PaginationProps>>
  currentPage: Ref<number>
  total: Ref<number>
  loading: Ref<boolean>
  data: Ref<D extends false ? ExtractApiResponse<T> : ExtractMultipleApiResponse<T>[]>
  error: unknown
  hasSelected?: Ref<boolean>
  onSectionChange: (rows: ExtractMultipleApiResponse<T>[]) => void
  selectedList: ExtractMultipleApiResponse<T>[]
  fetchTable: () => void
}

type OptionalPage = Partial<BaseMultiplePageQuery>

export type ModelType<T extends ApiKeys> = Omit<ExtractApiParams<T>, keyof BaseMultiplePageQuery>
export type MultipleResponseType<T extends ApiKeys> = ExtractMultipleApiResponse<T>
export type ResponseType<T extends ApiKeys> = ExtractApiResponse<T>

const DEFAULT_PARAMS: BaseMultiplePageQuery = {
  pageNumber: 1,
  pageSize: 10,
}

const DEFAULT_PAGINATION: Partial<PaginationProps> = {
  layout: 'prev,pager,next',
}

function isPageQuery(c: any): c is OptionalPage {
  return typeOf(c) === 'object'
}

const useTable = <
  T extends ApiKeys,
  C extends Partial<ModelType<T>> = Partial<ModelType<T>>,
  D extends boolean | OptionalPage = true
>(
  api: T,
  additional?: C,
  pageQuery?: D,
  httpConfig: P[1] = 'post',
  immediate = true
): ReturnTable<T, D> => {
  const tableRef = ref<InstanceTable>()

  const elTableRef = computed(() => {
    return tableRef?.value?.tableRef
  })

  const { hasSelected, onSectionChange, selectedList } = useSection<ExtractMultipleApiResponse<T>>()

  const multipleType = pageQuery !== false

  const baseParams = reactive<BaseMultiplePageQuery>(
    Object.assign(DEFAULT_PARAMS, isPageQuery(pageQuery) ? pageQuery : {})
  )

  const currentPage = computed({
    get() {
      return baseParams.pageNumber
    },
    set(n: number) {
      baseParams.pageNumber = n
    },
  })

  const payload = computed(() => {
    return Object.assign({}, multipleType ? baseParams : {}, additional || {}) as ExtractApiParams<T>
  })

  const { loading, fetch, cancel, error, result } = useFetch(api, httpConfig)

  const fetchTable = () => {
    cancel()
    fetch(payload.value)
  }

  watch(payload, () => fetchTable(), { immediate })

  const pageCount = computed(() => {
    return multipleType ? (result.value as MultipleResult<ExtractApiResponse<T>>)?.pageCount || 1 : 1
  })

  const total = computed(() => {
    return multipleType
      ? (result.value as MultipleResult<ExtractApiResponse<T>>)?.totalCount || 0
      : (result.value as Array<any>).length || 0
  })

  const data = computed(() => {
    return multipleType
      ? (result.value as MultipleResult<ExtractMultipleApiResponse<T>>)?.result || []
      : (unref(result) as any)
  })

  const pagination = computed(() => {
    return Object.assign(DEFAULT_PAGINATION, {
      pageSize: baseParams.pageSize,
      pageCount: pageCount.value,
      total: total.value,
    })
  })

  return {
    tableRef,
    elTableRef,
    hasSelected,
    onSectionChange,
    fetchTable,
    selectedList,
    loading,
    total,
    data,
    pagination,
    currentPage: currentPage,
    error,
  }
}

export default useTable
