import { DEFAULT_SQL_DATABASE_PROMPT, SQL_SAP_HANA_PROMPT, SQL_MSSQL_PROMPT, SQL_MYSQL_PROMPT, SQL_POSTGRES_PROMPT, SQL_SQLITE_PROMPT, SQL_ORACLE_PROMPT, } from "../chains/sql_db/sql_db_prompt.js";
export const verifyListTablesExistInDatabase = (tablesFromDatabase, listTables, errorPrefixMsg) => {
    const onlyTableNames = tablesFromDatabase.map((table) => table.tableName);
    if (listTables.length > 0) {
        for (const tableName of listTables) {
            if (!onlyTableNames.includes(tableName)) {
                throw new Error(`${errorPrefixMsg} the table ${tableName} was not found in the database`);
            }
        }
    }
};
export const verifyIncludeTablesExistInDatabase = (tablesFromDatabase, includeTables) => {
    verifyListTablesExistInDatabase(tablesFromDatabase, includeTables, "Include tables not found in database:");
};
export const verifyIgnoreTablesExistInDatabase = (tablesFromDatabase, ignoreTables) => {
    verifyListTablesExistInDatabase(tablesFromDatabase, ignoreTables, "Ignore tables not found in database:");
};
const formatToSqlTable = (rawResultsTableAndColumn) => {
    const sqlTable = [];
    for (const oneResult of rawResultsTableAndColumn) {
        const sqlColumn = {
            columnName: oneResult.column_name,
            dataType: oneResult.data_type,
            isNullable: oneResult.is_nullable === "YES",
        };
        const currentTable = sqlTable.find((oneTable) => oneTable.tableName === oneResult.table_name);
        if (currentTable) {
            currentTable.columns.push(sqlColumn);
        }
        else {
            const newTable = {
                tableName: oneResult.table_name,
                columns: [sqlColumn],
            };
            sqlTable.push(newTable);
        }
    }
    return sqlTable;
};
export const getTableAndColumnsName = async (appDataSource) => {
    let sql;
    if (appDataSource.options.type === "postgres") {
        const schema = appDataSource.options?.schema ?? "public";
        sql = `SELECT 
            t.table_name, 
            c.* 
          FROM 
            information_schema.tables t 
              JOIN information_schema.columns c 
                ON t.table_name = c.table_name 
          WHERE 
            t.table_schema = '${schema}' 
              AND c.table_schema = '${schema}' 
          ORDER BY 
            t.table_name,
            c.ordinal_position;`;
        const rep = await appDataSource.query(sql);
        return formatToSqlTable(rep);
    }
    if (appDataSource.options.type === "sqlite" ||
        appDataSource.options.type === "better-sqlite3" ||
        appDataSource.options.type === "sqljs") {
        sql =
            "SELECT \n" +
                "   m.name AS table_name,\n" +
                "   p.name AS column_name,\n" +
                "   p.type AS data_type,\n" +
                "   CASE \n" +
                "      WHEN p.\"notnull\" = 0 THEN 'YES' \n" +
                "      ELSE 'NO' \n" +
                "   END AS is_nullable \n" +
                "FROM \n" +
                "   sqlite_master m \n" +
                "JOIN \n" +
                "   pragma_table_info(m.name) p \n" +
                "WHERE \n" +
                "   m.type = 'table' AND \n" +
                "   m.name NOT LIKE 'sqlite_%';\n";
        const rep = await appDataSource.query(sql);
        return formatToSqlTable(rep);
    }
    if (appDataSource.options.type === "mysql" ||
        appDataSource.options.type === "aurora-mysql") {
        sql =
            "SELECT " +
                "TABLE_NAME AS table_name, " +
                "COLUMN_NAME AS column_name, " +
                "DATA_TYPE AS data_type, " +
                "IS_NULLABLE AS is_nullable " +
                "FROM INFORMATION_SCHEMA.COLUMNS " +
                `WHERE TABLE_SCHEMA = '${appDataSource.options.database}';`;
        const rep = await appDataSource.query(sql);
        return formatToSqlTable(rep);
    }
    if (appDataSource.options.type === "mssql") {
        const schema = appDataSource.options?.schema;
        const sql = `SELECT
    TABLE_NAME AS table_name,
    COLUMN_NAME AS column_name,
    DATA_TYPE AS data_type,
    IS_NULLABLE AS is_nullable
    FROM INFORMATION_SCHEMA.COLUMNS
    ${schema && `WHERE TABLE_SCHEMA = '${schema}'`} 
ORDER BY TABLE_NAME, ORDINAL_POSITION;`;
        const rep = await appDataSource.query(sql);
        return formatToSqlTable(rep);
    }
    if (appDataSource.options.type === "sap") {
        const schema = appDataSource.options?.schema ?? "public";
        sql = `SELECT
        TABLE_NAME,
        COLUMN_NAME,
        DATA_TYPE_NAME AS data_type,
        CASE WHEN IS_NULLABLE='TRUE' THEN 'YES' ELSE 'NO' END AS is_nullable
      FROM TABLE_COLUMNS
      WHERE SCHEMA_NAME='${schema}'`;
        const rep = await appDataSource.query(sql);
        const repLowerCase = [];
        rep.forEach((_rep) => repLowerCase.push({
            table_name: _rep.TABLE_NAME,
            column_name: _rep.COLUMN_NAME,
            data_type: _rep.DATA_TYPE,
            is_nullable: _rep.IS_NULLABLE,
        }));
        return formatToSqlTable(repLowerCase);
    }
    if (appDataSource.options.type === "oracle") {
        const schemaName = appDataSource.options.schema;
        const sql = `  
      SELECT
          TABLE_NAME AS "table_name",
          COLUMN_NAME AS "column_name",
          DATA_TYPE AS "data_type",
          NULLABLE AS "is_nullable"
      FROM ALL_TAB_COLS
      WHERE
          OWNER = UPPER('${schemaName}')`;
        const rep = await appDataSource.query(sql);
        return formatToSqlTable(rep);
    }
    throw new Error("Database type not implemented yet");
};
const formatSqlResponseToSimpleTableString = (rawResult) => {
    if (!rawResult || !Array.isArray(rawResult) || rawResult.length === 0) {
        return "";
    }
    let globalString = "";
    for (const oneRow of rawResult) {
        globalString += `${Object.values(oneRow).reduce((completeString, columnValue) => `${completeString} ${columnValue}`, "")}\n`;
    }
    return globalString;
};
export const generateTableInfoFromTables = async (tables, appDataSource, nbSampleRow, customDescription) => {
    if (!tables) {
        return "";
    }
    let globalString = "";
    for (const currentTable of tables) {
        // Add the custom info of the table
        const tableCustomDescription = customDescription &&
            Object.keys(customDescription).includes(currentTable.tableName)
            ? `${customDescription[currentTable.tableName]}\n`
            : "";
        // Add the creation of the table in SQL
        let schema = null;
        if (appDataSource.options.type === "postgres") {
            schema = appDataSource.options?.schema ?? "public";
        }
        else if (appDataSource.options.type === "mssql") {
            schema = appDataSource.options?.schema;
        }
        else if (appDataSource.options.type === "sap") {
            schema =
                appDataSource.options?.schema ??
                    appDataSource.options?.username ??
                    "public";
        }
        else if (appDataSource.options.type === "oracle") {
            schema = appDataSource.options.schema;
        }
        let sqlCreateTableQuery = schema
            ? `CREATE TABLE "${schema}"."${currentTable.tableName}" (\n`
            : `CREATE TABLE ${currentTable.tableName} (\n`;
        for (const [key, currentColumn] of currentTable.columns.entries()) {
            if (key > 0) {
                sqlCreateTableQuery += ", ";
            }
            sqlCreateTableQuery += `${currentColumn.columnName} ${currentColumn.dataType} ${currentColumn.isNullable ? "" : "NOT NULL"}`;
        }
        sqlCreateTableQuery += ") \n";
        let sqlSelectInfoQuery;
        if (appDataSource.options.type === "mysql") {
            // We use backticks to quote the table names and thus allow for example spaces in table names
            sqlSelectInfoQuery = `SELECT * FROM \`${currentTable.tableName}\` LIMIT ${nbSampleRow};\n`;
        }
        else if (appDataSource.options.type === "postgres") {
            const schema = appDataSource.options?.schema ?? "public";
            sqlSelectInfoQuery = `SELECT * FROM "${schema}"."${currentTable.tableName}" LIMIT ${nbSampleRow};\n`;
        }
        else if (appDataSource.options.type === "mssql") {
            const schema = appDataSource.options?.schema;
            sqlSelectInfoQuery = schema
                ? `SELECT TOP ${nbSampleRow} * FROM ${schema}.[${currentTable.tableName}];\n`
                : `SELECT TOP ${nbSampleRow} * FROM [${currentTable.tableName}];\n`;
        }
        else if (appDataSource.options.type === "sap") {
            const schema = appDataSource.options?.schema ??
                appDataSource.options?.username ??
                "public";
            sqlSelectInfoQuery = `SELECT * FROM "${schema}"."${currentTable.tableName}" LIMIT ${nbSampleRow};\n`;
        }
        else if (appDataSource.options.type === "oracle") {
            sqlSelectInfoQuery = `SELECT * FROM "${schema}"."${currentTable.tableName}" WHERE ROWNUM <= '${nbSampleRow}'`;
        }
        else {
            sqlSelectInfoQuery = `SELECT * FROM "${currentTable.tableName}" LIMIT ${nbSampleRow};\n`;
        }
        const columnNamesConcatString = `${currentTable.columns.reduce((completeString, column) => `${completeString} ${column.columnName}`, "")}\n`;
        let sample = "";
        try {
            const infoObjectResult = nbSampleRow
                ? await appDataSource.query(sqlSelectInfoQuery)
                : null;
            sample = formatSqlResponseToSimpleTableString(infoObjectResult);
        }
        catch (error) {
            // If the request fails we catch it and only display a log message
            console.log(error);
        }
        globalString = globalString.concat(tableCustomDescription +
            sqlCreateTableQuery +
            sqlSelectInfoQuery +
            columnNamesConcatString +
            sample);
    }
    return globalString;
};
export const getPromptTemplateFromDataSource = (appDataSource) => {
    if (appDataSource.options.type === "postgres") {
        return SQL_POSTGRES_PROMPT;
    }
    if (appDataSource.options.type === "sqlite") {
        return SQL_SQLITE_PROMPT;
    }
    if (appDataSource.options.type === "mysql") {
        return SQL_MYSQL_PROMPT;
    }
    if (appDataSource.options.type === "mssql") {
        return SQL_MSSQL_PROMPT;
    }
    if (appDataSource.options.type === "sap") {
        return SQL_SAP_HANA_PROMPT;
    }
    if (appDataSource.options.type === "oracle") {
        return SQL_ORACLE_PROMPT;
    }
    return DEFAULT_SQL_DATABASE_PROMPT;
};
