import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { useData, useError, useInput, useMaybeBody, useMaybeResult } from "../../data/index.js";
import { isUnauthenticated, getMaybeAuthenticationState, setAuthenticationState as setAuthenticatedAuthenticationState, getMaybeUser } from "../../../../authentication/index.js";
import { addAnonymousUser, addAuthenticationState, addCookieState, change, DEFAULT_INVITEE_EXCHANGE_STATE_EXPIRES_MS, deleteAuthenticationState, getAuthenticationState, getExpiresAt, getExternalUser, getInviteeState, getUserAuthenticationRoleForUser, isInviteeState, setAuthenticationState, setUserAuthenticationRole, } from "../../../../data/index.js";
import { ok } from "../../../../is.js";
import { path as DEFAULT_LOGIN_URL } from "../login.js";
import { getOrigin } from "../../../../listen/config.js";
import { setAuthenticationStateCookie } from "../../../../listen/index.js";
const { LOGIN_URL } = process.env;
export const path = "/invite/accept";
export const anonymous = true;
const INVALID_INVITE = "Invalid or expired invite";
export async function handler(request, response) {
    const stateId = request.body?.stateKey || request.query.stateKey || request.query.state;
    const token = request.body?.token || request.query.token;
    ok(stateId, INVALID_INVITE);
    let state;
    const deletableStates = [];
    if (!token) {
        const exchangeState = await getAuthenticationState(stateId);
        deletableStates.push(exchangeState);
        ok(exchangeState, INVALID_INVITE);
        ok(exchangeState.type === "exchange", INVALID_INVITE);
        ok(exchangeState.userState, INVALID_INVITE);
        const foundState = await getAuthenticationState(exchangeState.userState);
        ok(isInviteeState(foundState), INVALID_INVITE);
        state = foundState;
        if (!state.inviteRepeating) {
            deletableStates.push(state);
        }
    }
    else {
        state = await getInviteeState({
            stateId,
            token
        });
        if (!state.inviteRepeating) {
            deletableStates.push(state);
        }
    }
    ok(state, INVALID_INVITE);
    const input = {
        state,
        deletableStates,
        stateId
    };
    if (state.inviteAutoAccept) {
        const existingUser = getMaybeUser();
        if (existingUser) {
            ok(existingUser.userId !== state.createdByUserId, "Creating user cannot accept invite");
        }
        if (state.inviteAnonymous || existingUser) {
            const result = await accept(input);
            await addAcceptCookieState(state, response, result);
            const redirect = new URL(state.inviteRedirectUrl || "/", getOrigin());
            response.header("Location", redirect.toString());
            response.status(302);
            response.send();
        }
        // Else default
    }
    return input;
}
async function addAcceptCookieState(from, response, { user, role }) {
    const cookieState = await addCookieState({
        userId: user.userId,
        roles: role.roles,
        from: {
            type: "invitee",
            createdAt: from.createdAt,
            data: from.data,
            from: from.from
        },
        data: from.data
    });
    setAuthenticationStateCookie(response, cookieState);
}
async function accept(input) {
    const { state, deletableStates } = input;
    const user = await getInviteeUser();
    const existingRole = await getUserAuthenticationRoleForUser(user);
    const role = await setUserAuthenticationRole({
        ...existingRole,
        userId: user.userId,
        expiresAt: user.expiresAt,
        roles: [...new Set([
                "member",
                ...(existingRole?.roles || []),
                ...(state.roles || [])
            ])]
    });
    for (const state of deletableStates) {
        await deleteAuthenticationState(state.stateId);
    }
    const maybe = getMaybeAuthenticationState();
    if (maybe?.stateId) {
        const updatedState = await setAuthenticationState({
            ...maybe,
            roles: [...new Set([
                    ...maybe.roles,
                    ...role.roles
                ])]
        });
        setAuthenticatedAuthenticationState(updatedState);
    }
    let changed;
    if (state.inviteChange) {
        changed = await change(state.inviteChange);
    }
    return {
        role,
        user,
        changed
    };
    async function getInviteeUser() {
        const maybe = getMaybeUser();
        if (state.externalId) {
            return getExternalUser("invitee", state.externalId, maybe);
        }
        if (maybe)
            return maybe;
        ok(state.inviteAnonymous, "Expected authenticated user to accept invite");
        return addAnonymousUser();
    }
}
export async function submit(request, response, input) {
    const { stateId, state } = input;
    if (isUnauthenticated() && (!input.state.inviteAnonymous || request.body.submit === "login")) {
        let expiresAt = getExpiresAt(DEFAULT_INVITEE_EXCHANGE_STATE_EXPIRES_MS);
        // Ensure it can't be used longer than the base invite
        if (new Date(expiresAt).getTime() > new Date(state.expiresAt).getTime()) {
            expiresAt = state.expiresAt;
        }
        const url = new URL(LOGIN_URL || DEFAULT_LOGIN_URL, getOrigin());
        // The exchange state is not the same as the base invitee state with a token
        // A base invitee state MUST have a token, which this does not
        // So they cannot be interchanged, the types are also different...
        const exchangeState = await addAuthenticationState({
            type: "exchange",
            exchange: true,
            // Reference the original stateKey
            userState: stateId,
            // Shorter lived
            expiresAt,
            redirectUrl: url.toString(),
        });
        url.searchParams.set("state", exchangeState.stateId);
        response.status(302);
        response.header("Location", url.toString());
        response.send("Redirecting...");
        return;
    }
    const result = await accept(input);
    await addAcceptCookieState(state, response, result);
    if (state.inviteRedirectUrl) {
        const redirect = new URL(state.inviteRedirectUrl, getOrigin());
        response.header("Location", redirect.toString());
        response.status(302);
        response.send();
    }
    return result;
}
export function AcceptInvite() {
    const { state } = useInput();
    const body = useMaybeBody();
    const error = useError();
    const { url, isUnauthenticated } = useData();
    const { searchParams } = new URL(url);
    const result = useMaybeResult();
    console.error(error);
    return _jsx(Body, { body: body });
    function Body({ body }) {
        let submit = (_jsx("button", { type: "submit", className: "bg-sky-500 hover:bg-sky-700 px-4 py-2.5 text-sm leading-5 rounded-md font-semibold text-white", children: "Accept Invite" }));
        if (!result) {
            const loginUrl = new URL(url);
            loginUrl.searchParams.set("login", "true");
            if (state.inviteAnonymous) {
                if (isUnauthenticated) {
                    submit = (_jsxs(_Fragment, { children: [submit, _jsx("button", { type: "submit", formAction: loginUrl.toString(), className: "bg-sky-500 hover:bg-sky-700 px-4 py-2.5 text-sm leading-5 rounded-md font-semibold text-white", children: "Login & Accept Invite" })] }));
                }
            }
            else if (isUnauthenticated) {
                submit = (_jsx("button", { type: "submit", formAction: loginUrl.toString(), className: "bg-sky-500 hover:bg-sky-700 px-4 py-2.5 text-sm leading-5 rounded-md font-semibold text-white", children: "Login & Accept Invite" }));
            }
        }
        return (_jsxs("form", { name: "accept-invite", action: `${url.toString()}#action-section`, method: "post", children: [!result ? (_jsxs(_Fragment, { children: [_jsx("input", { name: "stateKey", value: body?.stateKey ?? searchParams.get("state") ?? searchParams.get("stateKey"), type: "hidden" }), _jsx("input", { name: "token", value: body?.token ?? searchParams.get("token"), type: "hidden" }), _jsx("div", { id: "action-section", children: submit })] })) : undefined, error ? (_jsxs(_Fragment, { children: [_jsx("hr", { className: "my-8" }), _jsx("div", { className: "pointer-events-auto flex items-center justify-between gap-x-6 bg-red-400 px-6 py-2.5 sm:rounded-xl sm:py-3 sm:pl-4 sm:pr-3.5", children: _jsx("p", { className: "text-sm leading-6 text-white", children: error instanceof Error ? error.message : String(error) }) }), _jsx("hr", { className: "my-8" })] })) : undefined, result ? (_jsxs("div", { children: [_jsx("br", {}), _jsx("br", {}), _jsxs("p", { children: ["Assigned role", result.role.roles.length > 1 ? "s" : "", ": ", result.role.roles.map(value => `"${value}"`).join(", ")] })] })) : undefined] }));
    }
}
export const Component = AcceptInvite;
//# sourceMappingURL=accept.js.map