// src/components/signup-form.tsx
"use client";

import Link from "next/link";
import { useTranslations } from "next-intl";
import { useEffect, useState } from "react";
import { z } from "zod";
import { PasswordStrength } from "@/components/auth/password-strength";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
  Field,
  FieldDescription,
  FieldGroup,
  FieldLabel,
  FieldSeparator,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { normalizeEmail } from "@/lib/auth/email-guard";
import {
  MIN_PASSWORD_LENGTH,
  passwordSchema,
} from "@/lib/auth/password-policy";
import { postSignupOutcome } from "@/lib/auth/post-signup";
import {
  isValidUsername,
  MAX_USERNAME_LENGTH,
  MIN_USERNAME_LENGTH,
} from "@/lib/auth/username-policy";
import { authClient } from "@/lib/auth-client";
import { absoluteUrl, cn } from "@/lib/utils";

const AFTER_SIGNUP = "/onboarding";

// Client-side validation — the server enforces the same rules (email-guard +
// password policy in auth.ts hooks.before), this is for instant feedback.
const signupSchema = z.object({
  name: z.string().trim().min(1, "name").max(100, "name"),
  email: z.email("email"),
  password: passwordSchema,
});

type FieldErrors = Partial<Record<"name" | "email" | "password", true>>;

/** Better Auth duplicate-signup error codes (422 UNPROCESSABLE_ENTITY). */
function isExistingUserError(error: {
  code?: string;
  status?: number;
}): boolean {
  return (
    error.code === "USER_ALREADY_EXISTS" ||
    error.code === "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL" ||
    error.status === 422
  );
}

