import { levelingService } from "@/modules/leveling/leveling.service";
import { economyService } from "@/modules/economy/economy.service";
import { userTimers } from "@/db/schema";
import type { EffectHandler } from "./types";
import type { LootTableItem } from "@/lib/types";
import { inventoryService } from "@/modules/inventory/inventory.service";
import { inventory, items } from "@/db/schema";
import { TimerType, TransactionType, LootType } from "@/lib/constants";


// Helper to extract duration in seconds
const getDuration = (effect: any): number => {
    if (effect.durationHours) return effect.durationHours * 3600;
    if (effect.durationMinutes) return effect.durationMinutes * 60;
    return effect.durationSeconds || 60; // Default to 60s if nothing provided
};

export const handleAddXp: EffectHandler = async (userId, effect, txFn) => {
    await levelingService.addXp(userId, BigInt(effect.amount), txFn);
    return `Gained ${effect.amount} XP`;
};

export const handleAddBalance: EffectHandler = async (userId, effect, txFn) => {
    await economyService.modifyUserBalance(userId, BigInt(effect.amount), TransactionType.ITEM_USE, `Used Item`, null, txFn);
    return `Gained ${effect.amount} 🪙`;
};

export const handleReplyMessage: EffectHandler = async (_userId, effect, _txFn) => {
    return effect.message;
};

export const handleXpBoost: EffectHandler = async (userId, effect, txFn) => {
    const boostDuration = getDuration(effect);
    const expiresAt = new Date(Date.now() + boostDuration * 1000);
    await txFn.insert(userTimers).values({
        userId: BigInt(userId),
        type: TimerType.EFFECT,
        key: 'xp_boost',
        expiresAt: expiresAt,
        metadata: { multiplier: effect.multiplier }
    }).onConflictDoUpdate({
        target: [userTimers.userId, userTimers.type, userTimers.key],
        set: { expiresAt: expiresAt, metadata: { multiplier: effect.multiplier } }
    });
    return `XP Boost (${effect.multiplier}x) active for ${Math.floor(boostDuration / 60)}m`;
};

export const handleTempRole: EffectHandler = async (userId, effect, txFn) => {
    const roleDuration = getDuration(effect);
    const roleExpiresAt = new Date(Date.now() + roleDuration * 1000);
    await txFn.insert(userTimers).values({
        userId: BigInt(userId),
        type: TimerType.ACCESS,
        key: `role_${effect.roleId}`,
        expiresAt: roleExpiresAt,
        metadata: { roleId: effect.roleId }
    }).onConflictDoUpdate({
        target: [userTimers.userId, userTimers.type, userTimers.key],
        set: { expiresAt: roleExpiresAt }
    });
    // Actual role assignment happens in the Command layer
    return `Temporary Role granted for ${Math.floor(roleDuration / 60)}m`;
};

export const handleColorRole: EffectHandler = async (_userId, _effect, _txFn) => {
    return "Color Role Equipped";
};

export const handleLootbox: EffectHandler = async (userId, effect, txFn) => {
    const pool = effect.pool as LootTableItem[];
    if (!pool || pool.length === 0) return "The box is empty...";

    const totalWeight = pool.reduce((sum, item) => sum + item.weight, 0);
    let random = Math.random() * totalWeight;

    let winner: LootTableItem | null = null;
    for (const item of pool) {
        if (random < item.weight) {
            winner = item;
            break;
        }
        random -= item.weight;
    }

    if (!winner) return "The box is empty..."; // Should not happen

    // Process Winner
    if (winner.type === LootType.NOTHING) {
        return winner.message || "You found nothing inside.";
    }

    if (winner.type === LootType.CURRENCY) {
        let amount = winner.amount || 0;
        if (winner.minAmount && winner.maxAmount) {
            amount = Math.floor(Math.random() * (winner.maxAmount - winner.minAmount + 1)) + winner.minAmount;
        }
        if (amount > 0) {
            await economyService.modifyUserBalance(userId, BigInt(amount), TransactionType.LOOTBOX, 'Lootbox Reward', null, txFn);
            return winner.message || `You found ${amount} 🪙!`;
        }
    }

    if (winner.type === LootType.XP) {
        let amount = winner.amount || 0;
        if (winner.minAmount && winner.maxAmount) {
            amount = Math.floor(Math.random() * (winner.maxAmount - winner.minAmount + 1)) + winner.minAmount;
        }
        if (amount > 0) {
            await levelingService.addXp(userId, BigInt(amount), txFn);
            return winner.message || `You gained ${amount} XP!`;
        }
    }

    if (winner.type === LootType.ITEM) {
        if (winner.itemId) {
            const quantity = BigInt(winner.amount || 1);

            await inventoryService.addItem(userId, winner.itemId, quantity, txFn);

            // Try to fetch item name for the message
            try {
                const item = await txFn.query.items.findFirst({
                    where: (items, { eq }) => eq(items.id, winner.itemId!)
                });
                if (item) {
                    return winner.message || `You found ${quantity > 1 ? quantity + 'x ' : ''}**${item.name}**!`;
                }
            } catch (e) {
                console.error("Failed to fetch item name for lootbox message", e);
            }

            return winner.message || `You found an item! (ID: ${winner.itemId})`;
        }
    }

    return "You found nothing suitable inside.";
};
