"use client";

import { Button } from "@app/components/ui/button";
import {
    Form,
    FormControl,
    FormDescription,
    FormField,
    FormItem,
    FormLabel,
    FormMessage
} from "@app/components/ui/form";
import { Input } from "@app/components/ui/input";
import {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue
} from "@app/components/ui/select";
import { useToast } from "@app/hooks/useToast";
import { zodResolver } from "@hookform/resolvers/zod";
import { InviteUserBody, InviteUserResponse } from "@server/routers/user";
import { AxiosResponse } from "axios";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import CopyTextBox from "@app/components/CopyTextBox";
import {
    Credenza,
    CredenzaBody,
    CredenzaClose,
    CredenzaContent,
    CredenzaDescription,
    CredenzaFooter,
    CredenzaHeader,
    CredenzaTitle
} from "@app/components/Credenza";
import { useOrgContext } from "@app/hooks/useOrgContext";
import { ListRolesResponse } from "@server/routers/role";
import { cn, formatAxiosError } from "@app/lib/utils";
import { createApiClient } from "@app/api";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { ListResourcesResponse } from "@server/routers/resource";
import {
    Popover,
    PopoverContent,
    PopoverTrigger
} from "@app/components/ui/popover";
import { CaretSortIcon } from "@radix-ui/react-icons";
import {
    Command,
    CommandEmpty,
    CommandGroup,
    CommandInput,
    CommandItem,
    CommandList
} from "@app/components/ui/command";
import { CheckIcon } from "lucide-react";
import { register } from "module";
import { Label } from "@app/components/ui/label";
import { Checkbox } from "@app/components/ui/checkbox";
import { GenerateAccessTokenResponse } from "@server/routers/accessToken";
import { constructShareLink } from "@app/lib/shareLinks";
import { ShareLinkRow } from "./ShareLinksTable";

type FormProps = {
    open: boolean;
    setOpen: (open: boolean) => void;
    onCreated?: (result: ShareLinkRow) => void;
};

const formSchema = z.object({
    resourceId: z.number({ message: "Please select a resource" }),
    resourceName: z.string(),
    timeUnit: z.string(),
    timeValue: z.coerce.number().int().positive().min(1),
    title: z.string().optional()
});

