import { users } from "@db/schema";
import { eq } from "drizzle-orm";
import { DrizzleClient } from "@shared/db/DrizzleClient";
import { withTransaction } from "@/lib/db";
import type { Transaction } from "@shared/lib/types";

export const userService = {
    /** Fetch a user by Discord ID, including their class relation. */
    getUserById: async (id: string) => {
        const user = await DrizzleClient.query.users.findFirst({
            where: eq(users.id, BigInt(id)),
            with: { class: true }
        });
        return user;
    },
    /** Fetch a user by their username. */
    getUserByUsername: async (username: string) => {
        const user = await DrizzleClient.query.users.findFirst({ where: eq(users.username, username) });
        return user;
    },
    /** Fetch a user by ID, creating a new record if one does not exist. */
    getOrCreateUser: async (id: string, username: string, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            let user = await txFn.query.users.findFirst({
                where: eq(users.id, BigInt(id)),
                with: { class: true }
            });

            if (!user) {
                await txFn.insert(users).values({
                    id: BigInt(id),
                    username,
                }).returning();

                // Re-query to get the user with class relation
                user = await txFn.query.users.findFirst({
                    where: eq(users.id, BigInt(id)),
                    with: { class: true }
                });
            }
            return user;
        }, tx);
    },
    /** Get the class assigned to a user, or undefined if none. */
    getUserClass: async (id: string) => {
        const user = await DrizzleClient.query.users.findFirst({
            where: eq(users.id, BigInt(id)),
            with: { class: true }
        });
        return user?.class;
    },
    /** Create a new user with an optional initial class assignment. */
    createUser: async (id: string | bigint, username: string, classId?: bigint, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            const [user] = await txFn.insert(users).values({
                id: BigInt(id),
                username,
                classId,
            }).returning();
            return user;
        }, tx);
    },
    /** Update a user record with partial data. */
    updateUser: async (id: string, data: Partial<typeof users.$inferInsert>, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            const [user] = await txFn.update(users)
                .set(data)
                .where(eq(users.id, BigInt(id)))
                .returning();
            return user;
        }, tx);
    },
    /** Delete a user by ID. */
    deleteUser: async (id: string, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            await txFn.delete(users).where(eq(users.id, BigInt(id)));
        }, tx);
    },
};