import { zodResolver } from "@hookform/resolvers/zod";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { Save } from "lucide-react";
import { z } from "zod";
import { cn } from "~/client/lib/utils";
import { Button } from "../../../components/ui/button";
import {
	Form,
	FormControl,
	FormDescription,
	FormField,
	FormItem,
	FormLabel,
	FormMessage,
} from "../../../components/ui/form";
import { Input } from "../../../components/ui/input";
import { SecretInput } from "../../../components/ui/secret-input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../../components/ui/select";
import { Tooltip, TooltipContent, TooltipTrigger } from "../../../components/ui/tooltip";
import { useSystemInfo } from "~/client/hooks/use-system-info";
import { useScrollToFormError } from "~/client/hooks/use-scroll-to-form-error";
import {
	COMPRESSION_MODES,
	azureRepositoryConfigSchema,
	gcsRepositoryConfigSchema,
	localRepositoryConfigSchema,
	r2RepositoryConfigSchema,
	rcloneRepositoryConfigSchema,
	restRepositoryConfigSchema,
	s3RepositoryConfigSchema,
	sftpRepositoryConfigSchema,
} from "~/schemas/restic";
import { Checkbox } from "../../../components/ui/checkbox";
import {
	LocalRepositoryForm,
	S3RepositoryForm,
	R2RepositoryForm,
	GCSRepositoryForm,
	AzureRepositoryForm,
	RcloneRepositoryForm,
	RestRepositoryForm,
	SftpRepositoryForm,
	AdvancedForm,
} from "./repository-forms";
import { useServerFn } from "@tanstack/react-start";
import { getServerConstants } from "~/server/lib/functions/server-constants";
import { useSuspenseQuery } from "@tanstack/react-query";

const formBaseFields = {
	name: z.string().min(2).max(32),
	compressionMode: z.enum(COMPRESSION_MODES).optional(),
};

export const formSchema = z.discriminatedUnion("backend", [
	localRepositoryConfigSchema.extend(formBaseFields),
	s3RepositoryConfigSchema.extend(formBaseFields),
	r2RepositoryConfigSchema.extend(formBaseFields),
	gcsRepositoryConfigSchema.extend(formBaseFields),
	azureRepositoryConfigSchema.extend(formBaseFields),
	rcloneRepositoryConfigSchema.extend(formBaseFields),
	restRepositoryConfigSchema.extend(formBaseFields),
	sftpRepositoryConfigSchema.extend(formBaseFields),
]);

export type RepositoryFormValues = z.input<typeof formSchema>;

type Props = {
	onSubmit: (values: RepositoryFormValues) => void;
	mode?: "create" | "update";
	initialValues?: Partial<RepositoryFormValues>;
	formId?: string;
	loading?: boolean;
	className?: string;
};

const defaultValuesForType = (repoBase: string) => ({
	local: { backend: "local" as const, compressionMode: "auto" as const, path: repoBase },
	s3: { backend: "s3" as const, compressionMode: "auto" as const },
	r2: { backend: "r2" as const, compressionMode: "auto" as const },
	gcs: { backend: "gcs" as const, compressionMode: "auto" as const },
	azure: { backend: "azure" as const, compressionMode: "auto" as const },
	rclone: { backend: "rclone" as const, compressionMode: "auto" as const },
	rest: { backend: "rest" as const, compressionMode: "auto" as const },
	sftp: { backend: "sftp" as const, compressionMode: "auto" as const, port: 22, skipHostKeyCheck: false },
});

