import { eq } from "drizzle-orm";
import { gameSettings } from "@db/schema";
import { DrizzleClient } from "@shared/db/DrizzleClient";
import { SystemError } from "@shared/lib/errors";
import type {
    LevelingConfig,
    EconomyConfig,
    InventoryConfig,
    LootdropConfig,
    TriviaConfig,
    ModerationConfig,
    QuestConfig,
} from "@db/schema/game-settings";

export type GameSettingsData = {
    leveling: LevelingConfig;
    economy: EconomyConfig;
    inventory: InventoryConfig;
    lootdrop: LootdropConfig;
    trivia: TriviaConfig;
    moderation: ModerationConfig;
    quest: QuestConfig;
    commands: Record<string, boolean>;
    system: Record<string, unknown>;
};

let cachedSettings: GameSettingsData | null = null;
let cacheTimestamp = 0;
const CACHE_TTL_MS = 30000;

export const gameSettingsService = {
    /** Retrieve game settings, using a 30-second TTL cache by default. */
    getSettings: async (useCache = true): Promise<GameSettingsData | null> => {
        if (useCache && cachedSettings && Date.now() - cacheTimestamp < CACHE_TTL_MS) {
            return cachedSettings;
        }

        const settings = await DrizzleClient.query.gameSettings.findFirst({
            where: eq(gameSettings.id, "default"),
        });

        if (!settings) return null;

        cachedSettings = {
            leveling: settings.leveling,
            economy: settings.economy,
            inventory: settings.inventory,
            lootdrop: settings.lootdrop,
            trivia: settings.trivia,
            moderation: settings.moderation,
            quest: settings.quest,
            commands: settings.commands ?? {},
            system: settings.system ?? {},
        };
        cacheTimestamp = Date.now();

        return cachedSettings;
    },

    /** Create or update game settings, merging with existing values and invalidating cache. */
    upsertSettings: async (data: Partial<GameSettingsData>) => {
        const existing = await gameSettingsService.getSettings(false);

        const values: typeof gameSettings.$inferInsert = {
            id: "default",
            leveling: data.leveling ?? existing?.leveling ?? gameSettingsService.getDefaultLeveling(),
            economy: data.economy ?? existing?.economy ?? gameSettingsService.getDefaultEconomy(),
            inventory: data.inventory ?? existing?.inventory ?? gameSettingsService.getDefaultInventory(),
            lootdrop: data.lootdrop ?? existing?.lootdrop ?? gameSettingsService.getDefaultLootdrop(),
            trivia: data.trivia ?? existing?.trivia ?? gameSettingsService.getDefaultTrivia(),
            moderation: data.moderation ?? existing?.moderation ?? gameSettingsService.getDefaultModeration(),
            quest: data.quest ?? existing?.quest ?? gameSettingsService.getDefaultQuest(),
            commands: data.commands ?? existing?.commands ?? {},
            system: data.system ?? existing?.system ?? {},
            updatedAt: new Date(),
        };

        const [result] = await DrizzleClient.insert(gameSettings)
            .values(values)
            .onConflictDoUpdate({
                target: gameSettings.id,
                set: values,
            })
            .returning();

        gameSettingsService.invalidateCache();

        return result;
    },

    /** Update a single configuration section (e.g., "leveling", "economy") and invalidate cache. */
    updateSection: async <K extends keyof GameSettingsData>(
        section: K,
        value: GameSettingsData[K]
    ) => {
        const existing = await gameSettingsService.getSettings(false);

        if (!existing) {
            throw new SystemError("Game settings not found. Initialize settings first.");
        }

        const updates: Partial<GameSettingsData> = { [section]: value };
        await gameSettingsService.upsertSettings(updates);

        gameSettingsService.invalidateCache();
    },

    /** Enable or disable a specific command in the game settings. */
    toggleCommand: async (commandName: string, enabled: boolean) => {
        const settings = await gameSettingsService.getSettings(false);

        if (!settings) {
            throw new SystemError("Game settings not found. Initialize settings first.");
        }

        const commands = {
            ...settings.commands,
            [commandName]: enabled,
        };

        await gameSettingsService.updateSection("commands", commands);
    },

    /** Invalidate the in-memory settings cache, forcing a fresh DB read on next access. */
    invalidateCache: () => {
        cachedSettings = null;
        cacheTimestamp = 0;
    },

    /** Return default leveling configuration values. */
    getDefaultLeveling: (): LevelingConfig => ({
        base: 100,
        exponent: 1.5,
        chat: {
            cooldownMs: 60000,
            minXp: 5,
            maxXp: 15,
        },
    }),

    /** Return default economy configuration values. */
    getDefaultEconomy: (): EconomyConfig => ({
        daily: {
            amount: "100",
            streakBonus: "10",
            weeklyBonus: "50",
            cooldownMs: 86400000,
        },
        transfers: {
            allowSelfTransfer: false,
            minAmount: "1",
        },
        exam: {
            multMin: 1.0,
            multMax: 2.0,
        },
    }),

    /** Return default inventory configuration values. */
    getDefaultInventory: (): InventoryConfig => ({
        maxStackSize: "99",
        maxSlots: 20,
    }),

    /** Return default lootdrop configuration values. */
    getDefaultLootdrop: (): LootdropConfig => ({
        activityWindowMs: 300000,
        minMessages: 5,
        spawnChance: 0.1,
        cooldownMs: 60000,
        reward: {
            min: 10,
            max: 50,
            currency: "AU",
        },
    }),

    /** Return default trivia configuration values. */
    getDefaultTrivia: (): TriviaConfig => ({
        entryFee: "50",
        rewardMultiplier: 1.8,
        timeoutSeconds: 30,
        cooldownMs: 60000,
        categories: [],
        difficulty: "random",
    }),

    /** Return default moderation configuration values. */
    getDefaultModeration: (): ModerationConfig => ({
        prune: {
            maxAmount: 100,
            confirmThreshold: 50,
            batchSize: 100,
            batchDelayMs: 1000,
        },
    }),

    /** Return default quest configuration values. */
    getDefaultQuest: (): QuestConfig => ({
        maxActiveQuests: 3,
    }),

    /** Return the complete set of default game settings across all sections. */
    getDefaults: (): GameSettingsData => ({
        leveling: gameSettingsService.getDefaultLeveling(),
        economy: gameSettingsService.getDefaultEconomy(),
        inventory: gameSettingsService.getDefaultInventory(),
        lootdrop: gameSettingsService.getDefaultLootdrop(),
        trivia: gameSettingsService.getDefaultTrivia(),
        moderation: gameSettingsService.getDefaultModeration(),
        quest: gameSettingsService.getDefaultQuest(),
        commands: {},
        system: {},
    }),
};
