import express from "express";
import { createServer } from "http";
import { ModuleRpcServer } from "rpc_ts/lib/server";
import { ModuleRpcProtocolServer } from "rpc_ts/lib/protocol/server";
import { NoctBotService } from "@shared/rpc/backend";
import { logger } from "./logging";
import { client } from "./client";
import { tryDo } from "@shared/common/async_utils";
import { getRepository } from "typeorm";
import { GuildVerification } from "@shared/db/entity/GuildVerification";
import { isAuthorisedAsync } from "./util";
import { GuildMember } from "discord.js";

const PORT = +(process.env.RPC_PORT ?? "8181");

const app = express();

async function checkUser(userId: string, check: (user: GuildMember, verification: GuildVerification) => Promise<boolean>): Promise<boolean> {
    const verificationGuildRepo = getRepository(GuildVerification);
    const guilds = await verificationGuildRepo.find({
        select: [
            "guildId",
            "verifiedRoleId",
        ]
    });
    for (const guild of guilds) {
        const guildInstance = await tryDo(client.bot.guilds.fetch(guild.guildId));
        if (!guildInstance.ok) {
            logger.error("Failed to fetch guild instance for guild %s: %s", guild.guildId, guildInstance.error);
            continue;
        }
        const user = await tryDo(guildInstance.result.members.fetch(userId));
        if (user.ok && await check(user.result, guild)) {
            return true;
        }
    }
    return false;
}

const handler: ModuleRpcServer.ServiceHandlerFor<typeof NoctBotService> = {
    async getPing({ ping }): Promise<{ text: string }> {
        return { text: `pong: ${ping}` };
    },

    async userInServer({ userId }): Promise<{ exists: boolean }> {
        return { exists: await checkUser(userId, async () => true) };
    },

    async userAuthorised({ userId }): Promise<{ authorised: boolean }> {
        return { authorised: await checkUser(userId, (user) => isAuthorisedAsync(user)) };
    },
    async userVerified({ userId }): Promise<{ verified: boolean }> {
        return { verified: await checkUser(userId, async (user, guild) => user.roles.cache.has(guild.verifiedRoleId)) };
    },
    async verifyUser({ userId }): Promise<{ ok: boolean }> {
        return { ok: !(await checkUser(userId, async (user, guild) => !(await tryDo(user.roles.add(guild.verifiedRoleId))).ok)) };
    }
};

app.use(ModuleRpcProtocolServer.registerRpcRoutes(NoctBotService, handler));

export function startRpcServer(): void {
    logger.info(`Starting RPC at *:${PORT}`);
    createServer(app).listen(PORT);
}