import generator from "generate-password";
import noDatabaseDbHandler from "../utils/noDatabaseDbHandler";
import dbHandler from "../utils/dbHandler";
import handleGrants from "./handleGrants";
import encrypt from "../../functions/dsql/encrypt";
import decrypt from "../../functions/dsql/decrypt";
import { DSQL_DATASQUIREL_MARIADB_USERS } from "../../types/dsql";
import { MariaDBUser } from "../../types";

type Param = {
    userId?: number | string;
    mariadbUserHost?: string;
    mariadbUsername?: string;
    sqlUserID?: string | number;
};

/**
 * # Refresh Mariadb User Grants
 */
export default async function refreshUsersAndGrants({
    userId,
    mariadbUserHost,
    mariadbUsername,
    sqlUserID,
}: Param) {
    const mariadbUsers = (await dbHandler({
        query: `SELECT * FROM mariadb_users`,
    })) as any[] | null;

    if (!mariadbUsers?.[0]) {
        return;
    }

    const isRootUser = userId
        ? userId == Number(process.env.DSQL_SU_USER_ID)
        : false;

    const isWildcardHost = mariadbUserHost == "%";

    if (isWildcardHost && !isRootUser) {
        return;
    }

    for (let i = 0; i < mariadbUsers.length; i++) {
        const mariadbUser = mariadbUsers[i] as
            | DSQL_DATASQUIREL_MARIADB_USERS
            | undefined;

        if (!mariadbUser) continue;
        if (userId && mariadbUser.user_id != userId) continue;
        if (sqlUserID && mariadbUser.id != sqlUserID) continue;

        try {
            const { username, password, host, user_id } = mariadbUser;

            const existingUser = await noDatabaseDbHandler(
                `SELECT * FROM mysql.user WHERE User = '${username}' AND Host = '${host}'`
            );

            const isUserExisting = Boolean(existingUser?.[0]?.User);

            const isPrimary = String(mariadbUser.primary)?.match(/1/)
                ? true
                : false;

            const dsqlPassword = mariadbUser?.password
                ? decrypt({ encryptedString: mariadbUser.password })
                : isUserExisting && password
                ? decrypt({ encryptedString: password })
                : generator.generate({
                      length: 16,
                      numbers: true,
                      symbols: true,
                      uppercase: true,
                      exclude: "*#.'`\"",
                  });

            const encryptedPassword = mariadbUser?.password
                ? mariadbUser.password
                : isUserExisting
                ? password
                : encrypt({ data: dsqlPassword });

            if (!isUserExisting) {
                if (isWildcardHost) {
                    const _existingUsers = (await noDatabaseDbHandler(
                        `SELECT * FROM mysql.user WHERE user='${mariadbUsername}'`
                    )) as MariaDBUser[];

                    for (let i = 0; i < _existingUsers.length; i++) {
                        const exUsr = _existingUsers[i];
                        await noDatabaseDbHandler(
                            `DROP USER '${exUsr.User}'@'${exUsr.Host}'`
                        );
                    }
                }

                const createNewUser = await noDatabaseDbHandler(
                    `CREATE USER IF NOT EXISTS '${mariadbUsername}'@'${mariadbUserHost}' IDENTIFIED BY '${dsqlPassword}'`
                );
            }

            if (isPrimary) {
                const updateUser = await dbHandler({
                    query: `UPDATE users SET mariadb_user = ?, mariadb_host = ?, mariadb_pass = ? WHERE id = ?`,
                    values: [
                        mariadbUsername,
                        mariadbUserHost,
                        encryptedPassword,
                        user_id,
                    ],
                });
            }

            const isGrantHandled = await handleGrants({
                username: mariadbUser.username,
                host: mariadbUser.host,
                grants:
                    mariadbUser.grants && typeof mariadbUser.grants == "string"
                        ? JSON.parse(mariadbUser.grants)
                        : [],
                userId: String(user_id),
            });

            if (!isGrantHandled) {
                console.log(
                    `Error in handling grants for user ${mariadbUser.username}@${mariadbUser.host}`
                );
            }
        } catch (error: any) {
            global.ERROR_CALLBACK?.(
                `Error Refreshing MariaDB Users and Grants`,
                error as Error
            );
        }
    }
}