import {BaseInteraction, Client} from "discord.js"
import {config} from "dotenv"
import {isAutocomplete, isChatInputCommand} from "../types/interactions.js"
import {Commands} from "../commands/index.js"
import {checkIsRestart, reportFailed, reportReady, reportStarted} from "../ipc/restart.js"
import {defaultPresence} from "../defaultPresence.js"
import {Pool} from "pg"
import {Database, DatabaseImpl} from "../database/database.js"

async function bot() {
    await checkIsRestart()
    config()
    const c = new Client({
        intents: [],
    })
    const p = new Pool({
        application_name: "VoreRPG Bot",
    })

    async function cleanUp() {
        await p.end()
        c.destroy()
    }

    const db: Database = new DatabaseImpl(p.query.bind(p))
    const cmd = new Commands({users: db.users, cleanUp})

    c.on("ready", async () => {
        try {
            await db.users.createBotOwnerAsAdmin(process.env.BOT_OWNER_ID || "")
        } catch (ex) {
            await reportFailed(c, ex)
            return
        }
        const app = c.application
        if (!app) {
            c.destroy()
            await reportFailed(c, "No application was given")
            return
        } else {
            try {
                cmd.setCache(await app.commands.set(cmd.definitions))
                const g = await c.guilds.fetch()
                for (const guild of g.values()) {
                    cmd.setCache(await app.commands.set(cmd.definitions, guild.id))
                }
            } catch (ex) {
                c.destroy()
                await reportFailed(c, ex)
                return
            }
        }
        const user = c.user
        if (!user) {
            c.destroy()
            await reportFailed(c, "No user found")
            return
        } else {
            user.setPresence(defaultPresence)
        }
        try {
            await reportReady(c)
        } catch (ex) {
            console.log(ex)
        }
    })
    c.on("error", async (ex) => {
        c.destroy()
        await reportFailed(c, ex)
    })
    c.on("interactionCreate", async (ev: BaseInteraction) => {
        if (ev.client.user.presence.status !== "online") {
            if (ev.isRepliable()) {
                try {
                    await ev.reply({
                        content: "Shhh... I'm sleeping!! Try again later.",
                        ephemeral: true,
                    })
                } catch (ex) {
                    console.log("failed sending busy reply", ex)
                }
            }
            return
        }
        if (isChatInputCommand(ev)) {
            try {
                await cmd.execute(ev)
            } catch (ex) {
                console.log("failed executing command", ev, ex)
            }
            if (!ev.replied) {
                console.log("never replied to command", ev)
                try {
                    await ev.reply({
                        ephemeral: true,
                        content: "Uuguuu... I can't think straight... try again later, 'kay?",
                    })
                } catch (ex) {
                    console.log("failed sending error reply", ex)
                }
            }
        } else if (isAutocomplete(ev)) {
            try {
                await cmd.autocomplete(ev)
            } catch (ex) {
                console.log("failed autocompleting for command", ev, ex)
            }
            if (!ev.responded) {
                console.log("never autocompleted for command", ev)
                try {
                    await ev.respond([])
                } catch (ex) {
                    console.log("failed sending error response", ex)
                }
            }
        } else if (ev.isRepliable()) {
            try {
                console.log("unknown command", ev)
                await ev.reply({
                    ephemeral: true,
                    content: "Huuuuuuuh? ... I don't know what to do with that yet.",
                })
            } catch (ex) {
                console.log("failed sending unknown command reply", ex)
            }
        } else {
            console.log("got an interaction but can't reply to it")
        }
    })
    await reportStarted()
    await c.login(process.env.DISCORD_TOKEN || "")
}

bot().catch((ex) => {
    console.log("main thread failed", ex)
})