import { useState, type FormEvent } from "react";
import { Link, useNavigate } from "react-router";
import axios from "axios";

import { client } from "@/client";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useAuth } from "@/context/auth-context";
import { AuthHeader, AuthPanel } from "./layout";
import {
  CaptchaField,
  FormMessage,
  PasswordInput,
  TextField,
} from "./components";
import { encryptPassword, fieldValue, getErrorMessage, passwordValue } from "./utils";

type LoginPayload = {
  username: string;
  password: string;
  captcha: string;
};

function isTwoFactorRequired(error: unknown) {
  return (
    axios.isAxiosError(error) &&
    (error.response?.status === 402 ||
      error.response?.data?.error === "two-factor authentication required")
  );
}

export default function LoginPage() {
  const navigate = useNavigate();
  const { refresh } = useAuth();
  const [submitting, setSubmitting] = useState(false);
  const [twoFactorSubmitting, setTwoFactorSubmitting] = useState(false);
  const [error, setError] = useState("");
  const [twoFactorError, setTwoFactorError] = useState("");
  const [twoFactorOpen, setTwoFactorOpen] = useState(false);
  const [pendingLogin, setPendingLogin] = useState<LoginPayload | null>(null);
  const [publicKey, setPublicKey] = useState("");

  const finishLogin = async (payload: LoginPayload, totpCode?: string) => {
    await client.authLogin({
      ...payload,
      totp_code: totpCode || undefined,
    });
    await refresh();
    navigate("/");
  };

  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setError("");
    setSubmitting(true);

    const form = new FormData(event.currentTarget);

    try {
      if (!publicKey) {
        throw new Error("Captcha is not ready. Please refresh it and try again.");
      }

      const payload = {
        username: fieldValue(form, "username"),
        password: encryptPassword(publicKey, passwordValue(form, "password")),
        captcha: fieldValue(form, "captcha"),
      };

      await finishLogin(payload);
    } catch (requestError) {
      if (isTwoFactorRequired(requestError)) {
        const form = new FormData(event.currentTarget);
        setPendingLogin({
          username: fieldValue(form, "username"),
          password: encryptPassword(publicKey, passwordValue(form, "password")),
          captcha: fieldValue(form, "captcha"),
        });
        setTwoFactorError("");
        setTwoFactorOpen(true);
      } else {
        setError(getErrorMessage(requestError));
      }
    } finally {
      setSubmitting(false);
    }
  };

  const handleTwoFactorSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    if (!pendingLogin) {
      return;
    }

    setTwoFactorError("");
    setTwoFactorSubmitting(true);

    try {
      const form = new FormData(event.currentTarget);
      await finishLogin(pendingLogin, fieldValue(form, "totpCode"));
    } catch (requestError) {
      setTwoFactorError(getErrorMessage(requestError));
    } finally {
      setTwoFactorSubmitting(false);
    }
  };

  return (
    <AuthPanel>
      <AuthHeader description="Please enter your details to sign in." title="Welcome back" />

      <form className="relative space-y-4 mt-10" onSubmit={(event) => void handleSubmit(event)}>
        <TextField
          id="username"
          label="Username or E-Mail Address"
          name="username"
          placeholder="Enter your username or email…"
        />

        <div className="space-y-2">
          <Label className="text-sm font-semibold text-foreground" htmlFor="password">
            Password
          </Label>
          <PasswordInput id="password" name="password" placeholder="Enter your password…" />
        </div>

        <CaptchaField onPublicKey={setPublicKey} />

        <div className="flex items-center justify-between gap-4 text-sm">
          <label className="flex items-center gap-3 font-medium text-foreground">
            <Checkbox className="size-4 rounded-md border-border" name="remember" />
            Remember me
          </label>
          <Link className="font-medium text-muted-foreground underline underline-offset-4 transition-colors hover:text-foreground" to="/auth/forgot-password">
            Forgot password?
          </Link>
        </div>

        {error && <FormMessage tone="error">{error}</FormMessage>}

        <Button
          className="h-11 w-full rounded-xl text-base shadow-inner shadow-primary-foreground/10"
          disabled={submitting}
          type="submit"
        >
          {submitting ? "Please wait…" : "Sign in"}
        </Button>
      </form>

      <footer className="mt-6 text-center text-base text-muted-foreground">
        Don&apos;t have an account yet?{" "}
        <Link className="font-semibold text-foreground transition-colors hover:text-primary" to="/auth/register">
          Sign Up
        </Link>
      </footer>

      <Dialog open={twoFactorOpen} onOpenChange={setTwoFactorOpen}>
        <DialogContent className="sm:max-w-90">
          <DialogHeader>
            <DialogTitle>Two-factor authentication</DialogTitle>
            <DialogDescription>
              Enter the 6-digit code from your authenticator app.
            </DialogDescription>
          </DialogHeader>

          <form className="space-y-4" onSubmit={(event) => void handleTwoFactorSubmit(event)}>
            <div className="space-y-2">
              <Label className="text-sm font-semibold" htmlFor="totpCode">
                Authentication code
              </Label>
              <Input
                autoComplete="one-time-code"
                autoFocus
                className="h-11 text-center text-lg tracking-[0.35em]"
                id="totpCode"
                inputMode="numeric"
                maxLength={6}
                name="totpCode"
                pattern="[0-9]{6}"
                placeholder="000000"
                required
              />
            </div>

            {twoFactorError && <FormMessage tone="error">{twoFactorError}</FormMessage>}

            <DialogFooter className="-mx-4 -mb-4">
              <Button
                className="h-10"
                disabled={twoFactorSubmitting}
                type="submit"
              >
                {twoFactorSubmitting ? "Verifying…" : "Verify"}
              </Button>
            </DialogFooter>
          </form>
        </DialogContent>
      </Dialog>
    </AuthPanel>
  );
}
