import type { Ref, VNodeChild } from 'vue'
import { computed, ref } from 'vue'
import useBoolean from './use-boolean'
import useLoading from './use-loading'

export interface PaginationData<T> {
  data: T[]
  pageNum: number
  pageSize: number
  total: number
}

type GetApiData<ApiData, Pagination extends boolean> = Pagination extends true ? PaginationData<ApiData> : ApiData[]

type Transform<ResponseData, ApiData, Pagination extends boolean> = (
  response: ResponseData
) => GetApiData<ApiData, Pagination>

export type TableColumnCheckTitle = string | ((...args: any) => VNodeChild)

export interface TableColumnCheck {
  key: string
  title: TableColumnCheckTitle
  checked: boolean
  visible: boolean
}

export interface UseTableOptions<ResponseData, ApiData, Column, Pagination extends boolean> {
  /**
   * 获取表格数据的 API 函数
   */
  api: () => Promise<ResponseData>
  /**
   * 是否开启分页
   */
  pagination?: Pagination
  /**
   * 转换 API 响应数据为表格数据
   */
  transform: Transform<ResponseData, ApiData, Pagination>
  /**
   * 列配置工厂函数
   */
  columns: () => Column[]
  /**
   * 获取列的勾选状态
   */
  getColumnChecks: (columns: Column[]) => TableColumnCheck[]
  /**
   * 获取列配置
   */
  getColumns: (columns: Column[], checks: TableColumnCheck[]) => Column[]
  /**
   * 数据获取后的回调
   */
  onFetched?: (data: GetApiData<ApiData, Pagination>) => void | Promise<void>
  /**
   * 是否立即获取数据
   *
   * @default true
   */
  immediate?: boolean
}

/**
 * 表格数据 hook
 * 用于获取表格数据，根据是否开启分页返回不同类型的数据
 * @param options 表格配置
 * @returns 表格数据
 */
export default function useTable<ResponseData, ApiData, Column, Pagination extends boolean>(
  options: UseTableOptions<ResponseData, ApiData, Column, Pagination>,
) {
  const { loading, startLoading, endLoading } = useLoading()
  const { bool: empty, setBool: setEmpty } = useBoolean()

  const { api, pagination, transform, columns, getColumnChecks, getColumns, onFetched, immediate = true } = options

  const data = ref([]) as Ref<ApiData[]>

  const columnChecks = ref(getColumnChecks(columns())) as Ref<TableColumnCheck[]>

  const $columns = computed(() => getColumns(columns(), columnChecks.value))

  function reloadColumns() {
    const checkMap = new Map(columnChecks.value.map(col => [col.key, col.checked]))

    const defaultChecks = getColumnChecks(columns())

    columnChecks.value = defaultChecks.map(col => ({
      ...col,
      checked: checkMap.get(col.key) ?? col.checked,
    }))
  }

  async function getData() {
    try {
      startLoading()

      const response = await api()

      const transformed = transform(response)

      data.value = getTableData(transformed, pagination)

      setEmpty(data.value.length === 0)

      await onFetched?.(transformed)
    }
    finally {
      endLoading()
    }
  }

  if (immediate) {
    getData()
  }

  return {
    loading,
    empty,
    data,
    columns: $columns,
    columnChecks,
    reloadColumns,
    getData,
  }
}

/**
 * 获取表格数据，根据是否开启分页返回不同类型的数据
 * @param data 转换后的 API 响应数据
 * @param pagination 是否开启分页
 * @returns 表格数据
 */
function getTableData<ApiData, Pagination extends boolean>(
  data: GetApiData<ApiData, Pagination>,
  pagination?: Pagination,
) {
  if (pagination) {
    return (data as PaginationData<ApiData>).data
  }

  return data as ApiData[]
}
