// @ts-check

const path = require("path");
require("dotenv").config({ path: path.resolve(__dirname, "../../../.env") });

const generator = require("generate-password");
const noDatabaseDbHandler = require("../utils/noDatabaseDbHandler");
const dbHandler = require("../utils/dbHandler");
const handleGrants = require("./handleGrants");
const encrypt = require("../../functions/dsql/encrypt");
const decrypt = require("../../functions/dsql/decrypt");

const defaultMariadbUserHost = process.env.DSQL_DB_HOST || "127.0.0.1";

/**
 * Refresh Mariadb User Grants
 * ===================================================
 * @param {object} params
 * @param {number | string} [params.userId]
 * @param {string} [params.mariadbUserHost]
 * @param {string} [params.mariadbUser]
 * @param {string | number} [params.sqlUserID]
 */
async function refreshUsersAndGrants({
    userId,
    mariadbUserHost,
    mariadbUser,
    sqlUserID,
}) {
    /**
     * @description Users
     * @type {*[] | null}
     */ // @ts-ignore
    const users = await dbHandler({
        query: `SELECT * FROM users`,
    });

    if (!users?.[0]) {
        process.exit();
    }

    for (let i = 0; i < users.length; i++) {
        const user = users[i];

        if (!user) continue;
        if (userId && user.id != userId) continue;

        try {
            const { mariadb_user, mariadb_host, mariadb_pass, id } = user;
            const existingUser = await noDatabaseDbHandler(
                `SELECT * FROM mysql.user WHERE User = '${mariadb_user}' AND Host = '${mariadb_host}'`
            );

            const existingMariaDBUserArray =
                userId && sqlUserID
                    ? await dbHandler({
                          query: `SELECT * FROM mariadb_users WHERE id = ? AND user_id = ?`,
                          values: [sqlUserID, userId],
                      })
                    : null;

            /**
             * @type {import("../../types").MYSQL_mariadb_users_table_def | undefined}
             */
            const activeMariadbUserObject = Array.isArray(
                existingMariaDBUserArray
            )
                ? existingMariaDBUserArray?.[0]
                : undefined;

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

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

            const isThisPrimaryHost = Boolean(
                mariadbUserHost == defaultMariadbUserHost
            );

            const dslUsername = `dsql_user_${id}`;
            const dsqlPassword = activeMariadbUserObject?.password
                ? activeMariadbUserObject.password
                : isUserExisting
                ? mariadb_pass
                : generator.generate({
                      length: 16,
                      numbers: true,
                      symbols: true,
                      uppercase: true,
                      exclude: "*#.'`\"",
                  });

            const encryptedPassword = activeMariadbUserObject?.password
                ? activeMariadbUserObject.password
                : isUserExisting
                ? mariadb_pass
                : encrypt({
                      data: dsqlPassword,
                      encryptionKey: process.env.DSQL_ENCRYPTION_PASSWORD,
                      encryptionSalt: process.env.DSQL_ENCRYPTION_SALT,
                  });
            if (
                !isUserExisting &&
                !sqlUserID &&
                !isPrimary &&
                !mariadbUserHost &&
                !mariadbUser
            ) {
                const createNewUser = await noDatabaseDbHandler(
                    `CREATE USER IF NOT EXISTS '${dslUsername}'@'${defaultMariadbUserHost}' IDENTIFIED BY '${dsqlPassword}'`
                );

                console.log("createNewUser", createNewUser);

                console.log(
                    `User ${user.id}: ${user.first_name} ${user.last_name} SQL credentials successfully updated.`
                );

                const updateUser = await dbHandler({
                    query: `UPDATE users SET mariadb_user = ?, mariadb_host = ?, mariadb_pass = ? WHERE id = ?`,
                    values: [
                        dslUsername,
                        defaultMariadbUserHost,
                        encryptedPassword,
                        user.id,
                    ],
                });
            }

            if (isPrimary) {
                const finalHost = mariadbUserHost
                    ? mariadbUserHost
                    : mariadb_host;

                const updateUser = await dbHandler({
                    query: `UPDATE users SET mariadb_user = ?, mariadb_host = ?, mariadb_pass = ? WHERE id = ?`,
                    values: [
                        dslUsername,
                        finalHost,
                        encryptedPassword,
                        user.id,
                    ],
                });
            }

            //////////////////////////////////////////////
            //////////////////////////////////////////////
            //////////////////////////////////////////////

            /**
             * @description Handle mariadb_users table
             */
            const existingMariadbPrimaryUser = await dbHandler({
                query: `SELECT * FROM mariadb_users WHERE user_id = ? AND \`primary\` = 1`,
                values: [id],
            });

            const isPrimaryUserExisting = Boolean(
                Array.isArray(existingMariadbPrimaryUser) &&
                    existingMariadbPrimaryUser?.[0]?.user_id
            );

            /** @type {import("./handleGrants").GrantType[]} */
            const primaryUserGrants = [
                {
                    database: "*",
                    table: "*",
                    privileges: ["ALL"],
                },
            ];

            if (!isPrimaryUserExisting) {
                const insertPrimaryMariadbUser = await dbHandler({
                    query: `INSERT INTO mariadb_users (user_id, username, password, \`primary\`, grants) VALUES (?, ?, ?, ?, ?)`,
                    values: [
                        id,
                        dslUsername,
                        encryptedPassword,
                        "1",
                        JSON.stringify(primaryUserGrants),
                    ],
                });
            }

            //////////////////////////////////////////////

            const existingExtraMariadbUsers = await dbHandler({
                query: `SELECT * FROM mariadb_users WHERE user_id = ? AND \`primary\` != '1'`,
                values: [id],
            });

            if (Array.isArray(existingExtraMariadbUsers)) {
                for (let i = 0; i < existingExtraMariadbUsers.length; i++) {
                    const mariadbUser = existingExtraMariadbUsers[i];
                    const {
                        user_id,
                        username,
                        host,
                        password,
                        primary,
                        grants,
                    } = mariadbUser;

                    if (mariadbUser && username != mariadbUser) continue;
                    if (mariadbUserHost && host != mariadbUserHost) continue;

                    const decrptedPassword = decrypt({
                        encryptedString: password,
                        encryptionKey: process.env.DSQL_ENCRYPTION_PASSWORD,
                        encryptionSalt: process.env.DSQL_ENCRYPTION_SALT,
                    });

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

                    const isExtraMariadbUserExisting = Boolean(
                        existingExtraMariadbUser?.[0]?.User
                    );

                    if (!isExtraMariadbUserExisting) {
                        await noDatabaseDbHandler(
                            `CREATE USER IF NOT EXISTS '${username}'@'${host}' IDENTIFIED BY '${decrptedPassword}'`
                        );
                    }

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

                    if (!isGrantHandled) {
                        console.log(
                            `Error in handling grants for user ${username}@${host}`
                        );
                    }
                }
            }

            //////////////////////////////////////////////
            //////////////////////////////////////////////
            //////////////////////////////////////////////
        } catch (/** @type {any} */ error) {
            console.log(`Error in adding SQL user =>`, error.message);
        }
    }

    process.exit();

    ////////////////////////////////////////
    ////////////////////////////////////////
    ////////////////////////////////////////
}

/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */

module.exports = refreshUsersAndGrants;
