import { userQuests } from "@/db/schema";
import { eq, and } from "drizzle-orm";
import { UserError } from "@/lib/errors";
import { DrizzleClient } from "@/lib/DrizzleClient";
import { economyService } from "@/modules/economy/economy.service";
import { levelingService } from "@/modules/leveling/leveling.service";
import { withTransaction } from "@/lib/db";
import type { Transaction } from "@/lib/types";
import { TransactionType } from "@/lib/constants";

export const questService = {
    assignQuest: async (userId: string, questId: number, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            return await txFn.insert(userQuests)
                .values({
                    userId: BigInt(userId),
                    questId: questId,
                    progress: 0,
                })
                .onConflictDoNothing() // Ignore if already assigned
                .returning();
        }, tx);
    },

    updateProgress: async (userId: string, questId: number, progress: number, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            return await txFn.update(userQuests)
                .set({ progress: progress })
                .where(and(
                    eq(userQuests.userId, BigInt(userId)),
                    eq(userQuests.questId, questId)
                ))
                .returning();
        }, tx);
    },

    completeQuest: async (userId: string, questId: number, tx?: Transaction) => {
        return await withTransaction(async (txFn) => {
            const userQuest = await txFn.query.userQuests.findFirst({
                where: and(
                    eq(userQuests.userId, BigInt(userId)),
                    eq(userQuests.questId, questId)
                ),
                with: {
                    quest: true,
                }
            });

            if (!userQuest) throw new UserError("Quest not assigned");
            if (userQuest.completedAt) throw new UserError("Quest already completed");

            // Mark completed
            await txFn.update(userQuests)
                .set({ completedAt: new Date() })
                .where(and(
                    eq(userQuests.userId, BigInt(userId)),
                    eq(userQuests.questId, questId)
                ));

            // Distribute Rewards
            const rewards = userQuest.quest.rewards as { xp?: number, balance?: number };
            const results = { xp: 0n, balance: 0n };

            if (rewards?.balance) {
                const bal = BigInt(rewards.balance);
                await economyService.modifyUserBalance(userId, bal, TransactionType.QUEST_REWARD, `Reward for quest ${questId}`, null, txFn);
                results.balance = bal;
            }

            if (rewards?.xp) {
                const xp = BigInt(rewards.xp);
                await levelingService.addXp(userId, xp, txFn);
                results.xp = xp;
            }

            return { success: true, rewards: results };
        }, tx);
    },

    getUserQuests: async (userId: string) => {
        return await DrizzleClient.query.userQuests.findMany({
            where: eq(userQuests.userId, BigInt(userId)),
            with: {
                quest: true,
            }
        });
    }
};
