// @ts-check

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

require("dotenv").config({ path: "../../.env" });
const generator = require("generate-password");
const noDatabaseDbHandler = require("../utils/noDatabaseDbHandler");
const dbHandler = require("../utils/dbHandler");
const encrypt = require("../../package-shared/functions/backend/encrypt");
const decrypt = require("../../package-shared/functions/backend/decrypt");
const { execSync } = require("child_process");
const handleGrants = require("./handleGrants");

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

const userIdFlagIndex = process.argv.findIndex((arg) => arg == "--userId");
const userId = userIdFlagIndex > 0 ? process.argv[userIdFlagIndex + 1] : null;

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

const mariadbUserHostFlagIndex = process.argv.findIndex(
    (arg) => arg == "--host"
);
const mariadbUserHostArg =
    mariadbUserHostFlagIndex > 0
        ? process.argv[mariadbUserHostFlagIndex + 1]
        : null;

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

const mariadbUserFlagIndex = process.argv.findIndex(
    (arg) => arg == "--username"
);
const mariadbUserArg =
    mariadbUserFlagIndex > 0 ? process.argv[mariadbUserFlagIndex + 1] : null;

const sqlUserIDFlagIndex = process.argv.findIndex(
    (arg) => arg == "--sql-user-id"
);
const sqlUserIDArg =
    sqlUserIDFlagIndex > 0 ? process.argv[sqlUserIDFlagIndex + 1] : null;

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

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

/**
 * Create database from Schema Function
 * ==============================================================================
 * @param {object} params - Single object params
 * @param {number|string|null} params.userId - User ID or null
 */
async function refreshUsersAndGrants() {
    /**
     * @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 && sqlUserIDArg
                    ? await dbHandler({
                          query: `SELECT * FROM mariadb_users WHERE id = ? AND user_id = ?`,
                          values: [sqlUserIDArg, userId],
                      })
                    : null;

            /**
             * @type {import("../../package-shared/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(
                mariadbUserHostArg == 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(dsqlPassword);
            if (
                !isUserExisting &&
                !sqlUserIDArg &&
                !isPrimary &&
                !mariadbUserHostArg &&
                !mariadbUserArg
            ) {
                const createNewUser = await noDatabaseDbHandler(
                    `CREATE USER IF NOT EXISTS '${dslUsername}'@'${defaultMariadbUserHost}' IDENTIFIED BY '${dsqlPassword}' REQUIRE SSL`
                );

                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 = mariadbUserHostArg
                    ? mariadbUserHostArg
                    : 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 (mariadbUserArg && username != mariadbUserArg) continue;
                    if (mariadbUserHostArg && host != mariadbUserHostArg)
                        continue;

                    const decrptedPassword = decrypt(password);

                    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}' REQUIRE SSL`
                        );
                    }

                    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();

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

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

refreshUsersAndGrants();
