"use client"

import React, { useEffect, useMemo, useRef, useState } from "react"
import { SearchIcon } from "lucide-react"
import { Input } from "@/shared/components/ui/input"
import { Tabs, TabsList, TabsTrigger } from "@/shared/components/ui/tabs"
import { formatEnum } from "@/shared/utils/formatters"
import { DataTable, type ActionsColumnOptions, type DataViewProps } from "@/shared/data-view/table-view"
import { useResourcePage, type UseResourcePageOptions, type ResourceItem, type ResourcePageClient } from "./use-resource-page"
import type { ColumnDef } from "@tanstack/react-table"

export type StatusFilterConfig = {
    /** Allowed status values (typically the matching `<Resource>Status` enum). */
    statuses: readonly string[]
    /** Query-param key forwarded to the API. Defaults to "status". */
    paramKey?: string
    /** Initial selected tab. Defaults to "all". */
    defaultValue?: string
    /** Label for the catch-all tab. Defaults to "All". */
    allLabel?: string
    /** Per-status label transform. Defaults to formatEnum (snake_case → Title Case). */
    formatLabel?: (status: string) => string
}

type LooseColumnDef<TData> = ColumnDef<TData, unknown> | (Omit<ColumnDef<TData, unknown>, "accessorKey"> & { accessorKey: string })

export type CrudResourceColumnHelpers<TClient extends ResourcePageClient> = {
    actionsColumn: (options?: Partial<ActionsColumnOptions<ResourceItem<TClient>>>) => ColumnDef<ResourceItem<TClient>, unknown>
    openEdit: (row: ResourceItem<TClient>) => void
    deleteItem: (id: string) => Promise<unknown>
}

export type CrudResourceContext<TClient extends ResourcePageClient> = {
    selectedItem: ResourceItem<TClient> | null
    isDialogOpen: boolean
    dialogResourceId: string | null
    isLoading: boolean
    data: ResourceItem<TClient>[]
    openCreate: () => void
    openEdit: (row: ResourceItem<TClient>) => void
    closeDialog: () => void
    deleteItem: (id: string) => Promise<unknown>
    invalidateQuery: () => void
}

type ReactNodeOrRender<TClient extends ResourcePageClient> =
    | React.ReactNode
    | ((context: CrudResourceContext<TClient>) => React.ReactNode)

type ManagedTableProps = "columns" | "data" | "pagination" | "sorting" | "onChange" | "isLoading"

export type CrudResourceProps<TClient extends ResourcePageClient> = UseResourcePageOptions<TClient> & {
    columns: LooseColumnDef<ResourceItem<TClient>>[] | ((helpers: CrudResourceColumnHelpers<TClient>) => LooseColumnDef<ResourceItem<TClient>>[])
    onRowClick?: (row: ResourceItem<TClient>) => void
    tableHeader?: ReactNodeOrRender<TClient>
    tableProps?: Omit<Partial<DataViewProps<ResourceItem<TClient>>>, ManagedTableProps>
    render?: (table: React.ReactElement, context: CrudResourceContext<TClient>) => React.ReactElement
    /** Render a built-in search input above the table and forward `search` to the API. */
    searchable?: boolean
    /** Placeholder for the built-in search input. */
    searchPlaceholder?: string
    /** Render a built-in status tab strip above the table and forward the selected status to the API. */
    statusFilter?: StatusFilterConfig
}

