"use client";

import { useState } from "react";
import Link from "next/link";
import { forgotPassword, resetPassword, verifyResetCode } from "@/lib/api/auth";
import { AuthCard, CodeField, Field, FormAlert, PasswordField, Submit, readError, type FieldErrors } from "./Form";
import { useFinish, useNextQuery } from "./useFinish";

type Step = "email" | "code" | "password";

/** Email, then the 6-digit code, then a new password. A finished reset signs the user in. */
export default function ForgotForm() {
  const finish = useFinish();
  const q = useNextQuery();
  const [step, setStep] = useState<Step>("email");
  const [email, setEmail] = useState("");
  const [code, setCode] = useState("");
  const [password, setPassword] = useState("");
  const [confirm, setConfirm] = useState("");
  const [requestToken, setRequestToken] = useState<string | null>(null);
  const [resetToken, setResetToken] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);
  const [errors, setErrors] = useState<FieldErrors>({});
  const [message, setMessage] = useState<string | null>(null);

  const run = async (fn: () => Promise<void>) => {
    setBusy(true);
    setErrors({});
    setMessage(null);
    try {
      await fn();
    } catch (err) {
      const r = readError(err);
      setErrors(r.fields);
      setMessage(r.message);
    } finally {
      setBusy(false);
    }
  };

  const sendCode = (e: React.FormEvent) => {
    e.preventDefault();
    void run(async () => {
      // Unknown emails get the same answer (null token); the code step then simply fails, so nobody learns which emails exist.
      setRequestToken((await forgotPassword(email.trim())).reset_request_token);
      setStep("code");
    });
  };

  const checkCode = (e: React.FormEvent) => {
    e.preventDefault();
    // No token means the email had no account; answer exactly like a wrong code.
    if (!requestToken) return setMessage("Invalid or expired password reset code.");
    void run(async () => {
      setResetToken((await verifyResetCode(requestToken, code)).reset_token);
      setStep("password");
    });
  };

  const save = (e: React.FormEvent) => {
    e.preventDefault();
    if (password.length < 8) return setErrors({ password: ["Use at least 8 characters."] });
    if (confirm !== password) return setErrors({ password_confirmation: ["The passwords do not match."] });
    void run(async () => {
      const s = await resetPassword(resetToken as string, password, confirm);
      finish(s.token, s.user);
    });
  };

  return (
    <AuthCard
      title={step === "password" ? "Choose a new password" : "Reset your password"}
      lead={
        step === "email" ? (
          "Enter your account email and we will send you a 6-digit code."
        ) : step === "code" ? (
          <>
            If <strong>{email.trim()}</strong> has an account, a code is on its way.
          </>
        ) : (
          "Use at least 8 characters. You will be signed in afterwards."
        )
      }
    >
      {step === "email" && (
        <form className="acct-form" onSubmit={sendCode} noValidate>
          <Field label="Email" type="email" value={email} onChange={setEmail} error={errors.email} autoComplete="email" required autoFocus />
          {message && <FormAlert>{message}</FormAlert>}
          <Submit busy={busy}>Send code</Submit>
        </form>
      )}
      {step === "code" && (
        <form className="acct-form" onSubmit={checkCode} noValidate>
          <CodeField label="Code" value={code} onChange={setCode} error={errors.code} required autoFocus />
          {message && <FormAlert>{message}</FormAlert>}
          <Submit busy={busy}>Continue</Submit>
          <button type="button" className="acct-link acct-alt" onClick={() => setStep("email")}>
            Use a different email
          </button>
        </form>
      )}
      {step === "password" && (
        <form className="acct-form" onSubmit={save} noValidate>
          <PasswordField label="New password" value={password} onChange={setPassword} error={errors.password} autoComplete="new-password" required autoFocus />
          <PasswordField label="Confirm new password" value={confirm} onChange={setConfirm} error={errors.password_confirmation} autoComplete="new-password" required />
          {message && <FormAlert>{message}</FormAlert>}
          <Submit busy={busy}>Save and sign in</Submit>
        </form>
      )}
      <p className="acct-alt">
        Remembered it?{" "}
        <Link className="acct-link" href={`/login${q}`}>
          Back to sign in
        </Link>
      </p>
    </AuthCard>
  );
}
