"use client"

import { useState } from "react"
import { useQueryStates } from "nuqs"
import { useQuery, useQueryClient, type UseQueryOptions } from "@tanstack/react-query"
import type { DataViewChangeEvent, DataViewPaginationState, DataViewSorting } from "./types"
import { dataTableSearchParams } from "./search-params"
import { DEFAULT_PER_PAGE, type CrudListResponse } from "@garage/api"

type DataTableClient = {
    list(query?: any): Promise<any>
}

type DataTableParams = {
    page: number
    per_page: number
    sort_by: string | null
    sort_order: "asc" | "desc" | null
}

type UseDataTableQueryOptions<C extends DataTableClient> = {
    queryKey: string[]
    client: C
    queryOptions?: Omit<UseQueryOptions<CrudListResponse<C>>, "queryKey" | "queryFn">
    extraParams?: Record<string, unknown>
    /** When true, pagination/sorting state lives in component state instead of URL params. Used for tables inside dialogs so they don't share state with the parent page. */
    localState?: boolean
}

export function useDataTableQuery<C extends DataTableClient>({
    queryKey,
    client,
    queryOptions,
    extraParams,
    localState = false,
}: UseDataTableQueryOptions<C>) {
    const [urlParams, setUrlParams] = useQueryStates(dataTableSearchParams)
    const [localParams, setLocalParams] = useState<DataTableParams>({
        page: 1,
        per_page: DEFAULT_PER_PAGE,
        sort_by: null,
        sort_order: null,
    })

    const params: DataTableParams = localState ? localParams : urlParams
    const setParams = (next: Partial<DataTableParams>) => {
        if (localState) {
            setLocalParams((prev) => ({ ...prev, ...next }))
        } else {
            setUrlParams(next)
        }
    }

    const _queryKey = [...queryKey, params, ...(extraParams ? [extraParams] : [])]
    const query = useQuery<CrudListResponse<C>>({
        queryKey: _queryKey,
        queryFn: () => {
            const apiParams: Record<string, unknown> = {
                page: params.page,
                per_page: params.per_page,
                ...extraParams,
            }
            if (params.sort_by) apiParams.sort_by = params.sort_by
            if (params.sort_order) apiParams.sort_order = params.sort_order

            return client.list(apiParams) as Promise<CrudListResponse<C>>
        },
        ...queryOptions,
    })

    const pagination: DataViewPaginationState = {
        page: params.page,
        pageSize: params.per_page,
        pageCount: (query.data as any)?.meta?.last_page ?? 1,
        total: (query.data as any)?.meta?.total ?? 0,
    }

    const sorting: DataViewSorting = params.sort_by
        ? [{ id: params.sort_by, desc: params.sort_order === "desc" }]
        : []

    const handleChange = (event: DataViewChangeEvent) => {
        switch (event.type) {
            case "pagination":
                setParams({
                    page: event.pagination.page,
                    per_page: event.pagination.pageSize,
                })
                break
            case "sorting": {
                const sort = event.sorting[0]
                setParams({
                    sort_by: sort?.id ?? null,
                    sort_order: sort ? (sort.desc ? "desc" : "asc") : null,
                    page: 1,
                })
                break
            }
        }
    }

    const queryClient = useQueryClient();
    const invalidateQuery = () => {
        // Prefix-match on the base route key so all paginated/sorted variants of
        // the list refetch — exact-matching the params object is flaky because
        // nuqs returns a fresh reference per render.
        queryClient.invalidateQueries({ queryKey, refetchType: "active" })
    }

    return {
        ...query,
        pagination,
        sorting,
        params,
        setParams,
        handleChange,
        invalidateQuery,
    }
}