export function CrudResource<TClient extends ResourcePageClient>({
    columns: columnsProp,
    routeKey,
    getClient,
    queryOptions,
    paramKey,
    extraParams,
    localState,
    onRowClick,
    tableHeader,
    tableProps,
    render,
    searchable = false,
    searchPlaceholder = "Search...",
    statusFilter,
}: CrudResourceProps<TClient>) {
    type TItem = ResourceItem<TClient>

    const [searchInput, setSearchInput] = useState("")
    const [search, setSearch] = useState("")
    const statusParamKey = statusFilter?.paramKey ?? "status"
    const statusAllValue = statusFilter?.defaultValue ?? "all"
    const [statusValue, setStatusValue] = useState<string>(statusAllValue)

    useEffect(() => {
        const timer = setTimeout(() => setSearch(searchInput.trim()), 300)
        return () => clearTimeout(timer)
    }, [searchInput])

    const mergedExtraParams = useMemo(() => {
        const base = { ...(extraParams ?? {}) } as Record<string, unknown>
        if (searchable && search) base.search = search
        if (statusFilter && statusValue !== statusAllValue) {
            base[statusParamKey] = statusValue
        }
        return base
    }, [extraParams, search, searchable, statusFilter, statusValue, statusAllValue, statusParamKey])

    const page = useResourcePage<TClient>({
        routeKey,
        getClient,
        queryOptions,
        paramKey,
        extraParams: mergedExtraParams,
        localState,
    })

    const prevSearchRef = useRef(search)
    const prevStatusRef = useRef(statusValue)
    const setParamsRef = useRef(page.setParams)
    setParamsRef.current = page.setParams
    useEffect(() => {
        if (prevSearchRef.current !== search) {
            prevSearchRef.current = search
            setParamsRef.current({ page: 1 })
        }
    }, [search])
    useEffect(() => {
        if (prevStatusRef.current !== statusValue) {
            prevStatusRef.current = statusValue
            setParamsRef.current({ page: 1 })
        }
    }, [statusValue])

    const columns = typeof columnsProp === "function"
        ? columnsProp({
            actionsColumn: page.actionsColumn,
            openEdit: page.openEdit,
            deleteItem: page.deleteItem,
        })
        : columnsProp

    type ListResponse = { data?: TItem[] }
    const responseData = page.data as ListResponse | undefined
    const items = (responseData?.data ?? []) as TItem[]

    const context: CrudResourceContext<TClient> = {
        selectedItem: page.selectedItem,
        isDialogOpen: page.isDialogOpen,
        dialogResourceId: page.dialogResourceId,
        isLoading: page.isLoading,
        data: items,
        openCreate: page.openCreate,
        openEdit: page.openEdit,
        closeDialog: page.closeDialog,
        deleteItem: page.deleteItem,
        invalidateQuery: () => page.invalidateQuery(),
    }

    const searchInputEl = searchable ? (
        <div className="relative w-64">
            <SearchIcon className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
            <Input
                placeholder={searchPlaceholder}
                value={searchInput}
                onChange={(e) => setSearchInput(e.target.value)}
                className="pl-8"
            />
        </div>
    ) : null

    const statusTabsEl = statusFilter ? (
        <Tabs value={statusValue} onValueChange={setStatusValue}>
            <TabsList variant="line">
                <TabsTrigger value={statusAllValue}>{statusFilter.allLabel ?? "All"}</TabsTrigger>
                {statusFilter.statuses.map((status) => (
                    <TabsTrigger key={status} value={status}>
                        {(statusFilter.formatLabel ?? formatEnum)(status)}
                    </TabsTrigger>
                ))}
            </TabsList>
        </Tabs>
    ) : null

    const resolvedTableHeader = tableHeader
        ? (typeof tableHeader === "function" ? tableHeader(context) : tableHeader)
        : null

    const leftHeader = statusTabsEl ?? resolvedTableHeader
    const extraHeaderAbove = statusTabsEl && resolvedTableHeader ? resolvedTableHeader : null

    const headerRow = (searchInputEl || leftHeader) ? (
        <div className="flex flex-wrap items-center justify-between gap-2">
            <div className="flex-1 min-w-0">{leftHeader}</div>
            {searchInputEl}
        </div>
    ) : null

    const table = (
        <>
            {extraHeaderAbove}
            {headerRow}
            <DataTable
                {...tableProps}
                columns={columns as ColumnDef<TItem, any>[]}
                data={items}
                pagination={page.pagination}
                sorting={page.sorting}
                onChange={page.handleChange}
                isLoading={page.isLoading}
                onRowClick={onRowClick ?? tableProps?.onRowClick}
            />
        </>
    )

    if (render) return render(table, context)
    return table
}
