import { classes, users } from "@/db/schema";
import { eq, sql } from "drizzle-orm";
import { DrizzleClient } from "@/lib/DrizzleClient";
import { UserError } from "@/lib/errors";
import { withTransaction } from "@/lib/db";
import type { Transaction } from "@/lib/types";

export const classService = {
    getAllClasses: async () => {
        return await DrizzleClient.query.classes.findMany();
    },

    assignClass: async (userId: string, classId: bigint, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            const cls = await txFn.query.classes.findFirst({
                where: eq(classes.id, classId),
            });

            if (!cls) throw new UserError("Class not found");

            const [user] = await txFn.update(users)
                .set({ classId: classId })
                .where(eq(users.id, BigInt(userId)))
                .returning();

            return user;
        }, tx);
    },
    getClassBalance: async (classId: bigint) => {
        const cls = await DrizzleClient.query.classes.findFirst({
            where: eq(classes.id, classId),
        });
        return cls?.balance || 0n;
    },
    modifyClassBalance: async (classId: bigint, amount: bigint, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            const cls = await txFn.query.classes.findFirst({
                where: eq(classes.id, classId),
            });

            if (!cls) throw new UserError("Class not found");

            if ((cls.balance ?? 0n) + amount < 0n) {
                throw new UserError("Insufficient class funds");
            }

            const [updatedClass] = await txFn.update(classes)
                .set({
                    balance: sql`${classes.balance} + ${amount} `,
                })
                .where(eq(classes.id, classId))
                .returning();

            return updatedClass;
        }, tx);
    },

    updateClass: async (id: bigint, data: Partial<typeof classes.$inferInsert>, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            const [updatedClass] = await txFn.update(classes)
                .set(data)
                .where(eq(classes.id, id))
                .returning();
            return updatedClass;
        }, tx);
    },

    createClass: async (data: typeof classes.$inferInsert, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            const [newClass] = await txFn.insert(classes)
                .values(data)
                .returning();
            return newClass;
        }, tx);
    },

    deleteClass: async (id: bigint, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            await txFn.delete(classes).where(eq(classes.id, id));
        }, tx);
    }
};
