import { ref, computed, watch, type Ref, type ComputedRef } from 'vue'
import { useUrlState } from './useUrlState'
import type { DataTableSort, DataTablePagination, DataTableColumnFilters, SortDirection } from '~/shared/types/dataTable'

export interface UseDataTableDeps {
  /** Called whenever sort/page/search/perPage/filter changes. The composable does NOT fetch data itself. */
  fetchData: () => void | Promise<void>
}

export interface UseDataTableOptions {
  /** Default sort field */
  defaultSort?: string
  /** Default sort direction */
  defaultDirection?: SortDirection
  /** Default items per page */
  defaultPerPage?: number
  /** Available per-page options */
  perPageOptions?: number[]
  /** Search debounce in ms. Default: 300 */
  searchDebounceMs?: number
  /** Whether to persist state to URL. Default: true */
  persistToUrl?: boolean
  /** Extra URL state params for page-specific filters */
  extraParams?: Record<string, { default: string | number; type?: 'string' | 'number' }>
  /** Column filter keys to persist in URL (prefixed with `f.` in query string) */
  columnFilterKeys?: string[]
}

export interface UseDataTableReturn {
  searchTerm: Ref<string>
  sortField: Ref<string>
  sortDirection: Ref<SortDirection>
  currentPage: Ref<number>
  itemsPerPage: Ref<number>
  columnFilters: Ref<DataTableColumnFilters>
  filters: Record<string, Ref<string | number>>
  sort: ComputedRef<DataTableSort>
  pagination: (total: Ref<number>, pageItems: Ref<number>) => ComputedRef<DataTablePagination>
  handleSort: (newSort: DataTableSort) => void
  handlePageChange: (page: number) => void
  handlePerPageChange: (perPage: number) => void
  handleFilterChange: () => void
  handleColumnFiltersChange: (filters: DataTableColumnFilters) => void
  debouncedSearch: () => void
  refresh: () => void
  perPageOptions: number[]
}

export function useDataTable(
  deps: UseDataTableDeps,
  options: UseDataTableOptions = {},
): UseDataTableReturn {
  const {
    defaultSort = 'name',
    defaultDirection = 'asc',
    defaultPerPage = 20,
    perPageOptions = [20, 50, 100],
    searchDebounceMs = 300,
    persistToUrl = true,
    extraParams = {},
    columnFilterKeys = [],
  } = options

  let searchTerm: Ref<string>
  let sortField: Ref<string>
  let sortDirection: Ref<SortDirection>
  let currentPage: Ref<number>
  let itemsPerPage: Ref<number>
  const filters: Record<string, Ref<string | number>> = {}
  const columnFilterRefs: Record<string, Ref<string>> = {}

  if (persistToUrl) {
    const paramDefs: Record<string, { default: string | number; type?: 'string' | 'number'; debounce?: number }> = {
      page: { default: 1, type: 'number' },
      perPage: { default: defaultPerPage, type: 'number' },
      q: { default: '', debounce: searchDebounceMs },
      sort: { default: defaultSort },
      dir: { default: defaultDirection },
      ...extraParams,
    }

    for (const key of columnFilterKeys) {
      paramDefs[`f.${key}`] = { default: '', debounce: 300 }
    }

    const state = useUrlState(paramDefs, {
      onRestore: () => deps.fetchData(),
    })

    searchTerm = state.q as Ref<string>
    sortField = state.sort as Ref<string>
    sortDirection = state.dir as unknown as Ref<SortDirection>
    currentPage = state.page as unknown as Ref<number>
    itemsPerPage = state.perPage as unknown as Ref<number>

    for (const key of Object.keys(extraParams)) {
      filters[key] = (state as Record<string, Ref<string | number>>)[key]!
    }

    for (const key of columnFilterKeys) {
      columnFilterRefs[key] = (state as Record<string, Ref<string>>)[`f.${key}`]!
    }
  }
  else {
    searchTerm = ref('')
    sortField = ref(defaultSort)
    sortDirection = ref(defaultDirection) as Ref<SortDirection>
    currentPage = ref(1)
    itemsPerPage = ref(defaultPerPage)

    for (const [key, def] of Object.entries(extraParams)) {
      filters[key] = ref(def.default)
    }
  }

  // Search debounce
  let searchTimeout: ReturnType<typeof setTimeout> | null = null

  const debouncedSearch = () => {
    if (searchTimeout) clearTimeout(searchTimeout)
    searchTimeout = setTimeout(() => {
      currentPage.value = 1
      deps.fetchData()
    }, searchDebounceMs)
  }

  // Sort
  const sort = computed<DataTableSort>(() => ({
    field: sortField.value,
    direction: sortDirection.value,
  }))

  const handleSort = (newSort: DataTableSort) => {
    sortField.value = newSort.field
    sortDirection.value = newSort.direction
    currentPage.value = 1
    deps.fetchData()
  }

  // Pagination
  const handlePageChange = (page: number) => {
    currentPage.value = page
    deps.fetchData()
  }

  const handlePerPageChange = (perPage: number) => {
    itemsPerPage.value = perPage
    currentPage.value = 1
    deps.fetchData()
  }

  // Column filters — seed from URL-persisted refs
  const initialColumnFilters: DataTableColumnFilters = {}
  for (const [key, r] of Object.entries(columnFilterRefs)) {
    if (r.value) initialColumnFilters[key] = r.value
  }
  const columnFilters = ref<DataTableColumnFilters>(initialColumnFilters)

  // Sync columnFilters → URL refs
  if (persistToUrl && columnFilterKeys.length > 0) {
    watch(columnFilters, (val) => {
      for (const key of columnFilterKeys) {
        columnFilterRefs[key]!.value = val[key] || ''
      }
    }, { deep: true })

    // Sync URL refs → columnFilters (back/forward navigation)
    for (const key of columnFilterKeys) {
      watch(columnFilterRefs[key]!, (urlVal) => {
        const current = columnFilters.value[key] || ''
        if (current !== urlVal) {
          columnFilters.value = { ...columnFilters.value, [key]: urlVal }
        }
      })
    }
  }

  const handleColumnFiltersChange = (newFilters: DataTableColumnFilters) => {
    columnFilters.value = newFilters
    currentPage.value = 1
    deps.fetchData()
  }

  // Generic filter change handler (resets page and refetches)
  const handleFilterChange = () => {
    currentPage.value = 1
    deps.fetchData()
  }

  const pagination = (total: Ref<number>, pageItems: Ref<number>): ComputedRef<DataTablePagination> =>
    computed(() => ({
      currentPage: currentPage.value,
      totalPages: Math.ceil(total.value / itemsPerPage.value) || 1,
      totalItems: total.value,
      pageItems: pageItems.value,
      perPageOptions,
      perPage: itemsPerPage.value,
    }))

  const refresh = () => deps.fetchData()

  return {
    searchTerm,
    sortField,
    sortDirection,
    currentPage,
    itemsPerPage,
    columnFilters,
    filters,
    sort,
    pagination,
    handleSort,
    handlePageChange,
    handlePerPageChange,
    handleFilterChange,
    handleColumnFiltersChange,
    debouncedSearch,
    refresh,
    perPageOptions,
  }
}