export default function CreateShareLinkForm({
    open,
    setOpen,
    onCreated
}: FormProps) {
    const { toast } = useToast();
    const { org } = useOrgContext();

    const api = createApiClient(useEnvContext());

    const [link, setLink] = useState<string | null>(null);
    const [loading, setLoading] = useState(false);
    const [neverExpire, setNeverExpire] = useState(false);

    const [resources, setResources] = useState<
        { resourceId: number; name: string }[]
    >([]);

    const timeUnits = [
        { unit: "minutes", name: "Minutes" },
        { unit: "hours", name: "Hours" },
        { unit: "days", name: "Days" },
        { unit: "weeks", name: "Weeks" },
        { unit: "months", name: "Months" },
        { unit: "years", name: "Years" }
    ];

    const form = useForm<z.infer<typeof formSchema>>({
        resolver: zodResolver(formSchema),
        defaultValues: {
            timeUnit: "days",
            timeValue: 30,
            title: ""
        }
    });

    useEffect(() => {
        if (!open) {
            return;
        }

        async function fetchResources() {
            const res = await api
                .get<
                    AxiosResponse<ListResourcesResponse>
                >(`/org/${org?.org.orgId}/resources`)
                .catch((e) => {
                    console.error(e);
                    toast({
                        variant: "destructive",
                        title: "Failed to fetch resources",
                        description: formatAxiosError(
                            e,
                            "An error occurred while fetching the resources"
                        )
                    });
                });

            if (res?.status === 200) {
                setResources(res.data.data.resources);
            }
        }

        fetchResources();
    }, [open]);

    async function onSubmit(values: z.infer<typeof formSchema>) {
        setLoading(true);

        // convert time to seconds
        let timeInSeconds = values.timeValue;
        switch (values.timeUnit) {
            case "minutes":
                timeInSeconds *= 60;
                break;
            case "hours":
                timeInSeconds *= 60 * 60;
                break;
            case "days":
                timeInSeconds *= 60 * 60 * 24;
                break;
            case "weeks":
                timeInSeconds *= 60 * 60 * 24 * 7;
                break;
            case "months":
                timeInSeconds *= 60 * 60 * 24 * 30;
                break;
            case "years":
                timeInSeconds *= 60 * 60 * 24 * 365;
                break;
        }

        const res = await api
            .post<AxiosResponse<GenerateAccessTokenResponse>>(
                `/resource/${values.resourceId}/access-token`,
                {
                    validForSeconds: neverExpire ? undefined : timeInSeconds,
                    title:
                        values.title ||
                        `${values.resourceName || "Resource" + values.resourceId} Share Link`
                }
            )
            .catch((e) => {
                console.error(e);
                toast({
                    variant: "destructive",
                    title: "Failed to create share link",
                    description: formatAxiosError(
                        e,
                        "An error occurred while creating the share link"
                    )
                });
            });

        if (res && res.data.data.accessTokenId) {
            const token = res.data.data;
            const link = constructShareLink(
                values.resourceId,
                token.accessTokenId,
                token.tokenHash
            );
            setLink(link);
            onCreated?.({
                ...token,
                resourceName: values.resourceName
            });
        }

        setLoading(false);
    }

    return (
        <>
            <Credenza
                open={open}
                onOpenChange={(val) => {
                    setOpen(val);
                    setLink(null);
                    setLoading(false);
                    form.reset();
                }}
            >
                <CredenzaContent>
                    <CredenzaHeader>
                        <CredenzaTitle>Create Sharable Link</CredenzaTitle>
                        <CredenzaDescription>
                            Anyone with this link can access the resource
                        </CredenzaDescription>
                    </CredenzaHeader>
                    <CredenzaBody>
                        <div className="space-y-8">
                            {!link && (
                                <Form {...form}>
                                    <form
                                        onSubmit={form.handleSubmit(onSubmit)}
                                        className="space-y-4"
                                        id="share-link-form"
                                    >
                                        <FormField
                                            control={form.control}
                                            name="resourceId"
                                            render={({ field }) => (
                                                <FormItem className="flex flex-col">
                                                    <FormLabel className="mb-2">
                                                        Resource
                                                    </FormLabel>
                                                    <Popover>
                                                        <PopoverTrigger asChild>
                                                            <FormControl>
                                                                <Button
                                                                    variant="outline"
                                                                    role="combobox"
                                                                    className={cn(
                                                                        "justify-between",
                                                                        !field.value &&
                                                                            "text-muted-foreground"
                                                                    )}
                                                                >
                                                                    {field.value
                                                                        ? resources.find(
                                                                              (
                                                                                  r
                                                                              ) =>
                                                                                  r.resourceId ===
                                                                                  field.value
                                                                          )
                                                                              ?.name
                                                                        : "Select resource"}
                                                                    <CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
                                                                </Button>
                                                            </FormControl>
                                                        </PopoverTrigger>
                                                        <PopoverContent className="p-0">
                                                            <Command>
                                                                <CommandInput placeholder="Search resources..." />
                                                                <CommandList>
                                                                    <CommandEmpty>
                                                                        No
                                                                        resources
                                                                        found
                                                                    </CommandEmpty>
                                                                    <CommandGroup>
                                                                        {resources.map(
                                                                            (
                                                                                r
                                                                            ) => (
                                                                                <CommandItem
                                                                                    value={r.name}
                                                                                    key={
                                                                                        r.resourceId
                                                                                    }
                                                                                    onSelect={() => {
                                                                                        form.setValue(
                                                                                            "resourceId",
                                                                                            r.resourceId
                                                                                        );
                                                                                        form.setValue(
                                                                                            "resourceName",
                                                                                            r.name
                                                                                        );
                                                                                    }}
                                                                                >
                                                                                    <CheckIcon
                                                                                        className={cn(
                                                                                            "mr-2 h-4 w-4",
                                                                                            r.resourceId ===
                                                                                                field.value
                                                                                                ? "opacity-100"
                                                                                                : "opacity-0"
                                                                                        )}
                                                                                    />
                                                                                    {
                                                                                        r.name
                                                                                    }
                                                                                </CommandItem>
                                                                            )
                                                                        )}
                                                                    </CommandGroup>
                                                                </CommandList>
                                                            </Command>
                                                        </PopoverContent>
                                                    </Popover>
                                                    <FormMessage />
                                                </FormItem>
                                            )}
                                        />

                                        <FormField
                                            control={form.control}
                                            name="title"
                                            render={({ field }) => (
                                                <FormItem>
                                                    <Label>
                                                        Title (optional)
                                                    </Label>
                                                    <FormControl>
                                                        <Input
                                                            placeholder="Enter title"
                                                            {...field}
                                                        />
                                                    </FormControl>
                                                    <FormMessage />
                                                </FormItem>
                                            )}
                                        />

                                        <div className="space-y-4">
                                            <Label>Expire In</Label>
                                            <div className="grid grid-cols-2 gap-4 mt-2">
                                                <FormField
                                                    control={form.control}
                                                    name="timeUnit"
                                                    render={({ field }) => (
                                                        <FormItem>
                                                            <Select
                                                                onValueChange={
                                                                    field.onChange
                                                                }
                                                                defaultValue={field.value.toString()}
                                                            >
                                                                <FormControl>
                                                                    <SelectTrigger>
                                                                        <SelectValue placeholder="Select duration" />
                                                                    </SelectTrigger>
                                                                </FormControl>
                                                                <SelectContent>
                                                                    {timeUnits.map(
                                                                        (
                                                                            option
                                                                        ) => (
                                                                            <SelectItem
                                                                                key={
                                                                                    option.unit
                                                                                }
                                                                                value={
                                                                                    option.unit
                                                                                }
                                                                            >
                                                                                {
                                                                                    option.name
                                                                                }
                                                                            </SelectItem>
                                                                        )
                                                                    )}
                                                                </SelectContent>
                                                            </Select>
                                                            <FormMessage />
                                                        </FormItem>
                                                    )}
                                                />

                                                <FormField
                                                    control={form.control}
                                                    name="timeValue"
                                                    render={({ field }) => (
                                                        <FormItem>
                                                            <FormControl>
                                                                <Input
                                                                    type="number"
                                                                    min={1}
                                                                    placeholder="Enter duration"
                                                                    {...field}
                                                                />
                                                            </FormControl>
                                                            <FormMessage />
                                                        </FormItem>
                                                    )}
                                                />
                                            </div>

                                            <div className="flex items-center space-x-2">
                                                <Checkbox
                                                    id="terms"
                                                    checked={neverExpire}
                                                    onCheckedChange={(val) =>
                                                        setNeverExpire(
                                                            val as boolean
                                                        )
                                                    }
                                                />
                                                <label
                                                    htmlFor="terms"
                                                    className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
                                                >
                                                    Never expire
                                                </label>
                                            </div>

                                            <p className="text-sm text-muted-foreground">
                                                Expiration time is how long the
                                                link will be usable and provide
                                                access to the resource. After
                                                this time, the link will expire
                                                and no longer work, and users
                                                who used this link will lose
                                                access to the resource.
                                            </p>
                                        </div>
                                    </form>
                                </Form>
                            )}
                            {link && (
                                <div className="max-w-md space-y-4">
                                    <p>
                                        Anyone with this link can access the
                                        resource. Share it with care.
                                    </p>
                                    <CopyTextBox text={link} wrapText={false} />
                                </div>
                            )}
                        </div>
                    </CredenzaBody>
                    <CredenzaFooter>
                        <Button
                            type="submit"
                            form="share-link-form"
                            loading={loading}
                            disabled={link !== null || loading}
                        >
                            Create Link
                        </Button>
                        <CredenzaClose asChild>
                            <Button variant="outline">Close</Button>
                        </CredenzaClose>
                    </CredenzaFooter>
                </CredenzaContent>
            </Credenza>
        </>
    );
}
