import { type Session, type SessionIdStorageStrategy, createSessionStorage } from "@remix-run/node";

import serverConfig from "~/config/config.server";
import db from "./db.server";
import logger from "./logger.server";

const SECOND = 1;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;

export const sessionStorage = createDatabaseSessionStorage({
	cookie: {
		name: "__session",
		httpOnly: true,
		path: "/",
		sameSite: "lax",
		secrets: [serverConfig.app.sessionSecret],
		secure: process.env.NODE_ENV === "production" && process.env.CI !== "true",
		maxAge: 30 * DAY,
	},
});

export function getSession(request: Request): Promise<Session> {
	return sessionStorage.getSession(request.headers.get("Cookie"));
}

export const { commitSession, destroySession, getSession: __getSession } = sessionStorage;

function createDatabaseSessionStorage({ cookie }: Pick<SessionIdStorageStrategy, "cookie">) {
	return createSessionStorage({
		cookie,
		async createData(sessionData, expiresAt) {
			let user;
			if (sessionData.user) {
				user = { connect: { id: sessionData.user.id } };
			}
			const { id } = await db.session.create({
				data: {
					expiresAt,
					user,
					data: JSON.stringify(sessionData),
				},
			});
			return id;
		},
		async readData(id) {
			const session = await db.session.findUnique({ where: { id } });
			if (!session) {
				return null;
			}

			const sessionHasExpired = session.expiresAt && session.expiresAt < new Date();
			if (sessionHasExpired) {
				await db.session.delete({ where: { id } });
				return null;
			}

			return JSON.parse(session.data);
		},
		async updateData(id, sessionData, expiresAt) {
			try {
				await db.session.update({
					where: { id },
					data: {
						data: JSON.stringify(sessionData),
						expiresAt,
					},
				});
			} catch (error: any) {
				if (error.code === "P2025") {
					logger.warn("Could not update session because it's not in the DB");
					return;
				}

				throw error;
			}
		},
		async deleteData(id) {
			try {
				await db.session.delete({ where: { id } });
			} catch (error: any) {
				if (error.code === "P2025") {
					logger.warn("Could not delete session because it's not in the DB");
					return;
				}

				throw error;
			}
		},
	});
}
