import { jsonReplacer } from './utils';
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { z } from 'zod';

const configPath = join(import.meta.dir, '..', 'config', 'config.json');

export interface GameConfigType {
    leveling: {
        base: number;
        exponent: number;
        chat: {
            cooldownMs: number;
            minXp: number;
            maxXp: number;
        }
    },
    economy: {
        daily: {
            amount: bigint;
            streakBonus: bigint;
            weeklyBonus: bigint;
            cooldownMs: number;
        },
        transfers: {
            allowSelfTransfer: boolean;
            minAmount: bigint;
        },
        exam: {
            multMin: number;
            multMax: number;
        }
    },
    inventory: {
        maxStackSize: bigint;
        maxSlots: number;
    },
    commands: Record<string, boolean>;
    lootdrop: {
        activityWindowMs: number;
        minMessages: number;
        spawnChance: number;
        cooldownMs: number;
        reward: {
            min: number;
            max: number;
            currency: string;
        }
    };
    studentRole: string;
    visitorRole: string;
    colorRoles: string[];
    welcomeChannelId?: string;
    welcomeMessage?: string;
    feedbackChannelId?: string;
    terminal?: {
        channelId: string;
        messageId: string;
    };
    moderation: {
        prune: {
            maxAmount: number;
            confirmThreshold: number;
            batchSize: number;
            batchDelayMs: number;
        };
        cases: {
            dmOnWarn: boolean;
            logChannelId?: string;
            autoTimeoutThreshold?: number;
        };
    };
    trivia: {
        entryFee: bigint;
        rewardMultiplier: number;
        timeoutSeconds: number;
        cooldownMs: number;
        categories: number[];
        difficulty: 'easy' | 'medium' | 'hard' | 'random';
    };
    system: Record<string, any>;
}

// Initial default config state
export const config: GameConfigType = {} as GameConfigType;

const bigIntSchema = z.union([z.string(), z.number(), z.bigint()])
    .refine((val) => {
        try {
            BigInt(val);
            return true;
        } catch {
            return false;
        }
    }, { message: "Must be a valid integer" })
    .transform((val) => BigInt(val));

const configSchema = z.object({
    leveling: z.object({
        base: z.number(),
        exponent: z.number(),
        chat: z.object({
            cooldownMs: z.number(),
            minXp: z.number(),
            maxXp: z.number(),
        })
    }),
    economy: z.object({
        daily: z.object({
            amount: bigIntSchema,
            streakBonus: bigIntSchema,
            weeklyBonus: bigIntSchema.default(50n),
            cooldownMs: z.number(),
        }),
        transfers: z.object({
            allowSelfTransfer: z.boolean(),
            minAmount: bigIntSchema,
        }),
        exam: z.object({
            multMin: z.number(),
            multMax: z.number(),
        })
    }),
    inventory: z.object({
        maxStackSize: bigIntSchema,
        maxSlots: z.number(),
    }),
    commands: z.record(z.string(), z.boolean()),
    lootdrop: z.object({
        activityWindowMs: z.number(),
        minMessages: z.number(),
        spawnChance: z.number(),
        cooldownMs: z.number(),
        reward: z.object({
            min: z.number(),
            max: z.number(),
            currency: z.string(),
        })

    }),
    studentRole: z.string(),
    visitorRole: z.string(),
    colorRoles: z.array(z.string()).default([]),
    welcomeChannelId: z.string().optional(),
    welcomeMessage: z.string().optional(),
    feedbackChannelId: z.string().optional(),
    terminal: z.object({
        channelId: z.string(),
        messageId: z.string()
    }).optional(),
    moderation: z.object({
        prune: z.object({
            maxAmount: z.number().default(100),
            confirmThreshold: z.number().default(50),
            batchSize: z.number().default(100),
            batchDelayMs: z.number().default(1000)
        }),
        cases: z.object({
            dmOnWarn: z.boolean().default(true),
            logChannelId: z.string().optional(),
            autoTimeoutThreshold: z.number().optional()
        })
    }).default({
        prune: {
            maxAmount: 100,
            confirmThreshold: 50,
            batchSize: 100,
            batchDelayMs: 1000
        },
        cases: {
            dmOnWarn: true
        }
    }),
    trivia: z.object({
        entryFee: bigIntSchema,
        rewardMultiplier: z.number().min(0).max(10),
        timeoutSeconds: z.number().min(5).max(300),
        cooldownMs: z.number().min(0),
        categories: z.array(z.number()).default([]),
        difficulty: z.enum(['easy', 'medium', 'hard', 'random']).default('random'),
    }).default({
        entryFee: 50n,
        rewardMultiplier: 1.8,
        timeoutSeconds: 30,
        cooldownMs: 60000,
        categories: [],
        difficulty: 'random'
    }),
    system: z.record(z.string(), z.any()).default({}),
});

export function reloadConfig() {
    if (!existsSync(configPath)) {
        throw new Error(`Config file not found at ${configPath}`);
    }

    const raw = readFileSync(configPath, 'utf-8');
    const rawConfig = JSON.parse(raw);

    // Update config object in place
    // We use Object.assign to keep the reference to the exported 'config' object same
    const validatedConfig = configSchema.parse(rawConfig);
    Object.assign(config, validatedConfig);

    console.log("🔄 Config reloaded from disk.");
}

// Initial load
reloadConfig();

// Backwards compatibility alias
export const GameConfig = config;

export function saveConfig(newConfig: unknown) {
    // Validate and transform input
    const validatedConfig = configSchema.parse(newConfig);

    const jsonString = JSON.stringify(validatedConfig, jsonReplacer, 4);
    writeFileSync(configPath, jsonString, 'utf-8');
    reloadConfig();
}

export function toggleCommand(commandName: string, enabled: boolean) {
    const newConfig = {
        ...config,
        commands: {
            ...config.commands,
            [commandName]: enabled
        }
    };
    saveConfig(newConfig);
}
