import { Message, TextChannel } from "discord.js";
import { getLootdropMessage } from "@/modules/economy/lootdrop.view";
import { config } from "@shared/lib/config";
import { economyService } from "./economy.service";
import { terminalService } from "@shared/modules/terminal/terminal.service";
import { lootdrops } from "@db/schema";
import { DrizzleClient } from "@shared/db/DrizzleClient";
import { eq, and, isNull, lt } from "drizzle-orm";

interface Lootdrop {
    messageId: string;
    channelId: string;
    rewardAmount: number;
    currency: string;
    claimedBy?: string;
    createdAt: Date;
}

class LootdropService {
    private channelActivity: Map<string, number[]> = new Map();
    private channelCooldowns: Map<string, number> = new Map();

    constructor() {
        // Cleanup interval for activity tracking and expired lootdrops
        setInterval(() => {
            this.cleanupActivity();
            this.cleanupExpiredLootdrops(true);
        }, 60000);
    }

    private cleanupActivity() {
        const now = Date.now();
        const window = config.lootdrop.activityWindowMs;

        for (const [channelId, timestamps] of this.channelActivity.entries()) {
            const validTimestamps = timestamps.filter(t => now - t < window);
            if (validTimestamps.length === 0) {
                this.channelActivity.delete(channelId);
            } else {
                this.channelActivity.set(channelId, validTimestamps);
            }
        }
    }

    public async cleanupExpiredLootdrops(includeClaimed: boolean = false): Promise<number> {
        try {
            const now = new Date();
            const whereClause = includeClaimed
                ? lt(lootdrops.expiresAt, now)
                : and(isNull(lootdrops.claimedBy), lt(lootdrops.expiresAt, now));

            const result = await DrizzleClient.delete(lootdrops)
                .where(whereClause)
                .returning();

            if (result.length > 0) {
                console.log(`[LootdropService] Cleaned up ${result.length} expired lootdrops.`);
            }
            return result.length;
        } catch (error) {
            console.error("Failed to cleanup lootdrops:", error);
            return 0;
        }
    }

    public async processMessage(message: Message) {
        if (message.author.bot || !message.guild) return;

        const channelId = message.channel.id;
        const now = Date.now();

        // Check cooldown
        const cooldown = this.channelCooldowns.get(channelId);
        if (cooldown && now < cooldown) return;

        // Track activity
        const timestamps = this.channelActivity.get(channelId) || [];
        timestamps.push(now);
        this.channelActivity.set(channelId, timestamps);

        // Filter for window
        const window = config.lootdrop.activityWindowMs;
        const recentActivity = timestamps.filter(t => now - t < window);

        if (recentActivity.length >= config.lootdrop.minMessages) {
            // Chance to spawn
            if (Math.random() < config.lootdrop.spawnChance) {
                await this.spawnLootdrop(message.channel as TextChannel);
                // Set cooldown
                this.channelCooldowns.set(channelId, now + config.lootdrop.cooldownMs);
                this.channelActivity.set(channelId, []);
            }
        }
    }

    private async spawnLootdrop(channel: TextChannel) {
        const min = config.lootdrop.reward.min;
        const max = config.lootdrop.reward.max;
        const reward = Math.floor(Math.random() * (max - min + 1)) + min;
        const currency = config.lootdrop.reward.currency;

        const { content, files, components } = await getLootdropMessage(reward, currency);

        try {
            const message = await channel.send({ content, files, components });

            // Persist to DB
            await DrizzleClient.insert(lootdrops).values({
                messageId: message.id,
                channelId: channel.id,
                rewardAmount: reward,
                currency: currency,
                createdAt: new Date(),
                // Expire after 10 mins
                expiresAt: new Date(Date.now() + 600000)
            });

            // Trigger Terminal Update
            terminalService.update();

        } catch (error) {
            console.error("Failed to spawn lootdrop:", error);
        }
    }

    public async tryClaim(messageId: string, userId: string, username: string): Promise<{ success: boolean; amount?: number; currency?: string; error?: string }> {
        try {
            // Atomic update: Try to set claimedBy where it is currently null
            // This acts as a lock and check in one query
            const result = await DrizzleClient.update(lootdrops)
                .set({ claimedBy: BigInt(userId) })
                .where(and(
                    eq(lootdrops.messageId, messageId),
                    isNull(lootdrops.claimedBy)
                ))
                .returning();

            if (result.length === 0 || !result[0]) {
                // If update affected 0 rows, check if it was because it doesn't exist or is already claimed
                const check = await DrizzleClient.select().from(lootdrops).where(eq(lootdrops.messageId, messageId));
                if (check.length === 0) {
                    return { success: false, error: "This lootdrop has expired." };
                }
                return { success: false, error: "This lootdrop has already been claimed." };
            }

            const drop = result[0];

            await economyService.modifyUserBalance(
                userId,
                BigInt(drop.rewardAmount),
                "LOOTDROP_CLAIM",
                `Claimed lootdrop in channel ${drop.channelId}`
            );

            // Trigger Terminal Update
            terminalService.update();

            return { success: true, amount: drop.rewardAmount, currency: drop.currency };

        } catch (error) {
            console.error("Error claiming lootdrop:", error);
            return { success: false, error: "An error occurred while processing the reward." };
        }
    }
    public getLootdropState() {
        let hottestChannel: { id: string; messages: number; progress: number; cooldown: boolean; } | null = null;
        let maxMessages = -1;

        const window = config.lootdrop.activityWindowMs;
        const now = Date.now();
        const required = config.lootdrop.minMessages;

        for (const [channelId, timestamps] of this.channelActivity.entries()) {
            // Filter valid just to be sure we are reporting accurate numbers
            const validCount = timestamps.filter(t => now - t < window).length;

            // Check cooldown
            const cooldownUntil = this.channelCooldowns.get(channelId);
            const isOnCooldown = !!(cooldownUntil && now < cooldownUntil);

            if (validCount > maxMessages) {
                maxMessages = validCount;
                hottestChannel = {
                    id: channelId,
                    messages: validCount,
                    progress: Math.min(100, (validCount / required) * 100),
                    cooldown: isOnCooldown
                };
            }
        }

        return {
            monitoredChannels: this.channelActivity.size,
            hottestChannel,
            config: {
                requiredMessages: required,
                dropChance: config.lootdrop.spawnChance
            }
        };
    }

    public async clearCaches() {
        this.channelActivity.clear();
        this.channelCooldowns.clear();
        console.log("[LootdropService] Caches cleared via administrative action.");
    }
}

export const lootdropService = new LootdropService();
