import fullAccessDbHandler from "../fullAccessDbHandler";
import varReadOnlyDatabaseDbHandler from "../varReadOnlyDatabaseDbHandler";
import serverError from "../serverError";
import addDbEntry from "./addDbEntry";
import updateDbEntry from "./updateDbEntry";
import deleteDbEntry from "./deleteDbEntry";
import trimSql from "../../../utils/trim-sql";
import { DSQL_TableSchemaType } from "../../../types";

export const DbContextsArray = ["Master", "Dsql User"] as const;

type Param = {
    dbContext?: (typeof DbContextsArray)[number];
    dbFullName: string;
    query: string | any;
    readOnly?: boolean;
    debug?: boolean;
    dbSchema?: import("../../../types").DSQL_DatabaseSchemaType;
    queryValuesArray?: (string | number)[];
    tableName?: string;
    forceLocal?: boolean;
};

type Return = {
    result: any;
    error?: string;
};

/**
 * # Run DSQL users queries
 */
export default async function runQuery({
    dbFullName,
    query,
    readOnly,
    dbSchema,
    queryValuesArray,
    tableName,
    debug,
    dbContext,
    forceLocal,
}: Param): Promise<Return> {
    /**
     * Declare variables
     *
     * @description Declare "results" variable
     */

    let result: any;
    let error: string | undefined;
    let tableSchema: DSQL_TableSchemaType | undefined;

    if (dbSchema) {
        try {
            const table = tableName
                ? tableName
                : typeof query == "string"
                ? null
                : query
                ? query?.table
                : null;
            if (!table) throw new Error("No table name provided");
            tableSchema = dbSchema.tables.filter(
                (tb) => tb?.tableName === table
            )[0];
        } catch (_err) {
            // console.log("ERROR getting tableSchema: ", _err.message);
        }
    }

    /**
     * Declare variables
     *
     * @description Declare "results" variable
     */
    try {
        if (typeof query === "string") {
            const formattedQuery = trimSql(query);

            if (debug && global.DSQL_USE_LOCAL) {
                console.log("runQuery:formattedQuery", formattedQuery);
            }

            /**
             * Input Validation
             *
             * @description Input Validation
             */
            if (readOnly && formattedQuery.match(/^alter|^delete|^create/i)) {
                throw new Error("Wrong Input!");
            }

            if (readOnly) {
                result = await varReadOnlyDatabaseDbHandler({
                    queryString: formattedQuery,
                    queryValuesArray: queryValuesArray?.map((vl) => String(vl)),
                    tableSchema,
                    forceLocal,
                });
            } else {
                result = await fullAccessDbHandler({
                    queryString: formattedQuery,
                    queryValuesArray: queryValuesArray?.map((vl) => String(vl)),
                    tableSchema,
                    forceLocal,
                });
            }
        } else if (typeof query === "object") {
            /**
             * Declare variables
             *
             * @description Declare "results" variable
             */
            const {
                data,
                action,
                table,
                identifierColumnName,
                identifierValue,
                update,
                duplicateColumnName,
                duplicateColumnValue,
            } = query;

            switch (action.toLowerCase()) {
                case "insert":
                    result = await addDbEntry({
                        dbContext,
                        dbFullName: dbFullName,
                        tableName: table,
                        data: data,
                        update,
                        duplicateColumnName,
                        duplicateColumnValue,
                        tableSchema,
                    });

                    if (!result?.insertId) {
                        error = "Couldn't insert data";
                    }

                    break;

                case "update":
                    result = await updateDbEntry({
                        dbContext,
                        dbFullName: dbFullName,
                        tableName: table,
                        data: data,
                        identifierColumnName,
                        identifierValue,
                        tableSchema,
                    });

                    break;

                case "delete":
                    result = await deleteDbEntry({
                        dbContext,
                        dbFullName: dbFullName,
                        tableName: table,
                        identifierColumnName,
                        identifierValue,
                        tableSchema,
                    });

                    break;

                default:
                    result = null;
                    break;
            }
        }
    } catch (err: any) {
        serverError({
            component: "functions/backend/runQuery",
            message: err.message,
        });

        if (debug && global.DSQL_USE_LOCAL) {
            console.log("runQuery:error", err.message);
        }

        result = null;
        error = err.message;
    }

    return { result, error };
}
