"use client";

import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { ColumnFilterButton } from "@app/components/ColumnFilterButton";
import { Button } from "@app/components/ui/button";
import {
    ControlledDataTable,
    type ExtendedColumnDef
} from "@app/components/ui/controlled-data-table";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useNavigationContext } from "@app/hooks/useNavigationContext";
import { toast } from "@app/hooks/useToast";
import { createApiClient, formatAxiosError } from "@app/lib/api";
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
import { type PaginationState } from "@tanstack/react-table";
import {
    ArrowDown01Icon,
    ArrowRight,
    ArrowUp10Icon,
    ChevronsUpDownIcon,
    MoreHorizontal
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import { useState, useTransition } from "react";
import { useDebouncedCallback } from "use-debounce";
import z from "zod";
import {
    DropdownMenu,
    DropdownMenuItem,
    DropdownMenuContent,
    DropdownMenuTrigger
} from "@app/components/ui/dropdown-menu";
import {
    Credenza,
    CredenzaContent,
    CredenzaDescription,
    CredenzaHeader,
    CredenzaTitle,
    CredenzaBody,
    CredenzaFooter,
    CredenzaClose
} from "@app/components/Credenza";
import CopyToClipboard from "@app/components/CopyToClipboard";

export type GlobalUserRow = {
    id: string;
    name: string | null;
    username: string;
    email: string | null;
    type: string;
    idpId: number | null;
    idpName: string;
    dateCreated: string;
    twoFactorEnabled: boolean | null;
    twoFactorSetupRequested: boolean | null;
    serverAdmin?: boolean;
};

type FilterOption = { value: string; label: string };

type Props = {
    users: GlobalUserRow[];
    pagination: PaginationState;
    rowCount: number;
    idpFilterOptions: FilterOption[];
};

type AdminGeneratePasswordResetCodeResponse = {
    token: string;
    email: string;
    url: string;
};

export default function UsersTable({
    users,
    pagination,
    rowCount,
    idpFilterOptions
}: Props) {
    const router = useRouter();
    const t = useTranslations();
    const api = createApiClient(useEnvContext());

    const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
    const [selected, setSelected] = useState<GlobalUserRow | null>(null);
    const [isPasswordResetCodeDialogOpen, setIsPasswordResetCodeDialogOpen] =
        useState(false);
    const [passwordResetCodeData, setPasswordResetCodeData] =
        useState<AdminGeneratePasswordResetCodeResponse | null>(null);
    const [isGeneratingCode, setIsGeneratingCode] = useState(false);

    const [isRefreshing, startTransition] = useTransition();
    const {
        navigate: filter,
        isNavigating: isFiltering,
        searchParams,
        pathname
    } = useNavigationContext();

    const idpIdParamSchema = z
        .union([z.literal("internal"), z.string().regex(/^\d+$/)])
        .optional()
        .catch(undefined);

    const twoFactorFilterSchema = z
        .enum(["true", "false"])
        .optional()
        .catch(undefined);

    function handleFilterChange(
        column: string,
        value: string | undefined | null
    ) {
        const sp = new URLSearchParams(searchParams);
        sp.delete(column);
        sp.delete("page");

        if (value) {
            sp.set(column, value);
        }
        startTransition(() => router.push(`${pathname}?${sp.toString()}`));
    }

    const refreshData = async () => {
        startTransition(async () => {
            try {
                await new Promise((resolve) => setTimeout(resolve, 200));
                router.refresh();
            } catch (error) {
                toast({
                    title: t("error"),
                    description: t("refreshError"),
                    variant: "destructive"
                });
            }
        });
    };

    const deleteUser = (id: string) => {
        startTransition(() => {
            void api
                .delete(`/user/${id}`)
                .catch((e) => {
                    console.error(t("userErrorDelete"), e);
                    toast({
                        variant: "destructive",
                        title: t("userErrorDelete"),
                        description: formatAxiosError(e, t("userErrorDelete"))
                    });
                })
                .then(() => {
                    router.refresh();
                    setIsDeleteModalOpen(false);
                    setSelected(null);
                });
        });
    };

    const generatePasswordResetCode = async (userId: string) => {
        setIsGeneratingCode(true);
        try {
            const res = await api.post(
                `/user/${userId}/generate-password-reset-code`
            );

            const envelope = res.data as {
                data?: AdminGeneratePasswordResetCodeResponse;
            };
            if (envelope?.data) {
                setPasswordResetCodeData(envelope.data);
                setIsPasswordResetCodeDialogOpen(true);
            }
        } catch (e) {
            console.error("Failed to generate password reset code", e);
            toast({
                variant: "destructive",
                title: t("error"),
                description: formatAxiosError(e, t("errorOccurred"))
            });
        } finally {
            setIsGeneratingCode(false);
        }
    };

    function toggleSort(column: string) {
        const newSearch = getNextSortOrder(column, searchParams);
        filter({
            searchParams: newSearch
        });
    }

    const handlePaginationChange = (newPage: PaginationState) => {
        searchParams.set("page", (newPage.pageIndex + 1).toString());
        searchParams.set("pageSize", newPage.pageSize.toString());
        filter({
            searchParams
        });
    };

    const handleSearchChange = useDebouncedCallback((query: string) => {
        searchParams.set("query", query);
        searchParams.delete("page");
        filter({
            searchParams
        });
    }, 300);

    const columns: ExtendedColumnDef<GlobalUserRow>[] = [
        {
            accessorKey: "id",
            friendlyName: "ID",
            header: () => <span className="p-3">ID</span>
        },
        {
            accessorKey: "username",
            enableHiding: false,
            friendlyName: t("username"),
            header: () => {
                const sortOrder = getSortDirection("username", searchParams);
                const Icon =
                    sortOrder === "asc"
                        ? ArrowDown01Icon
                        : sortOrder === "desc"
                          ? ArrowUp10Icon
                          : ChevronsUpDownIcon;
                return (
                    <Button
                        variant="ghost"
                        className="p-3"
                        onClick={() => toggleSort("username")}
                    >
                        {t("username")}
                        <Icon className="ml-2 h-4 w-4" />
                    </Button>
                );
            }
        },
        {
            accessorKey: "email",
            friendlyName: t("email"),
            header: () => {
                const sortOrder = getSortDirection("email", searchParams);
                const Icon =
                    sortOrder === "asc"
                        ? ArrowDown01Icon
                        : sortOrder === "desc"
                          ? ArrowUp10Icon
                          : ChevronsUpDownIcon;
                return (
                    <Button
                        variant="ghost"
                        className="p-3"
                        onClick={() => toggleSort("email")}
                    >
                        {t("email")}
                        <Icon className="ml-2 h-4 w-4" />
                    </Button>
                );
            }
        },
        {
            accessorKey: "name",
            friendlyName: t("name"),
            header: () => {
                const sortOrder = getSortDirection("name", searchParams);
                const Icon =
                    sortOrder === "asc"
                        ? ArrowDown01Icon
                        : sortOrder === "desc"
                          ? ArrowUp10Icon
                          : ChevronsUpDownIcon;
                return (
                    <Button
                        variant="ghost"
                        className="p-3"
                        onClick={() => toggleSort("name")}
                    >
                        {t("name")}
                        <Icon className="ml-2 h-4 w-4" />
                    </Button>
                );
            }
        },
        {
            accessorKey: "idpName",
            friendlyName: t("identityProvider"),
            header: () => (
                <ColumnFilterButton
                    options={idpFilterOptions}
                    selectedValue={idpIdParamSchema.parse(
                        searchParams.get("idp_id") ?? undefined
                    )}
                    onValueChange={(value) =>
                        handleFilterChange("idp_id", value)
                    }
                    searchPlaceholder={t("searchPlaceholder")}
                    emptyMessage={t("emptySearchOptions")}
                    label={t("identityProvider")}
                    className="p-3"
                />
            )
        },
        {
            accessorKey: "twoFactorEnabled",
            friendlyName: t("twoFactor"),
            header: () => (
                <ColumnFilterButton
                    options={[
                        { value: "true", label: t("enabled") },
                        { value: "false", label: t("disabled") }
                    ]}
                    selectedValue={twoFactorFilterSchema.parse(
                        searchParams.get("two_factor") ?? undefined
                    )}
                    onValueChange={(value) =>
                        handleFilterChange("two_factor", value)
                    }
                    searchPlaceholder={t("searchPlaceholder")}
                    emptyMessage={t("emptySearchOptions")}
                    label={t("twoFactor")}
                    className="p-3"
                />
            ),
            cell: ({ row }) => {
                const userRow = row.original;
                return (
                    <div className="flex flex-row items-center gap-2">
                        <span>
                            {userRow.twoFactorEnabled ||
                            userRow.twoFactorSetupRequested ? (
                                <span className="text-green-500">
                                    {t("enabled")}
                                </span>
                            ) : (
                                <span>{t("disabled")}</span>
                            )}
                        </span>
                    </div>
                );
            }
        },
        {
            id: "actions",
            enableHiding: false,
            header: () => <span className="p-3"></span>,
            cell: ({ row }) => {
                const r = row.original;
                return (
                    <div className="flex items-center gap-2 justify-end">
                        <DropdownMenu>
                            <DropdownMenuTrigger asChild>
                                <Button variant="ghost" className="h-8 w-8 p-0">
                                    <span className="sr-only">Open menu</span>
                                    <MoreHorizontal className="h-4 w-4" />
                                </Button>
                            </DropdownMenuTrigger>
                            <DropdownMenuContent align="end">
                                {r.type === "internal" && (
                                    <DropdownMenuItem
                                        disabled={isGeneratingCode}
                                        onClick={() => {
                                            void generatePasswordResetCode(
                                                r.id
                                            );
                                        }}
                                    >
                                        {t("generatePasswordResetCode")}
                                    </DropdownMenuItem>
                                )}
                                <DropdownMenuItem
                                    onClick={() => {
                                        setSelected(r);
                                        setIsDeleteModalOpen(true);
                                    }}
                                >
                                    {t("delete")}
                                </DropdownMenuItem>
                            </DropdownMenuContent>
                        </DropdownMenu>
                        <Button
                            variant={"outline"}
                            onClick={() => {
                                router.push(`/admin/users/${r.id}`);
                            }}
                        >
                            {t("edit")}
                            <ArrowRight className="ml-2 w-4 h-4" />
                        </Button>
                    </div>
                );
            }
        }
    ];

    return (
        <>
            {selected && (
                <ConfirmDeleteDialog
                    open={isDeleteModalOpen}
                    setOpen={(val) => {
                        setIsDeleteModalOpen(val);
                        setSelected(null);
                    }}
                    dialog={
                        <div className="space-y-2">
                            <p>
                                {t("userQuestionRemove", {
                                    selectedUser: selected
                                        ? getUserDisplayName({
                                              email: selected.email,
                                              name: selected.name,
                                              username: selected.username
                                          })
                                        : ""
                                })}
                            </p>

                            <p>
                                <b>{t("userMessageRemove")}</b>
                            </p>

                            <p>{t("userMessageConfirm")}</p>
                        </div>
                    }
                    buttonText={t("userDeleteConfirm")}
                    onConfirm={async () => deleteUser(selected!.id)}
                    string={getUserDisplayName({
                        email: selected.email,
                        name: selected.name,
                        username: selected.username
                    })}
                    title={t("userDeleteServer")}
                />
            )}

            <ControlledDataTable
                columns={columns}
                rows={users}
                tableId="admin-users-table"
                searchPlaceholder={t("userSearch")}
                pagination={pagination}
                onPaginationChange={handlePaginationChange}
                searchQuery={searchParams.get("query")?.toString()}
                onSearch={handleSearchChange}
                onRefresh={refreshData}
                isRefreshing={isRefreshing || isFiltering}
                rowCount={rowCount}
                enableColumnVisibility
                stickyLeftColumn="username"
                stickyRightColumn="actions"
            />

            <Credenza
                open={isPasswordResetCodeDialogOpen}
                onOpenChange={setIsPasswordResetCodeDialogOpen}
            >
                <CredenzaContent>
                    <CredenzaHeader>
                        <CredenzaTitle>
                            {t("passwordResetCodeGenerated")}
                        </CredenzaTitle>
                        <CredenzaDescription>
                            {t("passwordResetCodeGeneratedDescription")}
                        </CredenzaDescription>
                    </CredenzaHeader>
                    <CredenzaBody>
                        {passwordResetCodeData && (
                            <div className="space-y-4">
                                <div>
                                    <label className="text-sm font-medium mb-2 block">
                                        {t("email")}
                                    </label>
                                    <CopyToClipboard
                                        text={passwordResetCodeData.email}
                                    />
                                </div>
                                <div>
                                    <label className="text-sm font-medium mb-2 block">
                                        {t("passwordResetCode")}
                                    </label>
                                    <CopyToClipboard
                                        text={passwordResetCodeData.token}
                                    />
                                </div>
                                <div>
                                    <label className="text-sm font-medium mb-2 block">
                                        {t("passwordResetUrl")}
                                    </label>
                                    <CopyToClipboard
                                        text={passwordResetCodeData.url}
                                        isLink={true}
                                    />
                                </div>
                            </div>
                        )}
                    </CredenzaBody>
                    <CredenzaFooter>
                        <CredenzaClose asChild>
                            <Button variant="outline">{t("close")}</Button>
                        </CredenzaClose>
                    </CredenzaFooter>
                </CredenzaContent>
            </Credenza>
        </>
    );
}
