import type { FlatResponseData } from '@sa/axios'
import type { PaginationData, TableColumnCheck, UseTableOptions } from '@sa/hooks'
import type { PaginationProps } from 'naive-ui'
import type { Ref } from 'vue'
import { useBoolean, useTable } from '@sa/hooks'
import { jsonClone } from '@sa/utils'
import { computed, effectScope, onScopeDispose, reactive, shallowRef, watch } from 'vue'
import { $t } from '@/locales'
import { useAppStore } from '@/store/modules/app'

export type UseNaiveTableOptions<ResponseData, ApiData, Pagination extends boolean> = Omit<
  UseTableOptions<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, Pagination>,
  'pagination' | 'getColumnChecks' | 'getColumns'
> & {
  /**
   * 获取列的可见性
   *
   * @param column
   *
   * @default true
   *
   * @returns 如果列可见返回 true，否则返回 false
   */
  getColumnVisible?: (column: NaiveUI.TableColumn<ApiData>) => boolean
}

const SELECTION_KEY = '__selection__'

const EXPAND_KEY = '__expand__'

export function useNaiveTable<ResponseData, ApiData>(options: UseNaiveTableOptions<ResponseData, ApiData, false>) {
  const scope = effectScope()
  const appStore = useAppStore()

  const result = useTable<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, false>({
    ...options,
    getColumnChecks: cols => getColumnChecks(cols, options.getColumnVisible),
    getColumns,
  })

  // 计算表格的总宽度，用于水平滚动
  const scrollX = computed(() => {
    return result.columns.value.reduce((acc, column) => {
      return acc + Number(column.width ?? column.minWidth ?? 120)
    }, 0)
  })

  scope.run(() => {
    watch(
      () => appStore.locale,
      () => {
        result.reloadColumns()
      },
    )
  })

  onScopeDispose(() => {
    scope.stop()
  })

  return {
    ...result,
    scrollX,
  }
}

type PaginationParams = Pick<PaginationProps, 'page' | 'pageSize'>

type UseNaivePaginatedTableOptions<ResponseData, ApiData> = UseNaiveTableOptions<ResponseData, ApiData, true> & {
  paginationProps?: Omit<PaginationProps, 'page' | 'pageSize' | 'itemCount'>
  /**
   * 是否显示表格的总条数
   *
   * @default true
   */
  showTotal?: boolean
  onPaginationParamsChange?: (params: PaginationParams) => void | Promise<void>
}

export function useNaivePaginatedTable<ResponseData, ApiData>(
  options: UseNaivePaginatedTableOptions<ResponseData, ApiData>,
) {
  const scope = effectScope()
  const appStore = useAppStore()

  const isMobile = computed(() => appStore.isMobile)

  const showTotal = computed(() => options.showTotal ?? true)

  const pagination = reactive({
    page: 1,
    pageSize: 10,
    itemCount: 0,
    showSizePicker: true,
    pageSizes: [10, 15, 20, 25, 30],
    prefix: showTotal.value ? page => $t('datatable.itemCount', { total: page.itemCount }) : undefined,
    onUpdatePage(page) {
      pagination.page = page
    },
    onUpdatePageSize(pageSize) {
      pagination.pageSize = pageSize
      pagination.page = 1
    },
    ...options.paginationProps,
  }) as PaginationProps

  // 针对移动端，如果系统不支持移动端，可以直接使用 `pagination`
  const mobilePagination = computed(() => {
    const p: PaginationProps = {
      ...pagination,
      pageSlot: isMobile.value ? 3 : 9,
      prefix: !isMobile.value && showTotal.value ? pagination.prefix : undefined,
    }

    return p
  })

  const paginationParams = computed(() => {
    const { page, pageSize } = pagination

    return {
      page,
      pageSize,
    }
  })

  const result = useTable<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, true>({
    ...options,
    pagination: true,
    getColumnChecks: cols => getColumnChecks(cols, options.getColumnVisible),
    getColumns,
    onFetched: (data) => {
      pagination.itemCount = data.total
      pagination.pageSize = data.pageSize
    },
  })

  async function getDataByPage(page: number = 1) {
    if (page !== pagination.page) {
      pagination.page = page

      return
    }

    await result.getData()
  }

  scope.run(() => {
    watch(
      () => appStore.locale,
      () => {
        result.reloadColumns()
      },
    )

    watch(paginationParams, async (newVal) => {
      await options.onPaginationParamsChange?.(newVal)

      await result.getData()
    })
  })

  onScopeDispose(() => {
    scope.stop()
  })

  return {
    ...result,
    getDataByPage,
    pagination,
    mobilePagination,
  }
}

