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

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

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

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;

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

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

        try {
            const { mariadb_user, mariadb_host, mariadb_pass, user_id } =
                mariadbUser;
            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;

            const activeMariadbUserObject:
                | import("../../types").MYSQL_mariadb_users_table_def
                | undefined = 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 = isRootUser
                ? mariadbUsername
                : `dsql_user_${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 &&
                !mariadbUsername
            ) {
                const createNewUser = await noDatabaseDbHandler(
                    `CREATE USER IF NOT EXISTS '${dslUsername}'@'${defaultMariadbUserHost}' IDENTIFIED BY '${dsqlPassword}'`
                );

                console.log("createNewUser", createNewUser);

                console.log(
                    `User ${mariadbUser.id}: ${mariadbUser.first_name} ${mariadbUser.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,
                        mariadbUser.id,
                    ],
                });
            } else if (!isUserExisting && mariadbUserHost) {
                const createNewUser = await noDatabaseDbHandler(
                    `CREATE USER IF NOT EXISTS '${dslUsername}'@'${mariadbUserHost}' IDENTIFIED BY '${dsqlPassword}'`
                );
            }

            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,
                        mariadbUser.id,
                    ],
                });
            }

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

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

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

            const primaryUserGrants: GrantType[] = [
                {
                    database: "*",
                    table: "*",
                    privileges: ["ALL"],
                },
            ];

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

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

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

            if (Array.isArray(existingExtraMariadbUsers)) {
                for (let i = 0; i < existingExtraMariadbUsers.length; i++) {
                    const _mariadbUser = existingExtraMariadbUsers[
                        i
                    ] as MYSQL_mariadb_users_table_def;

                    if (
                        _mariadbUser &&
                        _mariadbUser.username != mariadbUsername
                    )
                        continue;
                    if (mariadbUserHost && _mariadbUser.host != mariadbUserHost)
                        continue;

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

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

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

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

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

                    if (!isGrantHandled) {
                        console.log(
                            `Error in handling grants for user ${_mariadbUser.username}@${_mariadbUser.host}`
                        );
                    }
                }
            }
        } catch (error: any) {
            console.log(`Error in adding SQL user =>`, error.message);
        }
    }
}
