import { createHash, randomString, toRequest } from "../../utils/web.js";
import { AccessDenied } from "../../../errors.js";
/**
 * Starts an e-mail login flow, by generating a token,
 * and sending it to the user's e-mail (with the help of a DB adapter).
 * At the end, it returns a redirect to the `verify-request` page.
 */
export async function sendToken(request, options) {
    const { body } = request;
    const { provider, callbacks, adapter } = options;
    const normalizer = provider.normalizeIdentifier ?? defaultNormalizer;
    const email = normalizer(body?.email);
    const defaultUser = { id: crypto.randomUUID(), email, emailVerified: null };
    const user = (await adapter.getUserByEmail(email)) ?? defaultUser;
    const account = {
        providerAccountId: email,
        userId: user.id,
        type: "email",
        provider: provider.id,
    };
    let authorized;
    try {
        authorized = await callbacks.signIn({
            user,
            account,
            email: { verificationRequest: true },
        });
    }
    catch (e) {
        throw new AccessDenied(e);
    }
    if (!authorized)
        throw new AccessDenied("AccessDenied");
    if (typeof authorized === "string") {
        return {
            redirect: await callbacks.redirect({
                url: authorized,
                baseUrl: options.url.origin,
            }),
        };
    }
    const { callbackUrl, theme } = options;
    const token = (await provider.generateVerificationToken?.()) ?? randomString(32);
    const ONE_DAY_IN_SECONDS = 86400;
    const expires = new Date(Date.now() + (provider.maxAge ?? ONE_DAY_IN_SECONDS) * 1000);
    const secret = provider.secret ?? options.secret;
    const baseUrl = new URL(options.basePath, options.url.origin);
    const sendRequest = provider.sendVerificationRequest({
        identifier: email,
        token,
        expires,
        url: `${baseUrl}/callback/${provider.id}?${new URLSearchParams({
            callbackUrl,
            token,
            email,
        })}`,
        provider,
        theme,
        request: toRequest(request),
    });
    const createToken = adapter.createVerificationToken?.({
        identifier: email,
        token: await createHash(`${token}${secret}`),
        expires,
    });
    await Promise.all([sendRequest, createToken]);
    return {
        redirect: `${baseUrl}/verify-request?${new URLSearchParams({
            provider: provider.id,
            type: provider.type,
        })}`,
    };
}
function defaultNormalizer(email) {
    if (!email)
        throw new Error("Missing email from request body.");
    const trimmedEmail = email.toLowerCase().trim();
    // Reject email addresses with quotes to prevent address parser confusion
    // This prevents attacks like "attacker@evil.com"@victim.com
    if (trimmedEmail.includes('"')) {
        throw new Error("Invalid email address format.");
    }
    // Get the first two elements only,
    // separated by `@` from user input.
    let [local, domain] = trimmedEmail.split("@");
    // Validate that we have exactly 2 parts (local and domain)
    if (!local || !domain || trimmedEmail.split("@").length !== 2) {
        throw new Error("Invalid email address format.");
    }
    // The part before "@" can contain a ","
    // but we remove it on the domain part
    domain = domain.split(",")[0];
    // Additional validation: domain should not be empty after comma split
    if (!domain) {
        throw new Error("Invalid email address format.");
    }
    return `${local}@${domain}`;
}
