module commands.moderation;

import util, api_data, api;
import asdf;
import core.stdc.time: time;
import commands.util;

import main: db;

enum TimeErr: int {
    @("Not enough arguments") NotEnoughArguments = -1,
    @("Invalid date") InvalidDate = -2,
    @("Invalid unit") InvalidUnit = -3
}

int parseTime(string str) @nogc pure {
    if (str.length < 2) return TimeErr.NotEnoughArguments;
    foreach (short c; str[0..$-1])
        if (!(c >= 48 && c <= 57))
            return TimeErr.InvalidDate;

    static foreach(unit; __traits(allMembers, Time))
        if (str[$-1] == __traits(getAttributes, __traits(getMember, Time, unit))[0])
            return strToInt(str[0..$-1]) * __traits(getMember, Time, unit);

    return TimeErr.InvalidUnit;
}

@Command("Admin tools")
MSG nctl(Arguments arg, EventWithoutRoomID* evt) {
    if (arg.parsed.length != 2) return MSG("❌️ Not enough arguments");
    if (getUsrPL(evt.room, evt.sender) < 50) return MSG("❌️ Unauthorized");
    db.exec("create table if not exists nctl (room string, user string, nc string, type string, unt integer)");
    
    bool wd, err;
    string cntnt, type;

    int dur;
    auto durStr = arg.getArg("D");
    if (durStr) dur = parseTime(durStr);

    if (dur < 0) {
        static foreach(Terr; __traits(allMembers, TimeErr)) {
            if (dur == __traits(getMember, TimeErr, Terr)) {
                return MSG("💥 " ~ __traits(getAttributes, __traits(getMember, TimeErr, Terr))[0]);
            }
        }
    }

    void mrq(string ep) {
        short rq = mkhsrq(
            "/_matrix/client/v3/rooms/"~evt.room~'/'~ep,
            "POST",
            Moderate(arg.parsed[1], "Moderated by "~evt.sender).serializeToJson
        ).status;
        if (rq != 200) err = true;
    }

    void setpl(short pl) {
        auto pls = State.PowerLevels(getUsrsPLs(evt.room));
        if (dur) cntnt = pls.serializeToJson;
        pls.users[arg.parsed[1]] = pl;
        state(evt.room, "m.room.power_levels", pls);
    }

    bool c() {
        if (db.query("select type from nctl where room = ? and user = ?",evt.room, arg.parsed[1]).step)
            return true;
        return false;
    }

    switch (arg.parsed[0]) {
        case "ban":
            wd = true;
            mrq("ban");
            break;
        case "kick", "unban":
            mrq(arg.parsed[0]);
            break;
        case "mute":
            if(c) goto e;
            type = "m.room.power_levels";
            wd = true;
            setpl(-100);
            break;
        case "unmute": setpl(0); break;
        case "acl", "delete-acl":
            State.ACL acls = getState!(State.ACL)(evt.room, "m.room.server_acl");
            if (dur) cntnt = acls.serializeToJson;

            if (arg.parsed[0] == "acl") {
                if(c) goto e;
                type = "m.room.server_acl";
                wd = true;
                acls.deny ~= arg.parsed[1];
            } else {
                foreach (n, srv; acls.deny) {
                    if (srv == arg.parsed[1])
                        acls.deny = acls.deny[0..n] ~ acls.deny[n+1..$];
                }
            }
            
            state(evt.room, "m.room.server_acl", acls);
            break;
        default: return MSG("❓️ Invalid argument");
    }

    if (err) return MSG("💥 Something went wrong");

    if (dur && wd) {
        import std.datetime.systime: SysTime;
        auto untill = time(null) + dur;

        db.exec(
            "insert into nctl (room, user, nc, type, unt) values (?,?,?,?,?)",
            evt.room,
            arg.parsed[1],
            cntnt,
            type,
            untill
        );

        return MSG("✅️ Blocked untill " ~ SysTime.fromUnixTime(untill).toSimpleString);
    }

    return MSG("✅️");
    e: return MSG("💥 Already moderated!");
}