import { zodResolver } from "@hookform/resolvers/zod";
import { useCallback, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { AuthLayout } from "~/client/components/auth-layout";
import { Button } from "~/client/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "~/client/components/ui/form";
import { Input } from "~/client/components/ui/input";
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "~/client/components/ui/input-otp";
import { Label } from "~/client/components/ui/label";
import { authClient } from "~/client/lib/auth-client";
import { logger } from "~/client/lib/logger";
import { RECOVERY_KEY_DOWNLOAD_SKIPPED_COOKIE_NAME } from "~/lib/recovery-key-skip";
import { decodeLoginError, getLoginErrorDescription } from "~/client/lib/sso-errors";
import { PASSKEY_LOGIN_FAILED_ERROR } from "~/lib/sso-errors";
import { ResetPasswordDialog } from "../components/reset-password-dialog";
import { useNavigate } from "@tanstack/react-router";
import { normalizeUsername } from "~/lib/username";
import { cn } from "~/client/lib/utils";
import { AlternativeSignInSection } from "../components/alternative-sign-in-section";
import { z } from "zod";

const loginSchema = z.object({
	username: z
		.string()
		.min(2, "Username must be at least 2 characters")
		.max(50, "Username must be at most 50 characters"),
	password: z.string().min(1, "Password is required"),
});

type LoginFormValues = z.input<typeof loginSchema>;

type LoginPageProps = {
	error?: string;
};

type PasskeySignInError = {
	code?: string;
	message?: string;
	status?: number;
	statusText?: string;
};

function isPasskeyVerificationFailure(error: PasskeySignInError | null) {
	return error?.code === "AUTHENTICATION_FAILED" || error?.code === "UNAUTHORIZED";
}

function hasSkippedRecoveryKeyDownload(userId: string) {
	return document.cookie
		.split(";")
		.some((cookie) => cookie.trim() === `${RECOVERY_KEY_DOWNLOAD_SKIPPED_COOKIE_NAME}=${userId}`);
}

export function LoginPage({ error }: LoginPageProps = {}) {
	const navigate = useNavigate();
	const [showResetDialog, setShowResetDialog] = useState(false);
	const [isLoggingIn, setIsLoggingIn] = useState(false);
	const [requires2FA, setRequires2FA] = useState(false);
	const [totpCode, setTotpCode] = useState("");
	const [isVerifying2FA, setIsVerifying2FA] = useState(false);
	const [trustDevice, setTrustDevice] = useState(false);
	const errorCode = decodeLoginError(error);
	const errorDescription = errorCode ? getLoginErrorDescription(errorCode) : null;

	const navigateAfterLogin = useCallback(async () => {
		const session = await authClient.getSession();

		if (
			session.data?.user &&
			!session.data.user.hasDownloadedResticPassword &&
			!hasSkippedRecoveryKeyDownload(session.data.user.id)
		) {
			void navigate({ to: "/download-recovery-key" });
		} else {
			void navigate({ to: "/volumes" });
		}
	}, [navigate]);

	useEffect(() => {
		const autoSignIn = async () => {
			if (
				typeof PublicKeyCredential === "undefined" ||
				!PublicKeyCredential.isConditionalMediationAvailable ||
				!(await PublicKeyCredential.isConditionalMediationAvailable())
			) {
				return;
			}

			const { data, error } = await authClient.signIn.passkey({
				autoFill: true,
			});

			if (isPasskeyVerificationFailure(error)) {
				void navigate({
					to: "/login",
					search: {
						error: PASSKEY_LOGIN_FAILED_ERROR,
					},
				});
				return;
			}

			if (data) {
				await navigateAfterLogin();
			}
		};

		void autoSignIn();
	}, [navigate, navigateAfterLogin]);

	const form = useForm<LoginFormValues>({
		resolver: zodResolver(loginSchema),
		defaultValues: {
			username: "",
			password: "",
		},
	});

	const onSubmit = async (values: LoginFormValues) => {
		const { data, error } = await authClient.signIn.username({
			username: normalizeUsername(values.username),
			password: values.password,
			fetchOptions: {
				onRequest: () => {
					setIsLoggingIn(true);
				},
				onResponse: () => {
					setIsLoggingIn(false);
				},
			},
		});

		if (error) {
			logger.error(error);
			toast.error("Login failed", { description: error.message });
			return;
		}

		if ("twoFactorRedirect" in data && data.twoFactorRedirect) {
			setRequires2FA(true);
			return;
		}

		await navigateAfterLogin();
	};

	const handleVerify2FA = async () => {
		if (totpCode.length !== 6) {
			toast.error("Please enter a 6-digit code");
			return;
		}

		const { data, error } = await authClient.twoFactor.verifyTotp({
			code: totpCode,
			trustDevice,
			fetchOptions: {
				onRequest: () => {
					setIsVerifying2FA(true);
				},
				onResponse: () => {
					setIsVerifying2FA(false);
				},
			},
		});

		if (error) {
			logger.error(error);
			toast.error("Verification failed", { description: error.message });
			setTotpCode("");
			return;
		}

		if (data) {
			toast.success("Login successful");
			const session = await authClient.getSession();
			if (
				session.data?.user &&
				!session.data.user.hasDownloadedResticPassword &&
				!hasSkippedRecoveryKeyDownload(session.data.user.id)
			) {
				void navigate({ to: "/download-recovery-key" });
			} else {
				void navigate({ to: "/volumes" });
			}
		}
	};

	const handleBackToLogin = () => {
		setRequires2FA(false);
		setTotpCode("");
		setTrustDevice(false);
		form.reset();
	};

	if (requires2FA) {
		return (
			<AuthLayout
				title="Two-Factor Authentication"
				description="Enter the 6-digit code from your authenticator app"
			>
				<div className="space-y-6">
					<div className="space-y-4 flex flex-col items-center">
						<Label htmlFor="totp-code">Authentication code</Label>
						<div>
							<InputOTP
								maxLength={6}
								value={totpCode}
								onChange={setTotpCode}
								onComplete={handleVerify2FA}
								disabled={isVerifying2FA}
							>
								<InputOTPGroup>
									<InputOTPSlot index={0} />
									<InputOTPSlot index={1} />
									<InputOTPSlot index={2} />
								</InputOTPGroup>
								<InputOTPSeparator />
								<InputOTPGroup>
									<InputOTPSlot index={3} />
									<InputOTPSlot index={4} />
									<InputOTPSlot index={5} />
								</InputOTPGroup>
							</InputOTP>
						</div>
					</div>

					<div className="flex items-center space-x-2">
						<input
							type="checkbox"
							id="trust-device"
							aria-label="Trust this device for 30 days"
							checked={trustDevice}
							onChange={(e) => setTrustDevice(e.target.checked)}
							className="h-4 w-4"
						/>
						<label htmlFor="trust-device" className="text-sm text-muted-foreground cursor-pointer">
							Trust this device for 30 days
						</label>
					</div>

					<div className="space-y-2">
						<Button
							type="button"
							className="w-full"
							loading={isVerifying2FA}
							onClick={handleVerify2FA}
							disabled={totpCode.length !== 6}
						>
							Verify
						</Button>
						<Button
							type="button"
							variant="outline"
							className="w-full"
							onClick={handleBackToLogin}
							disabled={isVerifying2FA}
						>
							Back to Login
						</Button>
					</div>
				</div>
			</AuthLayout>
		);
	}

	return (
		<AuthLayout title="Login to your account" description="Enter your credentials below to login to your account">
			<Form {...form}>
				<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
					<div
						className={cn("rounded-md border border-destructive/50 p-3 text-sm", {
							hidden: !errorDescription,
						})}
					>
						{errorDescription}
					</div>
					<FormField
						control={form.control}
						name="username"
						render={({ field }) => (
							<FormItem>
								<FormLabel>Username</FormLabel>
								<FormControl>
									<Input
										{...field}
										type="text"
										placeholder="admin"
										disabled={isLoggingIn}
										autoComplete="username webauthn"
									/>
								</FormControl>
								<FormMessage />
							</FormItem>
						)}
					/>
					<FormField
						control={form.control}
						name="password"
						render={({ field }) => (
							<FormItem>
								<div className="flex items-center justify-between">
									<FormLabel>Password</FormLabel>
									<button
										type="button"
										className="text-xs text-muted-foreground hover:underline"
										onClick={() => setShowResetDialog(true)}
									>
										Forgot your password?
									</button>
								</div>
								<FormControl>
									<Input
										{...field}
										type="password"
										disabled={isLoggingIn}
										autoComplete="current-password webauthn"
									/>
								</FormControl>
								<FormMessage />
							</FormItem>
						)}
					/>
					<Button type="submit" className="w-full" loading={isLoggingIn}>
						Login
					</Button>
				</form>
			</Form>

			<AlternativeSignInSection onPasskeySignIn={navigateAfterLogin} />

			<ResetPasswordDialog open={showResetDialog} onOpenChange={setShowResetDialog} />
		</AuthLayout>
	);
}