export const CreateRepositoryForm = ({
	onSubmit,
	mode = "create",
	initialValues,
	formId,
	loading,
	className,
}: Props) => {
	const getConstants = useServerFn(getServerConstants);
	const { data: constants } = useSuspenseQuery({
		queryKey: ["server-constants"],
		queryFn: getConstants,
	});

	const form = useForm<RepositoryFormValues>({
		resolver: zodResolver(formSchema, undefined, { raw: true }),
		defaultValues: initialValues,
		resetOptions: {
			keepDefaultValues: true,
			keepDirtyValues: false,
		},
	});

	const { watch, setValue } = form;

	const watchedBackend = watch("backend");
	const watchedIsExistingRepository = watch("isExistingRepository");

	const [passwordMode, setPasswordMode] = useState<"default" | "custom">("default");

	const { capabilities } = useSystemInfo();
	const scrollToFirstError = useScrollToFormError();

	return (
		<Form {...form}>
			<form
				id={formId}
				onSubmit={form.handleSubmit(onSubmit, scrollToFirstError)}
				className={cn("space-y-4", className)}
			>
				<FormField
					control={form.control}
					name="name"
					render={({ field }) => (
						<FormItem>
							<FormLabel>Name</FormLabel>
							<FormControl>
								<Input
									{...field}
									placeholder="Repository name"
									onChange={(e) => field.onChange(e.target.value)}
									maxLength={32}
									minLength={2}
								/>
							</FormControl>
							<FormDescription>Unique identifier for the repository.</FormDescription>
							<FormMessage />
						</FormItem>
					)}
				/>
				<FormField
					control={form.control}
					name="backend"
					render={({ field }) => (
						<FormItem>
							<FormLabel>Backend</FormLabel>
							<Select
								onValueChange={(value) => {
									field.onChange(value);
									form.reset({
										name: form.getValues().name,
										isExistingRepository: form.getValues().isExistingRepository,
										customPassword: form.getValues().customPassword,
										...defaultValuesForType(constants.REPOSITORY_BASE)[
											value as keyof ReturnType<typeof defaultValuesForType>
										],
									});
								}}
								value={field.value}
								disabled={mode === "update"}
							>
								<FormControl>
									<SelectTrigger>
										<SelectValue placeholder="Select a backend" />
									</SelectTrigger>
								</FormControl>
								<SelectContent>
									<SelectItem value="local">Local</SelectItem>
									<SelectItem value="s3">S3</SelectItem>
									<SelectItem value="r2">Cloudflare R2</SelectItem>
									<SelectItem value="gcs">Google Cloud Storage</SelectItem>
									<SelectItem value="azure">Azure Blob Storage</SelectItem>
									<SelectItem value="rest">REST Server</SelectItem>
									<SelectItem value="sftp">SFTP</SelectItem>
									<Tooltip>
										<TooltipTrigger>
											<SelectItem disabled={!capabilities.rclone} value="rclone">
												rclone (40+ cloud providers)
											</SelectItem>
										</TooltipTrigger>
										<TooltipContent className={cn({ hidden: capabilities.rclone })}>
											<p>Setup rclone to use this backend</p>
										</TooltipContent>
									</Tooltip>
								</SelectContent>
							</Select>
							<FormDescription>Choose the storage backend for this repository.</FormDescription>
							<FormMessage />
						</FormItem>
					)}
				/>

				<FormField
					control={form.control}
					name="compressionMode"
					render={({ field }) => (
						<FormItem>
							<FormLabel>Compression Mode</FormLabel>
							<Select onValueChange={field.onChange} value={field.value}>
								<FormControl>
									<SelectTrigger>
										<SelectValue placeholder="Select compression mode" />
									</SelectTrigger>
								</FormControl>
								<SelectContent>
									<SelectItem value="off">Off</SelectItem>
									<SelectItem value="auto">Auto (fast)</SelectItem>
									<SelectItem value="max">Max (slower, better compression)</SelectItem>
								</SelectContent>
							</Select>
							<FormDescription>Compression mode for backups stored in this repository.</FormDescription>
							<FormMessage />
						</FormItem>
					)}
				/>

				<FormField
					control={form.control}
					name="isExistingRepository"
					render={({ field }) => (
						<FormItem className="flex flex-row items-center space-x-3">
							<FormControl>
								<Checkbox
									checked={field.value}
									disabled={mode === "update"}
									onCheckedChange={(checked) => {
										field.onChange(checked);
										if (!checked) {
											setPasswordMode("default");
											setValue("customPassword", undefined);
										}
									}}
								/>
							</FormControl>
							<div className="space-y-1">
								<FormLabel>Import existing repository</FormLabel>
								<FormDescription>Check this if the repository already exists at the specified location</FormDescription>
							</div>
						</FormItem>
					)}
				/>
				{watchedIsExistingRepository && (
					<>
						<FormItem>
							<FormLabel>Repository Password</FormLabel>
							<Select
								onValueChange={(value) => {
									setPasswordMode(value as "default" | "custom");
									if (value === "default") {
										setValue("customPassword", undefined);
									}
								}}
								defaultValue={passwordMode}
								value={passwordMode}
							>
								<FormControl>
									<SelectTrigger>
										<SelectValue placeholder="Select password option" />
									</SelectTrigger>
								</FormControl>
								<SelectContent>
									<SelectItem value="default">Use the existing recovery key</SelectItem>
									<SelectItem value="custom">Enter password manually</SelectItem>
								</SelectContent>
							</Select>
							<FormDescription>
								Choose whether to use Zerobyte's recovery key (which you downloaded when creating your account) or enter
								a custom password for the existing repository.
							</FormDescription>
						</FormItem>

						{passwordMode === "custom" && (
							<FormField
								control={form.control}
								name="customPassword"
								render={({ field }) => (
									<FormItem>
										<FormLabel>Repository Password</FormLabel>
										<FormControl>
											<SecretInput
												placeholder="Enter repository password"
												value={field.value ?? ""}
												onChange={field.onChange}
											/>
										</FormControl>
										<FormDescription>
											The password used to encrypt this repository. It will be stored securely.
										</FormDescription>
										<FormMessage />
									</FormItem>
								)}
							/>
						)}
					</>
				)}

				{watchedBackend === "local" && <LocalRepositoryForm form={form} />}
				{watchedBackend === "s3" && <S3RepositoryForm form={form} />}
				{watchedBackend === "r2" && <R2RepositoryForm form={form} />}
				{watchedBackend === "gcs" && <GCSRepositoryForm form={form} />}
				{watchedBackend === "azure" && <AzureRepositoryForm form={form} />}
				{watchedBackend === "rclone" && <RcloneRepositoryForm form={form} />}
				{watchedBackend === "rest" && <RestRepositoryForm form={form} />}
				{watchedBackend === "sftp" && <SftpRepositoryForm form={form} />}

				{watchedBackend && watchedBackend !== "local" && <AdvancedForm form={form} />}

				{mode === "update" && (
					<Button type="submit" className="w-full" loading={loading}>
						<Save className="h-4 w-4 mr-2" />
						Save Changes
					</Button>
				)}
			</form>
		</Form>
	);
};
