import type { Cookies } from '@sveltejs/kit';
import { eq } from 'drizzle-orm';
import { testDb as db } from '$lib/server/db/test';
import * as table from '$lib/server/db/schema';

const DAY_IN_MS = 1000 * 60 * 60 * 24;

export const sessionCookieName = 'auth-session';

export function generateSessionToken() {
	const bytes = crypto.getRandomValues(new Uint8Array(18));
	return Buffer.from(bytes).toString('base64url');
}

export async function createSession(token: string, userId: string) {
	const sessionId = new Bun.CryptoHasher('sha256').update(token).digest('hex');
	const session: table.Session = {
		id: sessionId,
		userId,
		expiresAt: new Date(Date.now() + DAY_IN_MS * 30)
	};
	await db.insert(table.session).values(session);
	return session;
}

export async function validateSessionToken(token: string) {
	const sessionId = new Bun.CryptoHasher('sha256').update(token).digest('hex');
	const [result] = await db
		.select({
			// Adjust user table here to tweak returned data
			user: { id: table.user.id, email: table.user.email },
			session: table.session
		})
		.from(table.session)
		.innerJoin(table.user, eq(table.session.userId, table.user.id))
		.where(eq(table.session.id, sessionId));

	if (!result) {
		return { session: null, user: null };
	}
	const { session, user } = result;

	const sessionExpired = Date.now() >= session.expiresAt.getTime();
	if (sessionExpired) {
		await db.delete(table.session).where(eq(table.session.id, session.id));
		return { session: null, user: null };
	}

	const renewSession = Date.now() >= session.expiresAt.getTime() - DAY_IN_MS * 15;
	if (renewSession) {
		session.expiresAt = new Date(Date.now() + DAY_IN_MS * 30);
		await db
			.update(table.session)
			.set({ expiresAt: session.expiresAt })
			.where(eq(table.session.id, session.id));
	}

	return { session, user };
}

export type SessionValidationResult = Awaited<ReturnType<typeof validateSessionToken>>;

export async function invalidateSession(sessionId: string) {
	await db.delete(table.session).where(eq(table.session.id, sessionId));
}

export function setSessionTokenCookie({ cookies }: { cookies: Cookies }, token: string, expiresAt: Date) {
	cookies.set(sessionCookieName, token, {
		expires: expiresAt,
		path: '/'
	});
}

export function deleteSessionTokenCookie({ cookies }: { cookies: Cookies }) {
	cookies.delete(sessionCookieName, {
		path: '/'
	});
}

export async function hashPassword(password: string): Promise<string> {
	return await Bun.password.hash(password, {
		algorithm: 'argon2id',
		memoryCost: 4,
		timeCost: 3
	});
}

export async function verifyPassword(password: string, hash: string): Promise<boolean> {
	try {
		return await Bun.password.verify(password, hash);
	} catch {
		return false;
	}
}

export async function createUser(anonymousId: string, email: string, passwordHash: string, firstName?: string, lastName?: string) {
	const user: table.User = {
		id: anonymousId, // Use anonymousId as the user ID to preserve stats
		email,
		passwordHash,
		appleId: null,
		googleId: null,
		firstName: firstName || null,
		lastName: lastName || null,
		isPrivate: false
	};
	await db.insert(table.user).values(user);
	return user;
}

export async function getUserByEmail(email: string) {
	const [user] = await db.select().from(table.user).where(eq(table.user.email, email));
	return user || null;
}