import { db } from "./db";
import { users, sessions, projectPermissions, projects } from "./db/schema";
import { eq, and, or, gte } from "drizzle-orm";
import { randomBytes } from "crypto";
import Argon2id from "@rabbit-company/argon2id";

export async function passwordMatches(password: string, storedPassword: string) {
	return Argon2id.verify(storedPassword, password);
}

export async function hashPassword(password: string) {
	return Argon2id.hashEncoded(password);
}

// Generate a random session ID
function generateSessionId(): string {
	return randomBytes(32).toString("hex");
}

// Create a new user
export async function createUser(username: string, password: string) {
	const userId = randomBytes(16).toString("hex");
	const now = new Date();
	const hashedPassword = await Argon2id.hashEncoded(password);

	await db.insert(users).values({
		id: userId,
		username,
		password: hashedPassword,
		createdAt: now,
		updatedAt: now
	});

	return userId;
}

// Authenticate user
export async function authenticateUser(username: string, password: string) {
	const user = await db.select().from(users).where(eq(users.username, username)).get();
	if (!user) return null;
	const verified = await passwordMatches(password, user.password);
	if (!verified) {
		return null;
	}

	return user;
}

// Create a session
export async function createSession(userId: string) {
	const sessionId = generateSessionId();
	const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); // 30 days

	await db.insert(sessions).values({
		id: sessionId,
		userId,
		expiresAt,
		createdAt: new Date()
	});

	return sessionId;
}

// Validate session
export async function validateSession(sessionId: string) {
	const session = await db
		.select()
		.from(sessions)
		.where(and(eq(sessions.id, sessionId), gte(sessions.expiresAt, new Date())))
		.get();

	if (!session) {
		return null;
	}

	// Get user data
	const user = await db.select().from(users).where(eq(users.id, session.userId)).get();

	return user;
}

// Check if user can edit project
export async function canUserEditProject(userId: string, projectId: string) {
	// Admin users can edit all projects
	const user = await db.select().from(users).where(eq(users.id, userId)).get();
	if (user?.isAdmin) {
		return true;
	}

	// Check if user is project owner
	const project = await db
		.select()
		.from(projects)
		.where(and(eq(projects.id, projectId), eq(projects.ownerId, userId)))
		.get();
	if (project) {
		return true;
	}

	// Check if user has edit permission
	const permission = await db
		.select()
		.from(projectPermissions)
		.where(
			and(
				eq(projectPermissions.projectId, projectId),
				eq(projectPermissions.userId, userId)
			)
		)
		.get();

	return !!permission;
}

// Check if user can view project
export async function canUserViewProject(userID: string, projectId: string) {
	if (await canUserEditProject(userID, projectId)) {
		return true;
	}

	// Check if project is public
	const project = await db
		.select()
		.from(projects)
		.where(and(eq(projects.id, projectId), eq(projects.isPublic, true)))
		.get();

	return !!project;
}

// Get projects accessible to user
export async function getUserProjects(userId: string) {
	const user = await db.select().from(users).where(eq(users.id, userId)).get();
	
	if (user?.isAdmin) {
		// Admin can see all projects
		return await db.select().from(projects).all();
	}

	// Get projects where user is owner or has permission
	const accessibleProjects = await db
		.select()
		.from(projects)
		.where(
			or(
				eq(projects.ownerId, userId),
				eq(projectPermissions.userId, userId)
			)
		)
		.leftJoin(projectPermissions, eq(projects.id, projectPermissions.projectId))
		.all();

	return accessibleProjects.map(row => row.projects);
}

// Delete session (logout)
export async function deleteSession(sessionId: string) {
	await db.delete(sessions).where(eq(sessions.id, sessionId));
}