export function SignupForm({
  googleEnabled,
  githubEnabled,
  emailVerificationRequired,
  redirectTo,
  defaultEmail,
  className,
  ...props
}: React.ComponentProps<"div"> & {
  googleEnabled?: boolean;
  githubEnabled?: boolean;
  emailVerificationRequired?: boolean;
  redirectTo?: string;
  defaultEmail?: string;
}) {
  const t = useTranslations("Auth");
  const tCommon = useTranslations("Common");
  const [name, setName] = useState("");
  const [email, setEmail] = useState(defaultEmail ?? "");
  const [password, setPassword] = useState("");
  // Username is REQUIRED at signup (also enforced server-side in auth.ts
  // hooks.before). Invited users sign up through this same form/endpoint, so the
  // requirement applies to them too. `usernameStatus` drives the inline
  // availability hint; `usernameMissing` flags an empty handle on submit.
  const [username, setUsername] = useState("");
  const [usernameStatus, setUsernameStatus] = useState<
    "idle" | "invalid" | "checking" | "available" | "taken"
  >("idle");
  const [usernameMissing, setUsernameMissing] = useState(false);
  const [pending, setPending] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [fieldErrors, setFieldErrors] = useState<FieldErrors>({});
  const [existingAccount, setExistingAccount] = useState(false);
  const [verificationSent, setVerificationSent] = useState(false);
  // Land back here after verification (e.g. an invitation), default app home.
  const callbackURL = absoluteUrl(redirectTo ?? AFTER_SIGNUP);

  // Debounced username availability check. Validate the format locally first
  // (same rule the server enforces via usernameValidator) and only hit
  // isUsernameAvailable for well-formed, non-empty handles.
  useEffect(() => {
    const handle = username.trim();
    if (handle) setUsernameMissing(false);
    if (!handle) {
      setUsernameStatus("idle");
      return;
    }
    if (
      handle.length < MIN_USERNAME_LENGTH ||
      handle.length > MAX_USERNAME_LENGTH ||
      !isValidUsername(handle)
    ) {
      setUsernameStatus("invalid");
      return;
    }
    setUsernameStatus("checking");
    const id = setTimeout(async () => {
      const res = await authClient.isUsernameAvailable({ username: handle });
      setUsernameStatus(res.data?.available ? "available" : "taken");
    }, 400);
    return () => clearTimeout(id);
  }, [username]);

  // The server normalizes aliases (+tag, Gmail dots) — tell the user upfront
  // which address their account will actually use.
  const normalizedEmail = normalizeEmail(email);
  const emailIsAliased =
    email.includes("@") && normalizedEmail !== email.trim().toLowerCase();

  async function submit(e: React.FormEvent) {
    e.preventDefault();
    setError(null);
    setExistingAccount(false);
    const parsed = signupSchema.safeParse({ name, email, password });
    if (!parsed.success) {
      const errs: FieldErrors = {};
      for (const issue of parsed.error.issues) {
        const field = issue.path[0];
        if (field === "name" || field === "email" || field === "password") {
          errs[field] = true;
        }
      }
      setFieldErrors(errs);
      return;
    }
    // Username is required: reject an empty handle, and require it to be valid +
    // free before we submit. `checking` is treated as not-yet-OK (race guard).
    const handle = username.trim();
    if (!handle) {
      setUsernameMissing(true);
      setFieldErrors({});
      return;
    }
    if (usernameStatus !== "available") {
      setFieldErrors({});
      return;
    }
    setFieldErrors({});
    setPending(true);
    const res = await authClient.signUp.email({
      name: parsed.data.name,
      email: parsed.data.email,
      password: parsed.data.password,
      callbackURL,
      // Pass the raw handle: the server lowercases `username` and keeps the
      // original casing as `displayUsername`.
      username: handle,
    });
    if (res.error) {
      setPending(false);
      // Duplicate account → explicit message + link to sign in instead.
      if (isExistingUserError(res.error)) {
        setExistingAccount(true);
        return;
      }
      setError(res.error.message ?? t("signupFailed"));
      return;
    }
    const verificationRequired = Boolean(emailVerificationRequired);
    const outcome = postSignupOutcome({
      emailVerified: Boolean(res.data?.user?.emailVerified),
      emailVerificationRequired: verificationRequired,
    });

    if (outcome.kind === "redirect") {
      // Better Auth skips auto sign-in whenever requireEmailVerification is on
      // (= `emailVerificationRequired`), so no session was created. Invited
      // users are stamped verified at creation and land here: sign them in
      // explicitly before continuing (e.g. back to the invitation). With
      // verification disabled the signup already set the session cookie.
      if (verificationRequired) {
        const signIn = await authClient.signIn.email({ email, password });
        if (signIn.error) {
          setPending(false);
          setError(signIn.error.message ?? t("loginFailed"));
          return;
        }
      }
      // No setPending(false): we are leaving the page, keep the button busy.
      window.location.href = redirectTo ?? AFTER_SIGNUP;
      return;
    }

    // Account created but not signed in — they must confirm their email.
    setPending(false);
    setVerificationSent(true);
  }

  if (verificationSent) {
    return (
      <div className={cn("flex flex-col gap-6", className)} {...props}>
        <Card>
          <CardContent className="flex flex-col items-center gap-3 p-6 text-center md:p-8">
            <h1 className="font-bold text-2xl">{t("verifyEmailTitle")}</h1>
            <p className="text-balance text-muted-foreground">
              {t.rich("verifyEmailBody", {
                // The account is stored under the normalized address — show it.
                email: normalizedEmail,
                strong: (chunks) => <strong>{chunks}</strong>,
              })}
            </p>
            <Link
              href="/login"
              className="text-sm underline underline-offset-4"
            >
              {t("backToLogin")}
            </Link>
          </CardContent>
        </Card>
      </div>
    );
  }

  return (
    <div className={cn("flex flex-col gap-6", className)} {...props}>
      <Card>
        <CardContent className="p-6 md:p-8">
          <form onSubmit={submit}>
            <FieldGroup>
              <div className="flex flex-col items-center gap-2 text-center">
                <h1 className="font-bold text-2xl">{t("signupTitle")}</h1>
                <p className="text-balance text-muted-foreground">
                  {t("signupSubtitle")}
                </p>
              </div>
              <Field>
                <FieldLabel htmlFor="name">{t("nameLabel")}</FieldLabel>
                <Input
                  id="name"
                  value={name}
                  onChange={(e) => setName(e.target.value)}
                  aria-invalid={fieldErrors.name || undefined}
                  required
                />
                {fieldErrors.name ? (
                  <FieldDescription className="text-destructive">
                    {t("nameInvalid")}
                  </FieldDescription>
                ) : null}
              </Field>
              <Field>
                <FieldLabel htmlFor="email">{t("emailLabel")}</FieldLabel>
                <Input
                  id="email"
                  type="email"
                  placeholder={t("emailPlaceholder")}
                  value={email}
                  onChange={(e) => setEmail(e.target.value)}
                  aria-invalid={fieldErrors.email || undefined}
                  required
                />
                {fieldErrors.email ? (
                  <FieldDescription className="text-destructive">
                    {t("emailInvalid")}
                  </FieldDescription>
                ) : null}
                {emailIsAliased ? (
                  <FieldDescription>
                    {t("emailNormalizedNotice", { email: normalizedEmail })}
                  </FieldDescription>
                ) : null}
              </Field>
              <Field>
                <FieldLabel htmlFor="username">
                  {t("usernameLabel")}{" "}
                  <span className="text-muted-foreground">
                    {t("usernameRequired")}
                  </span>
                </FieldLabel>
                <Input
                  id="username"
                  value={username}
                  onChange={(e) => setUsername(e.target.value)}
                  autoComplete="username"
                  placeholder={t("usernamePlaceholder")}
                  required
                  aria-required
                  aria-invalid={
                    usernameMissing ||
                    usernameStatus === "invalid" ||
                    usernameStatus === "taken" ||
                    undefined
                  }
                />
                {usernameMissing ? (
                  <FieldDescription className="text-destructive">
                    {t("usernameMissing")}
                  </FieldDescription>
                ) : usernameStatus === "invalid" ? (
                  <FieldDescription className="text-destructive">
                    {t("usernameInvalid")}
                  </FieldDescription>
                ) : usernameStatus === "taken" ? (
                  <FieldDescription className="text-destructive">
                    {t("usernameTaken")}
                  </FieldDescription>
                ) : usernameStatus === "checking" ? (
                  <FieldDescription>{t("usernameChecking")}</FieldDescription>
                ) : usernameStatus === "available" ? (
                  <FieldDescription className="text-emerald-600 dark:text-emerald-400">
                    {t("usernameAvailable")}
                  </FieldDescription>
                ) : (
                  <FieldDescription>{t("usernameHint")}</FieldDescription>
                )}
              </Field>
              <Field>
                <FieldLabel htmlFor="password">{t("passwordLabel")}</FieldLabel>
                <Input
                  id="password"
                  type="password"
                  value={password}
                  onChange={(e) => setPassword(e.target.value)}
                  minLength={MIN_PASSWORD_LENGTH}
                  aria-invalid={fieldErrors.password || undefined}
                  required
                />
                <PasswordStrength password={password} />
                {fieldErrors.password ? (
                  <FieldDescription className="text-destructive">
                    {t("passwordTooWeak")}
                  </FieldDescription>
                ) : null}
              </Field>
              {existingAccount ? (
                <p className="text-destructive text-sm" role="alert">
                  {t("accountExists")}{" "}
                  <Link
                    href={`/login?email=${encodeURIComponent(normalizedEmail)}`}
                    className="underline underline-offset-4"
                  >
                    {t("signInLink")}
                  </Link>
                </p>
              ) : null}
              {error ? (
                <p className="text-destructive text-sm" role="alert">
                  {error}
                </p>
              ) : null}
              <Field>
                <Button type="submit" disabled={pending || !email || !password}>
                  {pending ? "…" : t("createAccountButton")}
                </Button>
              </Field>
              {googleEnabled || githubEnabled ? (
                <FieldSeparator>{tCommon("or")}</FieldSeparator>
              ) : null}
              {googleEnabled ? (
                <Field>
                  <Button
                    type="button"
                    variant="outline"
                    onClick={() =>
                      authClient.signIn.social({
                        provider: "google",
                        callbackURL,
                      })
                    }
                  >
                    {t("continueWithGoogle")}
                  </Button>
                </Field>
              ) : null}
              {githubEnabled ? (
                <Field>
                  <Button
                    type="button"
                    variant="outline"
                    onClick={() =>
                      authClient.signIn.social({
                        provider: "github",
                        callbackURL,
                      })
                    }
                  >
                    {t("continueWithGitHub")}
                  </Button>
                </Field>
              ) : null}
              <FieldDescription className="text-center">
                {t("alreadyHaveAccount")}{" "}
                <Link href="/login">{t("signInLink")}</Link>
              </FieldDescription>
            </FieldGroup>
          </form>
        </CardContent>
      </Card>
    </div>
  );
}
