import { useState, ReactNode, useCallback, useMemo } from 'react'
import { useSearchParams } from 'react-router-dom'
import { useDebounceControl, useResolver } from '../hooks'
import { SearchParams, SearchResult } from '../types'
import { prune } from '../utils'
import { TagPicker } from '../views/settings/TagPicker'
import { DataTable, DataTableProps } from './DataTable'
import TextInput from './form/TextInput'
import Heading from './Heading'
import { SearchIcon } from './icons'
import Pagination from './Pagination'
import Stack from './Stack'

export interface SearchTableProps<T extends Record<string, any>> extends Omit<DataTableProps<T>, 'items'> {
    title?: ReactNode
    description?: ReactNode
    actions?: ReactNode
    results: SearchResult<T> | null
    params: SearchParams
    setParams: (params: SearchParams) => void
    enableSearch?: boolean
    searchPlaceholder?: string
    tagEntity?: 'journeys' | 'lists' | 'users' | 'campaigns' // anything else we want to tag?
}

const DEFAULT_ITEMS_PER_PAGE = 25
const DEFAULT_PAGE = 0

const toTableParams = (searchParams: URLSearchParams) => {
    return {
        page: parseInt(searchParams.get('page') ?? '0'),
        itemsPerPage: parseInt(searchParams.get('itemsPerPage') ?? '10'),
        q: searchParams.get('q') ?? undefined,
        tag: searchParams.getAll('tag'),
        sort: searchParams.get('sort') ?? undefined,
        direction: searchParams.get('direction') ?? undefined,
    }
}

const fromTableParams = (params: SearchParams): Record<string, string> => {
    return prune({
        page: params.page.toString(),
        itemsPerPage: params.itemsPerPage.toString(),
        q: params.q,
        tag: params.tag ?? [],
        sort: params.sort,
        direction: params.direction,
    })
}

export const useTableSearchParams = () => {
    const [searchParams, setSearchParams] = useSearchParams({
        page: DEFAULT_PAGE.toString(),
        itemsPerPage: DEFAULT_ITEMS_PER_PAGE.toString(),
    })

    const setParams = useCallback<(params: SearchParams | ((prev: SearchParams) => SearchParams)) => void>(next => {
        typeof next === 'function'
            ? setSearchParams(prev => fromTableParams(next(toTableParams(prev))))
            : setSearchParams(fromTableParams(next))
    }, [setSearchParams])

    const str = searchParams.toString()

    return useMemo(() => [
        toTableParams(new URLSearchParams(str)),
        setParams,
    ] as const, [str, setParams])
}

/**
 * local state
 */
export function useSearchTableState<T>(loader: (params: SearchParams) => Promise<SearchResult<T> | null>) {

    const [params, setParams] = useState<SearchParams>({
        page: 0,
        itemsPerPage: 25,
        q: '',
    })

    const [results,, reload] = useResolver(useCallback(async () => await loader(params), [loader, params]))

    return {
        params,
        reload,
        results,
        setParams,
    }
}

export interface SearchTableQueryState<T> {
    results: SearchResult<T> | null
    params: SearchParams
    reload: () => Promise<void>
    setParams: (params: SearchParams) => void
}

/**
 * global query string state
 */
export function useSearchTableQueryState<T>(loader: (params: SearchParams) => Promise<SearchResult<T> | null>): SearchTableQueryState<T> {

    const [params, setParams] = useTableSearchParams()

    const [results,, reload] = useResolver(useCallback(async () => await loader(params), [loader, params]))

    return {
        params,
        reload,
        results,
        setParams,
    }
}

export function SearchTable<T extends Record<string, any>>({
    actions,
    description,
    enableSearch,
    params,
    results,
    searchPlaceholder = 'Search...',
    setParams,
    tagEntity,
    title,
    ...rest
}: SearchTableProps<T>) {

    const [search, setSearch] = useDebounceControl(params.q ?? '', q => setParams({ ...params, q }))
    const columnSort = params.sort
        ? { sort: params.sort, direction: params.direction ?? 'asc' }
        : undefined
    const filters: ReactNode[] = []

    if (enableSearch) {
        filters.push(
            <TextInput
                key="search"
                name="search"
                value={search}
                placeholder={searchPlaceholder}
                onChange={setSearch}
                hideLabel={true}
                icon={<SearchIcon />}
            />,
        )
    }

    if (tagEntity) {
        filters.push(
            <TagPicker
                key="tags"
                entity={tagEntity}
                value={params.tag ?? []}
                onChange={tag => setParams({ ...params, tag })}
                placeholder="Filter By Tag..."
            />,
        )
    }

    return (
        <>
            {
                // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
                (title || actions || description) && (
                    <Heading
                        size="h3"
                        title={title}
                        actions={actions}
                    >
                        {description}
                    </Heading>
                )
            }
            {
                filters.length > 0 && (
                    <Stack>
                        {filters}
                    </Stack>
                )
            }
            <DataTable {...rest}
                items={results?.results}
                isLoading={!results}
                columnSort={columnSort}
                onColumnSort={(onSort) => {
                    const { sort, direction, ...prevParams } = params
                    setParams({ ...prevParams, ...onSort })
                }} />
            {results && (
                <Pagination
                    page={results.page}
                    total={results.pages}
                    itemsPerPage={results.itemsPerPage}
                    onChangePage={page => setParams({ ...params, page })} />
            )}
        </>
    )
}