export function useTableOperate<TableData>(
  data: Ref<TableData[]>,
  idKey: keyof TableData,
  getData: () => Promise<void>,
): {
    drawerVisible: Ref<boolean>
    openDrawer: () => void
    closeDrawer: () => void
    operateType: Ref<NaiveUI.TableOperateType>
    handleAdd: () => void
    editingData: Ref<TableData | null>
    handleEdit: (id: TableData[keyof TableData]) => void
    checkedRowKeys: Ref<string[]>
    onBatchDeleted: () => Promise<void>
    onDeleted: () => Promise<void>
  } {
  const { bool: drawerVisible, setTrue: openDrawer, setFalse: closeDrawer } = useBoolean()

  const operateType = shallowRef<NaiveUI.TableOperateType>('add')

  function handleAdd() {
    operateType.value = 'add'
    openDrawer()
  }

  /** 编辑行数据 */
  const editingData = shallowRef<TableData | null>(null)

  function handleEdit(id: TableData[keyof TableData]) {
    operateType.value = 'edit'
    const findItem = data.value.find(item => item[idKey] === id) || null
    editingData.value = jsonClone(findItem)

    openDrawer()
  }

  /** 表格的选中行 keys */
  const checkedRowKeys = shallowRef<string[]>([])

  /** 批量删除操作完成后的钩子 */
  async function onBatchDeleted() {
    window.$message?.success($t('common.deleteSuccess'))

    checkedRowKeys.value = []

    await getData()
  }

  /** 删除操作完成后的钩子 */
  async function onDeleted() {
    window.$message?.success($t('common.deleteSuccess'))

    await getData()
  }

  return {
    drawerVisible,
    openDrawer,
    closeDrawer,
    operateType,
    handleAdd,
    editingData,
    handleEdit,
    checkedRowKeys,
    onBatchDeleted,
    onDeleted,
  }
}

/**
 * 默认的表格数据转换函数
 *
 * @param response 原始的 API 响应数据
 * @returns 转换后的分页数据
 */
export function defaultTransform<ApiData>(
  response: FlatResponseData<any, Api.Common.PaginatingQueryRecord<ApiData>>,
): PaginationData<ApiData> {
  const { data, error } = response

  if (!error) {
    const { list, currentPage: pageNum, pageSize: size, total } = data

    return {
      data: list,
      pageNum,
      pageSize: size,
      total,
    }
  }

  return {
    data: [],
    pageNum: 1,
    pageSize: 10,
    total: 0,
  }
}

function getColumnChecks<Column extends NaiveUI.TableColumn<any>>(
  cols: Column[],
  getColumnVisible?: (column: Column) => boolean,
) {
  const checks: TableColumnCheck[] = []

  cols.forEach((column) => {
    if (isTableColumnHasKey(column)) {
      checks.push({
        key: column.key as string,
        title: column.title!,
        checked: true,
        visible: getColumnVisible?.(column) ?? true,
      })
    }
    else if (column.type === 'selection') {
      checks.push({
        key: SELECTION_KEY,
        title: $t('common.check'),
        checked: true,
        visible: getColumnVisible?.(column) ?? false,
      })
    }
    else if (column.type === 'expand') {
      checks.push({
        key: EXPAND_KEY,
        title: $t('common.expandColumn'),
        checked: true,
        visible: getColumnVisible?.(column) ?? false,
      })
    }
  })

  return checks
}

function getColumns<Column extends NaiveUI.TableColumn<any>>(cols: Column[], checks: TableColumnCheck[]) {
  const columnMap = new Map<string, Column>()

  cols.forEach((column) => {
    if (isTableColumnHasKey(column)) {
      columnMap.set(column.key as string, column)
    }
    else if (column.type === 'selection') {
      columnMap.set(SELECTION_KEY, column)
    }
    else if (column.type === 'expand') {
      columnMap.set(EXPAND_KEY, column)
    }
  })

  const filteredColumns = checks.filter(item => item.checked).map(check => columnMap.get(check.key) as Column)

  return filteredColumns
}

export function isTableColumnHasKey<T>(column: NaiveUI.TableColumn<T>): column is NaiveUI.TableColumnWithKey<T> {
  return Boolean((column as NaiveUI.TableColumnWithKey<T>).key)
}
