import { createCookieSessionStorage, redirect } from "@remix-run/node";
import invariant from "tiny-invariant";

import type { User, UserWithFavoriteStations } from "~/models/user.server";
import { getUserById } from "~/models/user.server";

invariant(process.env.SESSION_SECRET, "SESSION_SECRET must be set");

export const sessionStorage = createCookieSessionStorage({
    cookie: {
        name: "__session",
        httpOnly: true,
        path: "/",
        sameSite: "lax",
        secrets: [process.env.SESSION_SECRET],
        secure: process.env.NODE_ENV === "production"
    }
});

const USER_SESSION_KEY = "userId";

export async function getSession(request: Request) {
    const cookie = request.headers.get("Cookie");
    return sessionStorage.getSession(cookie);
}

export async function getUserId(
    request: Request
): Promise<User["id"] | undefined> {
    const session = await getSession(request);
    return session.get(USER_SESSION_KEY);
}

export async function getUser(request: Request) {
    const userId = await getUserId(request);
    if (!userId) {
        return;
    }
    const user: UserWithFavoriteStations | null = await getUserById(userId);
    if (!user) {
        throw await logout(request);
    }
    return user;
}

export async function requireUserId(
    request: Request,
    redirectTo: string = new URL(request.url).pathname
) {
    const userId = await getUserId(request);
    if (!userId) {
        const searchParams = new URLSearchParams([["redirectTo", redirectTo]]);
        throw redirect(`/login?${searchParams}`);
    }
    return userId;
}

export async function requireUser(request: Request) {
    const userId = await requireUserId(request);
    const user = await getUserById(userId);
    if (!user) {
        throw await logout(request);
    }
    return user;
}

export async function createUserSession({
                                            request,
                                            userId,
                                            remember,
                                            redirectTo
                                        }: {
    request: Request;
    userId: string;
    remember: boolean;
    redirectTo: string;
}) {
    const session = await getSession(request);
    session.set(USER_SESSION_KEY, userId);
    return redirect(redirectTo, {
        headers: {
            "Set-Cookie": await sessionStorage.commitSession(session, {
                maxAge: remember
                    ? 60 * 60 * 24 * 7 // 7 days
                    : undefined
            })
        }
    });
}

export async function logout(request: Request) {
    const session = await getSession(request);
    return redirect("/listen", {
        headers: {
            "Set-Cookie": await sessionStorage.destroySession(session)
        }
    });
}