#!/usr/bin/env node
"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
    return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
var _a = require('child_process'), execFileSync = _a.execFileSync, execSync = _a.execSync;
var fs = require('fs');
var path = require('path');
var readline = require('readline');
var Handlebars = require('handlebars');
var package_json_1 = require("../package.json");
var args = process.argv.slice(2); // Slice the first two elements
var argMap = {};
for (var i = 0; i < args.length; i += 2) {
    argMap[args[i]] = args[i + 1];
}
var sanitizeIdentifier = function (value) {
    var normalized = value
        .trim()
        .replace(/[^A-Za-z0-9_]/g, "_")
        .replace(/^[^A-Za-z_]+/, "");
    return normalized.length > 0 ? normalized : "db";
};
var toPascalCaseFromIdentifier = function (identifier) {
    return identifier
        .split("_")
        .filter(Boolean)
        .map(capitalizeFirstLetter)
        .join("_") || "Db";
};
var toConstantIdentifier = function (value) {
    var normalized = value
        .toUpperCase()
        .replace(/[^A-Z0-9_]/g, "_")
        .replace(/_+/g, "_")
        .replace(/^([^A-Z_])/, "_$1");
    return normalized.length > 0 ? normalized : "COLUMN";
};
var createDirIfNotExists = function (dir) {
    return !fs.existsSync(dir) ? fs.mkdirSync(dir, { recursive: true }) : undefined;
};
var ensureString = function (value, label) {
    if (typeof value !== "string" || value.trim().length === 0) {
        throw new Error("".concat(label, " must be a non-empty string."));
    }
    return value.trim();
};
var resolveConfigPassword = function (entry) {
    if (typeof entry.pass === "string" && entry.pass.trim().length > 0) {
        return entry.pass.trim();
    }
    if (typeof entry.passEnv === "string" && entry.passEnv.trim().length > 0) {
        var envKey = entry.passEnv.trim();
        var envValue = process.env[envKey];
        if (typeof envValue !== "string" || envValue.trim().length === 0) {
            throw new Error("Environment variable '".concat(envKey, "' is required for database alias '").concat(entry.alias, "'."));
        }
        return envValue;
    }
    throw new Error("Database alias '".concat(entry.alias, "' must provide either 'pass' or 'passEnv'."));
};
var parseConfigDbNames = function (entry) {
    if (Array.isArray(entry.dbnames)) {
        var names = entry.dbnames
            .map(function (dbName) { return ensureString(dbName, "databases[".concat(entry.alias, "].dbnames[]")); })
            .filter(Boolean);
        if (names.length > 0) {
            return names;
        }
    }
    if (typeof entry.dbname === "string" && entry.dbname.trim().length > 0) {
        return [entry.dbname.trim()];
    }
    throw new Error("Database alias '".concat(entry.alias, "' must provide 'dbname' or a non-empty 'dbnames' array."));
};
var parseConfigDialect = function (entry) {
    var _a;
    var raw = String((_a = entry.dialect) !== null && _a !== void 0 ? _a : 'mysql').trim().toLowerCase();
    if (raw === 'mysql' || raw === 'postgresql') {
        return raw;
    }
    throw new Error("Database alias '".concat(entry.alias, "' has unsupported dialect '").concat(entry.dialect, "'. Use 'mysql' or 'postgresql'."));
};
var aliasForConfigDbName = function (baseAlias, dbName, totalDbNames) {
    if (totalDbNames <= 1 || dbName === baseAlias) {
        return baseAlias;
    }
    return "".concat(baseAlias, "_").concat(dbName);
};
var buildScopedDefinitionsFromConfig = function (config) {
    var e_1, _a, e_2, _b, e_3, _c;
    var _d;
    if (!Array.isArray(config.databases) || config.databases.length === 0) {
        throw new Error("Config must define a non-empty 'databases' array.");
    }
    var scopedDefinitions = [];
    try {
        for (var _e = __values(config.databases), _f = _e.next(); !_f.done; _f = _e.next()) {
            var entry = _f.value;
            var baseAlias = ensureString(entry.alias, "databases[].alias");
            var dialect = parseConfigDialect(entry);
            var host = ensureString(entry.host, "databases[".concat(baseAlias, "].host"));
            var user = ensureString(entry.user, "databases[".concat(baseAlias, "].user"));
            var port = String((_d = entry.port) !== null && _d !== void 0 ? _d : (dialect === 'postgresql' ? "5432" : "3306")).trim();
            if (port.length === 0) {
                throw new Error("databases[".concat(baseAlias, "].port must be non-empty when provided."));
            }
            var pass = resolveConfigPassword(entry);
            var dbNames = parseConfigDbNames(entry);
            try {
                for (var dbNames_1 = (e_2 = void 0, __values(dbNames)), dbNames_1_1 = dbNames_1.next(); !dbNames_1_1.done; dbNames_1_1 = dbNames_1.next()) {
                    var dbName = dbNames_1_1.value;
                    var runtimeAlias = aliasForConfigDbName(baseAlias, dbName, dbNames.length);
                    var aliasIdentifier = sanitizeIdentifier(runtimeAlias);
                    scopedDefinitions.push({
                        DATABASE_KEY: runtimeAlias,
                        DATABASE_NAME: dbName,
                        DATABASE_KEY_IDENTIFIER: aliasIdentifier,
                        DATABASE_KEY_PASCAL_CASE: toPascalCaseFromIdentifier(aliasIdentifier),
                        DIALECT: dialect,
                        CONNECTION: {
                            host: host,
                            port: port,
                            user: user,
                            pass: pass,
                        },
                    });
                }
            }
            catch (e_2_1) { e_2 = { error: e_2_1 }; }
            finally {
                try {
                    if (dbNames_1_1 && !dbNames_1_1.done && (_b = dbNames_1.return)) _b.call(dbNames_1);
                }
                finally { if (e_2) throw e_2.error; }
            }
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
        }
        finally { if (e_1) throw e_1.error; }
    }
    var seenRuntimeKeys = new Set();
    var seenRuntimeIdentifiers = new Set();
    try {
        for (var scopedDefinitions_1 = __values(scopedDefinitions), scopedDefinitions_1_1 = scopedDefinitions_1.next(); !scopedDefinitions_1_1.done; scopedDefinitions_1_1 = scopedDefinitions_1.next()) {
            var scopedDefinition = scopedDefinitions_1_1.value;
            if (seenRuntimeKeys.has(scopedDefinition.DATABASE_KEY)) {
                throw new Error("Duplicate database alias '".concat(scopedDefinition.DATABASE_KEY, "' in config."));
            }
            seenRuntimeKeys.add(scopedDefinition.DATABASE_KEY);
            if (seenRuntimeIdentifiers.has(scopedDefinition.DATABASE_KEY_IDENTIFIER)) {
                throw new Error("Database aliases map to duplicate identifier '".concat(scopedDefinition.DATABASE_KEY_IDENTIFIER, "'."));
            }
            seenRuntimeIdentifiers.add(scopedDefinition.DATABASE_KEY_IDENTIFIER);
        }
    }
    catch (e_3_1) { e_3 = { error: e_3_1 }; }
    finally {
        try {
            if (scopedDefinitions_1_1 && !scopedDefinitions_1_1.done && (_c = scopedDefinitions_1.return)) _c.call(scopedDefinitions_1);
        }
        finally { if (e_3) throw e_3.error; }
    }
    return scopedDefinitions;
};
var CONFIG_DISCOVERY_NAMES = [
    ".C6.ts",
    ".C6.json",
    "C6.config.ts",
    "C6.config.json",
];
var discoverConfigPath = function (startDir) {
    var e_4, _a;
    var current = path.resolve(startDir);
    while (true) {
        try {
            for (var CONFIG_DISCOVERY_NAMES_1 = (e_4 = void 0, __values(CONFIG_DISCOVERY_NAMES)), CONFIG_DISCOVERY_NAMES_1_1 = CONFIG_DISCOVERY_NAMES_1.next(); !CONFIG_DISCOVERY_NAMES_1_1.done; CONFIG_DISCOVERY_NAMES_1_1 = CONFIG_DISCOVERY_NAMES_1.next()) {
                var configName = CONFIG_DISCOVERY_NAMES_1_1.value;
                var candidate = path.join(current, configName);
                if (fs.existsSync(candidate)) {
                    return candidate;
                }
            }
        }
        catch (e_4_1) { e_4 = { error: e_4_1 }; }
        finally {
            try {
                if (CONFIG_DISCOVERY_NAMES_1_1 && !CONFIG_DISCOVERY_NAMES_1_1.done && (_a = CONFIG_DISCOVERY_NAMES_1.return)) _a.call(CONFIG_DISCOVERY_NAMES_1);
            }
            finally { if (e_4) throw e_4.error; }
        }
        var parent_1 = path.dirname(current);
        if (parent_1 === current) {
            return undefined;
        }
        current = parent_1;
    }
};
var askQuestion = function (rl, question) { return __awaiter(void 0, void 0, void 0, function () {
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0: return [4 /*yield*/, new Promise(function (resolve) {
                    rl.question(question, function (answer) { return resolve(answer.trim()); });
                })];
            case 1: return [2 /*return*/, _a.sent()];
        }
    });
}); };
var buildConfigInteractively = function () { return __awaiter(void 0, void 0, void 0, function () {
    var rl, databases, alias, _a, dialect, _b, host, _c, defaultPort, port, user, _d, pass, _e, dbnamesRaw, _f, addAnother;
    var _g;
    return __generator(this, function (_h) {
        switch (_h.label) {
            case 0:
                rl = readline.createInterface({
                    input: process.stdin,
                    output: process.stdout,
                });
                _h.label = 1;
            case 1:
                _h.trys.push([1, , 12, 13]);
                console.log("[generateRestBindings] No config found. Let's create C6.config.json.");
                databases = [];
                _h.label = 2;
            case 2:
                if (!true) return [3 /*break*/, 11];
                _a = ensureString;
                return [4 /*yield*/, askQuestion(rl, "Database alias (e.g. app): ")];
            case 3:
                alias = _a.apply(void 0, [_h.sent(), "alias"]);
                _b = parseConfigDialect;
                _g = {
                    alias: alias
                };
                return [4 /*yield*/, askQuestion(rl, "Dialect (mysql/postgresql, default mysql): ")];
            case 4:
                dialect = _b.apply(void 0, [(_g.dialect = (_h.sent()) || "mysql",
                        _g)]);
                _c = ensureString;
                return [4 /*yield*/, askQuestion(rl, "Host (e.g. 127.0.0.1): ")];
            case 5:
                host = _c.apply(void 0, [_h.sent(), "host"]);
                defaultPort = dialect === "postgresql" ? "5432" : "3306";
                return [4 /*yield*/, askQuestion(rl, "Port (default ".concat(defaultPort, "): "))];
            case 6:
                port = (_h.sent()) || defaultPort;
                _d = ensureString;
                return [4 /*yield*/, askQuestion(rl, "User: ")];
            case 7:
                user = _d.apply(void 0, [_h.sent(), "user"]);
                _e = ensureString;
                return [4 /*yield*/, askQuestion(rl, "Password: ")];
            case 8:
                pass = _e.apply(void 0, [_h.sent(), "pass"]);
                _f = ensureString;
                return [4 /*yield*/, askQuestion(rl, "Schema names (comma-separated, e.g. app,billing): ")];
            case 9:
                dbnamesRaw = _f.apply(void 0, [_h.sent(), "dbnames"]);
                databases.push({
                    alias: alias,
                    dialect: dialect,
                    host: host,
                    port: port,
                    user: user,
                    pass: pass,
                    dbnames: dbnamesRaw.split(",").map(function (name) { return name.trim(); }).filter(Boolean),
                });
                return [4 /*yield*/, askQuestion(rl, "Add another database entry? (y/N): ")];
            case 10:
                addAnother = (_h.sent()).toLowerCase();
                if (addAnother !== "y" && addAnother !== "yes") {
                    return [3 /*break*/, 11];
                }
                return [3 /*break*/, 2];
            case 11: return [2 /*return*/, {
                    databases: databases,
                }];
            case 12:
                rl.close();
                return [7 /*endfinally*/];
            case 13: return [2 /*return*/];
        }
    });
}); };
var isGeneratorConfigObject = function (value) {
    return !!value && typeof value === "object" && !Array.isArray(value);
};
var resolveLoadedConfig = function (loaded, configPath) { return __awaiter(void 0, void 0, void 0, function () {
    var evaluated, awaited;
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0:
                evaluated = typeof loaded === "function"
                    ? loaded()
                    : loaded;
                return [4 /*yield*/, Promise.resolve(evaluated)];
            case 1:
                awaited = _a.sent();
                if (!isGeneratorConfigObject(awaited)) {
                    throw new Error("Config at '".concat(configPath, "' must export an object, function, or async function that resolves to an object."));
                }
                return [2 /*return*/, awaited];
        }
    });
}); };
var loadConfigFromPath = function (configPath) { return __awaiter(void 0, void 0, void 0, function () {
    var ext, raw, typescript, source, transpiled, moduleRef, compiled;
    var _a;
    return __generator(this, function (_b) {
        switch (_b.label) {
            case 0:
                ext = path.extname(configPath).toLowerCase();
                if (!(ext === ".json")) return [3 /*break*/, 2];
                raw = fs.readFileSync(configPath, "utf-8");
                return [4 /*yield*/, resolveLoadedConfig(JSON.parse(raw), configPath)];
            case 1: return [2 /*return*/, _b.sent()];
            case 2:
                if (!(ext === ".ts")) return [3 /*break*/, 4];
                typescript = require("typescript");
                source = fs.readFileSync(configPath, "utf-8");
                transpiled = typescript.transpileModule(source, {
                    compilerOptions: {
                        module: typescript.ModuleKind.CommonJS,
                        target: typescript.ScriptTarget.ES2020,
                    },
                }).outputText;
                moduleRef = { exports: {} };
                compiled = new Function("require", "module", "exports", "__filename", "__dirname", transpiled);
                compiled(require, moduleRef, moduleRef.exports, configPath, path.dirname(configPath));
                return [4 /*yield*/, resolveLoadedConfig((_a = moduleRef.exports.default) !== null && _a !== void 0 ? _a : moduleRef.exports, configPath)];
            case 3: return [2 /*return*/, _b.sent()];
            case 4: throw new Error("Unsupported config extension '".concat(ext, "'. Use .json or .ts."));
        }
    });
}); };
var MySQLDump = /** @class */ (function () {
    function MySQLDump() {
    }
    MySQLDump.buildCNF = function (connection, cnfFile, cnfTag) {
        if (cnfFile === void 0) { cnfFile = ''; }
        if (cnfTag === void 0) { cnfTag = ''; }
        var cnf = [
            '[client]',
            "user = ".concat(connection.user),
            "password = ".concat(connection.pass),
            "host = ".concat(connection.host),
            "port = ".concat(connection.port),
            '',
        ];
        cnf.push("");
        if ('' === cnfFile) {
            var suffix = cnfTag ? ".".concat(sanitizeIdentifier(cnfTag)) : '';
            cnfFile = path.join(this.OUTPUT_DIR, "C6".concat(suffix, ".mysql.cnf"));
        }
        try {
            fs.writeFileSync(cnfFile, cnf.join('\n'));
            fs.chmodSync(cnfFile, 488);
            console.log("Successfully created C6.mysql.cnf file in (".concat(cnfFile, ")"));
        }
        catch (error) {
            console.error("Failed to store file contents of C6.mysql.cnf in (".concat(process.cwd(), ")"), error);
            process.exit(1);
        }
        this.mysqlcnf[cnfTag || "default"] = cnfFile;
        return cnfFile;
    };
    MySQLDump.MySQLDump = function (mysqldump, data, schemas, outputFile, otherOption, specificTable, databaseName, connection, cnfTag) {
        if (mysqldump === void 0) { mysqldump = 'mysqldump'; }
        if (data === void 0) { data = false; }
        if (schemas === void 0) { schemas = true; }
        if (outputFile === void 0) { outputFile = ''; }
        if (otherOption === void 0) { otherOption = ''; }
        if (specificTable === void 0) { specificTable = ''; }
        if (databaseName === void 0) { databaseName = this.DB_NAME; }
        if (connection === void 0) { connection = {
            user: this.DB_USER,
            pass: this.DB_PASS,
            host: this.DB_HOST,
            port: this.DB_PORT,
        }; }
        if (cnfTag === void 0) { cnfTag = ""; }
        if (outputFile === '') {
            outputFile = path.join(this.OUTPUT_DIR, 'C6.mysqldump.sql');
        }
        if (!data && !schemas) {
            console.warn("MysqlDump is running with --no-create-info and --no-data. Why?");
        }
        var defaultsExtraFile = this.buildCNF(connection, "", cnfTag);
        var hexBlobOption = data ? '--hex-blob ' : '--no-data ';
        var createInfoOption = schemas ? '' : ' --no-create-info ';
        var tempOutputFile = "".concat(outputFile, ".tmp");
        var cmd = "".concat(mysqldump, " --defaults-extra-file=\"").concat(defaultsExtraFile, "\" ").concat(otherOption, " --set-gtid-purged=\"OFF\" --skip-add-locks --lock-tables=false --single-transaction --quick ").concat(createInfoOption).concat(hexBlobOption).concat(databaseName, " ").concat(specificTable, " > '").concat(tempOutputFile, "'");
        var succeeded = this.executeAndCheckStatus(cmd, false);
        if (succeeded && fs.existsSync(tempOutputFile)) {
            fs.renameSync(tempOutputFile, outputFile);
        }
        else if (fs.existsSync(tempOutputFile)) {
            fs.unlinkSync(tempOutputFile);
        }
        if (!succeeded && fs.existsSync(outputFile)) {
            console.warn("[generateRestBindings] mysqldump for '".concat(databaseName, "' failed. Reusing existing dump file at ").concat(outputFile, "."));
        }
        if (!fs.existsSync(outputFile)) {
            console.warn("[generateRestBindings] mysqldump output not found at ".concat(outputFile, ". If running in CI/no-DB environment, ensure a prebuilt dump file exists at this path."));
        }
        return (this.mysqldump = outputFile);
    };
    MySQLDump.LoadInformationSchema = function (databaseName, connection, cnfTag) {
        var e_5, _a;
        if (databaseName === void 0) { databaseName = this.DB_NAME; }
        if (connection === void 0) { connection = {
            user: this.DB_USER,
            pass: this.DB_PASS,
            host: this.DB_HOST,
            port: this.DB_PORT,
        }; }
        if (cnfTag === void 0) { cnfTag = ""; }
        var defaultsExtraFile = this.buildCNF(connection, "", cnfTag);
        var escapedDatabaseName = databaseName.replace(/\\/g, "\\\\").replace(/'/g, "''");
        var query = "\n            SELECT\n                c.TABLE_NAME,\n                t.TABLE_TYPE,\n                c.COLUMN_NAME,\n                c.DATA_TYPE,\n                c.COLUMN_TYPE,\n                c.IS_NULLABLE,\n                c.EXTRA\n            FROM information_schema.COLUMNS c\n            JOIN information_schema.TABLES t\n                ON t.TABLE_SCHEMA = c.TABLE_SCHEMA\n                AND t.TABLE_NAME = c.TABLE_NAME\n            WHERE c.TABLE_SCHEMA = '".concat(escapedDatabaseName, "'\n            ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION\n        ");
        try {
            var stdout = execFileSync("mysql", [
                "--defaults-extra-file=".concat(defaultsExtraFile),
                "--batch",
                "--raw",
                "--skip-column-names",
                "-e",
                query,
            ], { encoding: "utf-8" });
            var schemaMetadata = {};
            try {
                for (var _b = __values(stdout.split(/\r?\n/)), _c = _b.next(); !_c.done; _c = _b.next()) {
                    var line = _c.value;
                    if (!line.trim())
                        continue;
                    var _d = __read(line.split("\t"), 7), tableName = _d[0], tableType = _d[1], columnName = _d[2], dataType = _d[3], columnType = _d[4], isNullable = _d[5], extra = _d[6];
                    if (!tableName || !columnName)
                        continue;
                    if (!schemaMetadata[tableName]) {
                        schemaMetadata[tableName] = {
                            TABLE_TYPE: tableType || "BASE TABLE",
                            COLUMNS: [],
                        };
                    }
                    schemaMetadata[tableName].COLUMNS.push({
                        TABLE_NAME: tableName,
                        TABLE_TYPE: tableType || "BASE TABLE",
                        COLUMN_NAME: columnName,
                        DATA_TYPE: dataType || "",
                        COLUMN_TYPE: columnType || dataType || "",
                        IS_NULLABLE: isNullable || "YES",
                        EXTRA: extra || "",
                    });
                }
            }
            catch (e_5_1) { e_5 = { error: e_5_1 }; }
            finally {
                try {
                    if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
                }
                finally { if (e_5) throw e_5.error; }
            }
            return schemaMetadata;
        }
        catch (error) {
            console.warn("[generateRestBindings] information_schema lookup for '".concat(databaseName, "' failed. Falling back to dump-derived metadata where possible."));
            return {};
        }
    };
    MySQLDump.executeAndCheckStatus = function (command, exitOnFailure, output) {
        if (exitOnFailure === void 0) { exitOnFailure = true; }
        if (output === void 0) { output = []; }
        try {
            var stdout = execSync(command, { encoding: 'utf-8' });
            output.push(stdout);
            return true;
        }
        catch (e) {
            console.log("Command output::", e);
            if (exitOnFailure) {
                process.exit(1);
            }
            return false;
        }
    };
    MySQLDump.mysqlcnf = {};
    MySQLDump.mysqldump = '';
    MySQLDump.DB_USER = argMap['--user'] || 'root';
    MySQLDump.DB_PASS = argMap['--pass'] || 'password';
    MySQLDump.DB_HOST = argMap['--host'] || '127.0.0.1';
    MySQLDump.DB_PORT = argMap['--port'] || '3306';
    MySQLDump.DB_NAME = argMap['--dbname'] || 'sakila';
    MySQLDump.DB_PREFIX = argMap['--prefix'] || '';
    MySQLDump.RELATIVE_OUTPUT_DIR = argMap['--output'] || '/src';
    MySQLDump.OUTPUT_DIR = path.isAbsolute(MySQLDump.RELATIVE_OUTPUT_DIR)
        ? MySQLDump.RELATIVE_OUTPUT_DIR
        : path.join(process.cwd(), MySQLDump.RELATIVE_OUTPUT_DIR);
    return MySQLDump;
}());
var PostgreSQLDump = /** @class */ (function () {
    function PostgreSQLDump() {
    }
    PostgreSQLDump.PgDump = function (pgDump, outputFile, databaseName, connection) {
        if (pgDump === void 0) { pgDump = 'pg_dump'; }
        if (outputFile === void 0) { outputFile = ''; }
        if (databaseName === void 0) { databaseName = MySQLDump.DB_NAME; }
        if (connection === void 0) { connection = {
            user: MySQLDump.DB_USER,
            pass: MySQLDump.DB_PASS,
            host: MySQLDump.DB_HOST,
            port: MySQLDump.DB_PORT,
        }; }
        if (outputFile === '') {
            outputFile = path.join(MySQLDump.OUTPUT_DIR, 'C6.pg_dump.sql');
        }
        var tempOutputFile = "".concat(outputFile, ".tmp");
        var env = __assign(__assign({}, process.env), { PGPASSWORD: connection.pass });
        try {
            var args_1 = [
                '--schema-only',
                '--no-owner',
                '--no-privileges',
                '--host', connection.host,
                '--port', connection.port,
                '--username', connection.user,
                '--dbname', databaseName,
                '--file', tempOutputFile,
            ];
            execFileSync(pgDump, args_1, { encoding: 'utf-8', env: env });
            if (fs.existsSync(tempOutputFile)) {
                fs.renameSync(tempOutputFile, outputFile);
            }
        }
        catch (error) {
            if (fs.existsSync(tempOutputFile)) {
                fs.unlinkSync(tempOutputFile);
            }
            if (fs.existsSync(outputFile)) {
                console.warn("[generateRestBindings] pg_dump for '".concat(databaseName, "' failed. Reusing existing dump file at ").concat(outputFile, "."));
            }
            else {
                console.warn("[generateRestBindings] pg_dump output not found at ".concat(outputFile, ". If running in CI/no-DB environment, ensure a prebuilt dump file exists at this path."));
            }
        }
        return outputFile;
    };
    PostgreSQLDump.LoadInformationSchema = function (databaseName, connection) {
        var e_6, _a;
        if (databaseName === void 0) { databaseName = MySQLDump.DB_NAME; }
        if (connection === void 0) { connection = {
            user: MySQLDump.DB_USER,
            pass: MySQLDump.DB_PASS,
            host: MySQLDump.DB_HOST,
            port: MySQLDump.DB_PORT,
        }; }
        var query = "\n            SELECT\n                c.table_name,\n                CASE WHEN t.table_type = 'VIEW' THEN 'VIEW' ELSE 'BASE TABLE' END AS table_type,\n                c.column_name,\n                c.data_type,\n                COALESCE(c.udt_name, c.data_type) AS column_type,\n                c.is_nullable,\n                CASE\n                    WHEN c.is_identity = 'YES' THEN 'auto_increment'\n                    WHEN c.column_default LIKE 'nextval(%' THEN 'auto_increment'\n                    ELSE ''\n                END AS extra\n            FROM information_schema.columns c\n            JOIN information_schema.tables t\n                ON t.table_schema = c.table_schema\n                AND t.table_name = c.table_name\n            WHERE c.table_schema = 'public'\n            ORDER BY c.table_name, c.ordinal_position\n        ";
        try {
            var stdout = execFileSync('psql', [
                '--host', connection.host,
                '--port', connection.port,
                '--username', connection.user,
                '--dbname', databaseName,
                '--tuples-only',
                '--no-align',
                '--field-separator', '\t',
                '--command', query,
            ], {
                encoding: 'utf-8',
                env: __assign(__assign({}, process.env), { PGPASSWORD: connection.pass }),
            });
            var schemaMetadata = {};
            try {
                for (var _b = __values(stdout.split(/\r?\n/)), _c = _b.next(); !_c.done; _c = _b.next()) {
                    var line = _c.value;
                    if (!line.trim())
                        continue;
                    var _d = __read(line.split('\t'), 7), tableName = _d[0], tableType = _d[1], columnName = _d[2], dataType = _d[3], columnType = _d[4], isNullable = _d[5], extra = _d[6];
                    if (!tableName || !columnName)
                        continue;
                    if (!schemaMetadata[tableName]) {
                        schemaMetadata[tableName] = {
                            TABLE_TYPE: tableType || 'BASE TABLE',
                            COLUMNS: [],
                        };
                    }
                    schemaMetadata[tableName].COLUMNS.push({
                        TABLE_NAME: tableName,
                        TABLE_TYPE: tableType || 'BASE TABLE',
                        COLUMN_NAME: columnName,
                        DATA_TYPE: dataType || '',
                        COLUMN_TYPE: columnType || dataType || '',
                        IS_NULLABLE: isNullable || 'YES',
                        EXTRA: extra || '',
                    });
                }
            }
            catch (e_6_1) { e_6 = { error: e_6_1 }; }
            finally {
                try {
                    if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
                }
                finally { if (e_6) throw e_6.error; }
            }
            return schemaMetadata;
        }
        catch (error) {
            console.warn("[generateRestBindings] PostgreSQL information_schema lookup for '".concat(databaseName, "' failed. Falling back to dump-derived metadata where possible."));
            return {};
        }
    };
    return PostgreSQLDump;
}());
var pathRuntimeReference = '';
function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}
function determineTypeScriptType(mysqlType, enumValues) {
    var baseType = mysqlType.toLowerCase().replace(/\(.+?\)/, '').split(' ')[0];
    if (baseType === 'enum' && Array.isArray(enumValues)) {
        return enumValues.map(function (val) { return "'".concat(val, "'"); }).join(' | ');
    }
    switch (mysqlType) {
        case 'enum':
            throw Error('An unexpected error occurred. Please report this issue to the maintainers of this script.');
        // Date & Time
        case 'time':
        case 'year':
        case 'date':
        case 'datetime':
        case 'timestamp':
            return 'Date | number | string';
        // String & Temporal
        case 'char':
        case 'varchar':
        case 'character':
        case 'text':
        case 'tinytext':
        case 'mediumtext':
        case 'longtext':
        case 'set':
        case 'uuid':
            return 'string';
        // Numeric
        case 'tinyint':
        case 'smallint':
        case 'mediumint':
        case 'int':
        case 'integer':
        case 'serial':
        case 'bigserial':
        case 'bigint':
        case 'decimal':
        case 'dec':
        case 'numeric':
        case 'float':
        case 'double':
        case 'real':
            return 'number';
        // Boolean
        case 'boolean':
        case 'bool':
            return 'boolean';
        // JSON
        case 'json':
        case 'jsonb':
            return 'any';
        // GeoJSON
        case 'geometry':
            return 'GeoJSON.Geometry';
        case 'point':
            return '{ x: number; y: number }';
        case 'linestring':
            return 'GeoJSON.LineString';
        case 'polygon':
            return 'GeoJSON.Polygon';
        case 'multipoint':
            return 'GeoJSON.MultiPoint';
        case 'multilinestring':
            return 'GeoJSON.MultiLineString';
        case 'multipolygon':
            return 'GeoJSON.MultiPolygon';
        case 'geometrycollection':
            return 'GeoJSON.GeometryCollection';
        // Binary
        case 'binary':
        case 'varbinary':
        case 'blob':
        case 'tinyblob':
        case 'mediumblob':
        case 'longblob':
            return 'Buffer | string'; // todo - we should pass driver specific types here
        default:
            return 'string';
    }
}
var parseSQLToTypeScript = function (sql, schemaMetadata) {
    var e_7, _a, e_8, _b, e_9, _c, e_10, _d;
    var _e, _f, _g, _h;
    if (schemaMetadata === void 0) { schemaMetadata = {}; }
    var tableMatches = sql.matchAll(/CREATE\s+TABLE\s+`?(\w+)`?\s+\(((.|\n)+?)\)\s*(ENGINE=.+?);/gm);
    var tableData = {};
    var references = [];
    var normalizeSqlIdentifier = function (identifier) {
        return identifier.trim().replace(/^["`]|["`]$/g, '');
    };
    var normalizeQualifiedSqlIdentifier = function (identifier) {
        var parts = identifier
            .split('.')
            .map(function (part) { return normalizeSqlIdentifier(part); })
            .filter(Boolean);
        return parts[parts.length - 1] || normalizeSqlIdentifier(identifier);
    };
    var normalizeSqlIdentifierList = function (identifierList) {
        return identifierList
            .split(',')
            .map(function (identifier) { return normalizeQualifiedSqlIdentifier(identifier.trim()); })
            .filter(Boolean);
    };
    var splitSqlDefinitionList = function (definitionList) {
        var definitions = [];
        var current = '';
        var depth = 0;
        var quote = null;
        for (var i = 0; i < definitionList.length; i++) {
            var char = definitionList[i];
            var previous = definitionList[i - 1];
            if ((char === "'" || char === '"') && previous !== '\\') {
                quote = quote === char ? null : quote !== null && quote !== void 0 ? quote : char;
            }
            if (!quote) {
                if (char === '(')
                    depth++;
                if (char === ')')
                    depth = Math.max(0, depth - 1);
                if (char === ',' && depth === 0) {
                    if (current.trim())
                        definitions.push(current.trim());
                    current = '';
                    continue;
                }
            }
            current += char;
        }
        if (current.trim())
            definitions.push(current.trim());
        return definitions;
    };
    var normalizePostgresColumnType = function (rawType) {
        var cleanedType = rawType
            .replace(/\s+COLLATE\s+("[^"]+"|\S+)/i, '')
            .trim();
        var lengthMatch = cleanedType.match(/\(([^)]+)\)/);
        var length = lengthMatch ? lengthMatch[1] : '';
        var withoutLength = cleanedType.replace(/\(.+?\)/, '').toLowerCase().replace(/\s+/g, ' ').trim();
        if (withoutLength === 'character varying')
            return { type: 'varchar', length: length };
        if (withoutLength === 'character')
            return { type: 'char', length: length };
        if (withoutLength === 'timestamp without time zone' || withoutLength === 'timestamp with time zone')
            return { type: 'timestamp', length: length };
        if (withoutLength === 'time without time zone' || withoutLength === 'time with time zone')
            return { type: 'time', length: length };
        if (withoutLength === 'double precision')
            return { type: 'double', length: length };
        if (withoutLength === 'integer')
            return { type: 'int', length: length };
        return {
            type: withoutLength.split(' ')[0] || 'text',
            length: length,
        };
    };
    var postgresPrimaryKeysByTable = new Map();
    var rememberPostgresPrimaryKeys = function (tableName, columns) {
        if (columns.length > 0) {
            postgresPrimaryKeysByTable.set(tableName, columns);
        }
    };
    var postgresIdentityColumns = new Set();
    var postgresAlterIdentityRegex = /ALTER\s+TABLE\s+(?:ONLY\s+)?((?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)(?:\.(?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*))?)\s+ALTER\s+COLUMN\s+("[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)\s+ADD\s+GENERATED\s+(?:ALWAYS|BY\s+DEFAULT)\s+AS\s+IDENTITY/gim;
    var postgresAlterIdentityMatch;
    while ((postgresAlterIdentityMatch = postgresAlterIdentityRegex.exec(sql))) {
        postgresIdentityColumns.add("".concat(normalizeQualifiedSqlIdentifier(postgresAlterIdentityMatch[1]), ".").concat(normalizeSqlIdentifier(postgresAlterIdentityMatch[2])));
    }
    var postgresAlterPrimaryKeyRegex = /ALTER\s+TABLE\s+(?:ONLY\s+)?((?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)(?:\.(?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*))?)\s+ADD\s+CONSTRAINT\s+(?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)\s+PRIMARY\s+KEY\s*\(([^)]+)\)/gim;
    var postgresAlterPrimaryKeyMatch;
    while ((postgresAlterPrimaryKeyMatch = postgresAlterPrimaryKeyRegex.exec(sql))) {
        rememberPostgresPrimaryKeys(normalizeQualifiedSqlIdentifier(postgresAlterPrimaryKeyMatch[1]), normalizeSqlIdentifierList(postgresAlterPrimaryKeyMatch[2]));
    }
    var postgresAlterForeignKeyRegex = /ALTER\s+TABLE\s+(?:ONLY\s+)?((?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)(?:\.(?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*))?)\s+ADD\s+CONSTRAINT\s+("[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)\s+FOREIGN\s+KEY\s*\(([^)]+)\)\s+REFERENCES\s+((?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)(?:\.(?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*))?)\s*\(([^)]+)\)(?:\s+ON\s+DELETE\s+([A-Z ]+?))?(?:\s+ON\s+UPDATE\s+([A-Z ]+?))?(?:;|\s+NOT\s+VALID|\s*$)/gim;
    var postgresAlterForeignKeyMatch;
    var _loop_1 = function () {
        var localColumns = normalizeSqlIdentifierList(postgresAlterForeignKeyMatch[3]);
        var foreignColumns = normalizeSqlIdentifierList(postgresAlterForeignKeyMatch[5]);
        localColumns.forEach(function (localColumn, index) {
            var _a, _b, _c;
            references.push({
                TABLE: normalizeQualifiedSqlIdentifier(postgresAlterForeignKeyMatch[1]),
                CONSTRAINT: normalizeSqlIdentifier(postgresAlterForeignKeyMatch[2]),
                FOREIGN_KEY: localColumn,
                REFERENCES: "".concat(normalizeQualifiedSqlIdentifier(postgresAlterForeignKeyMatch[4]), ".").concat((_a = foreignColumns[index]) !== null && _a !== void 0 ? _a : foreignColumns[0]),
                ON_DELETE: ((_b = postgresAlterForeignKeyMatch[6]) === null || _b === void 0 ? void 0 : _b.trim()) || '',
                ON_UPDATE: ((_c = postgresAlterForeignKeyMatch[7]) === null || _c === void 0 ? void 0 : _c.trim()) || '',
            });
        });
    };
    while ((postgresAlterForeignKeyMatch = postgresAlterForeignKeyRegex.exec(sql))) {
        _loop_1();
    }
    var columnInfoFromMetadata = function (column) {
        var fullType = (column.COLUMN_TYPE || column.DATA_TYPE || 'text').trim();
        var enumMatch = /^enum\((.+)\)$/i.exec(fullType);
        var enumValues = enumMatch
            ? enumMatch[1]
                .split(/,(?=(?:[^']*'[^']*')*[^']*$)/)
                .map(function (s) { return s.trim().replace(/^'(.*)'$/, '$1'); })
            : null;
        var type = fullType.replace(/\(.+?\)/, '').split(' ')[0].toLowerCase();
        var lengthMatch = fullType.match(/\(([^)]+)\)/);
        var length = lengthMatch ? lengthMatch[1] : '';
        return {
            type: type,
            length: length,
            srid: null,
            enumValues: enumValues,
            notNull: column.IS_NULLABLE.toUpperCase() === 'NO',
            autoIncrement: column.EXTRA.toLowerCase().includes('auto_increment'),
            defaultValue: '',
        };
    };
    var fallbackColumnInfo = function () { return ({
        type: 'text',
        length: '',
        srid: null,
        enumValues: null,
        notNull: false,
        autoIncrement: false,
        defaultValue: '',
    }); };
    var _loop_2 = function (tableMatch) {
        var _j;
        var tableName = tableMatch[1];
        var columnDefinitions = tableMatch[2];
        var columns = {};
        // Improved regular expression to match column definitions
        var columnRegex = /^\s*`([^`]+)`\s+((?:enum|set)\((?:'(?:[^']|\\')*'(?:,\s*'(?:[^']|\\')*')*)\)|[a-zA-Z0-9_]+(?:\s+unsigned)?(?:\(\d+(?:,\d+)?\))?)\s*(NOT NULL|NULL)?\s*(DEFAULT\s+(?:'[^']*'|[^\s,]+))?\s*(AUTO_INCREMENT)?/i;
        var columnDefinitionsLines = columnDefinitions.split('\n');
        columnDefinitionsLines.forEach(function (line) {
            if (!line.match(/(PRIMARY KEY|UNIQUE KEY|CONSTRAINT)/)) {
                var match = columnRegex.exec(line.trim());
                if (match) {
                    var _a = __read(match, 6), name_1 = _a[1], fullTypeRaw = _a[2], nullability = _a[3], defaultRaw = _a[4], autoInc = _a[5];
                    var fullType = fullTypeRaw.trim();
                    var enumMatch = /^enum\((.+)\)$/i.exec(fullType);
                    var enumValues = enumMatch
                        ? enumMatch[1]
                            .split(/,(?=(?:[^']*'[^']*')*[^']*$)/) // split only top-level commas
                            .map(function (s) { return s.trim().replace(/^'(.*)'$/, '$1'); })
                        : null;
                    var type = fullType.replace(/\(.+?\)/, '').split(' ')[0].toLowerCase();
                    var lengthMatch = fullType.match(/\(([^)]+)\)/);
                    var length_1 = lengthMatch ? lengthMatch[1] : '';
                    var sridMatch = line.match(/SRID\s+(\d+)/i);
                    var srid = sridMatch ? parseInt(sridMatch[1], 10) : null;
                    columns[name_1] = {
                        type: type,
                        length: length_1,
                        srid: srid,
                        enumValues: enumValues,
                        notNull: (nullability === null || nullability === void 0 ? void 0 : nullability.toUpperCase()) === 'NOT NULL',
                        autoIncrement: !!autoInc,
                        defaultValue: defaultRaw ? defaultRaw.replace(/^DEFAULT\s+/i, '') : ''
                    };
                }
            }
        });
        // Extract primary keys
        var primaryKeyMatch = columnDefinitions.match(/PRIMARY KEY \(([^)]+)\)/i);
        var primaryKeys = primaryKeyMatch
            ? primaryKeyMatch[1].split(',').map(function (key) { return key.trim().replace(/`/g, ''); })
            : [];
        var primaryKeysType = primaryKeys.length > 0
            ? primaryKeys.map(function (pk) { return "'".concat(pk, "'"); }).join(' | ')
            : 'never';
        // Extract foreign keys
        var foreignKeyRegex = /CONSTRAINT `([^`]+)` FOREIGN KEY \(`([^`]+)`\) REFERENCES `([^`]+)` \(`([^`]+)`\)( ON DELETE (\w+))?( ON UPDATE (\w+))?/g;
        var foreignKeyMatch = void 0;
        while ((foreignKeyMatch = foreignKeyRegex.exec(columnDefinitions))) {
            var constraintName = foreignKeyMatch[1];
            var localColumn = foreignKeyMatch[2];
            var foreignTable = foreignKeyMatch[3];
            var foreignColumn = foreignKeyMatch[4];
            var onDeleteAction = foreignKeyMatch[6] || '';
            var onUpdateAction = foreignKeyMatch[8] || '';
            references.push({
                TABLE: tableName,
                CONSTRAINT: constraintName,
                FOREIGN_KEY: localColumn,
                REFERENCES: "".concat(foreignTable, ".").concat(foreignColumn),
                ON_DELETE: onDeleteAction,
                ON_UPDATE: onUpdateAction
            });
        }
        var REACT_IMPORT = false, CARBON_REACT_INSTANCE = false;
        if (argMap['--react']) {
            var reactArgSplit = argMap['--react'].split(';');
            if (reactArgSplit.length !== 2) {
                console.error("React requires two arguments, the import and the carbon react instance statement. Example: --react 'import CustomCarbonReactApplication from \"src/CustomCarbonReactApplication.tsx\"; CustomCarbonReactApplication.instance'");
                process.exit(1);
            }
            _j = __read(reactArgSplit, 2), REACT_IMPORT = _j[0], CARBON_REACT_INSTANCE = _j[1];
        }
        var tsModel = {
            RELATIVE_OUTPUT_DIR: pathRuntimeReference,
            TABLE_NAME: tableName,
            RELATION_TYPE: 'TABLE',
            READ_ONLY: false,
            TABLE_DEFINITION: tableMatch[0].replace(/\/\*!([0-9]{5}) ([^*]+)\*\//g, function (_match, _version, body) {
                return "/!* ".concat(body.trim(), " *!/");
            }),
            TABLE_CONSTRAINT: references,
            REST_URL_EXPRESSION: argMap['--restUrlExpression'] || '"/rest/"',
            TABLE_NAME_SHORT: tableName.replace(MySQLDump.DB_PREFIX, ''),
            TABLE_NAME_LOWER: tableName.toLowerCase(),
            TABLE_NAME_UPPER: tableName.toUpperCase(),
            TABLE_NAME_PASCAL_CASE: tableName.split('_').map(capitalizeFirstLetter).join('_'),
            TABLE_NAME_SHORT_PASCAL_CASE: tableName.replace(MySQLDump.DB_PREFIX, '').split('_').map(capitalizeFirstLetter).join('_'),
            PRIMARY: primaryKeys.map(function (pk) { return "".concat(tableName, ".").concat(pk); }),
            PRIMARY_SHORT: primaryKeys,
            PRIMARY_KEYS_TYPE: primaryKeysType,
            COLUMNS: {},
            COLUMNS_UPPERCASE: {},
            TYPE_VALIDATION: {},
            REGEX_VALIDATION: {},
            TABLE_REFERENCES: {},
            TABLE_REFERENCED_BY: {},
            HAS_GEOJSON_TYPES: false,
            REACT_IMPORT: REACT_IMPORT,
            CARBON_REACT_INSTANCE: CARBON_REACT_INSTANCE,
        };
        for (var colName in columns) {
            tsModel.COLUMNS["".concat(tableName, ".").concat(colName)] = colName;
            tsModel.COLUMNS_UPPERCASE[toConstantIdentifier(colName)] = tableName + '.' + colName;
            var typescript_type = determineTypeScriptType(columns[colName].type.toLowerCase(), columns[colName].enumValues);
            if (typescript_type.includes('GeoJSON.')) {
                tsModel.HAS_GEOJSON_TYPES = true;
            }
            tsModel.TYPE_VALIDATION["".concat(tableName, ".").concat(colName)] = {
                COLUMN_NAME: colName,
                MYSQL_TYPE: columns[colName].type.toLowerCase(),
                TYPESCRIPT_TYPE: typescript_type,
                TYPESCRIPT_TYPE_IS_STRING: typescript_type === 'string' || typescript_type.includes("'"),
                TYPESCRIPT_TYPE_IS_NUMBER: 'number' === typescript_type,
                MAX_LENGTH: columns[colName].length,
                AUTO_INCREMENT: columns[colName].autoIncrement,
                NOT_NULL: columns[colName].notNull,
                SKIP_COLUMN_IN_POST: !columns[colName].notNull && !columns[colName].defaultValue,
            };
        }
        tableData[tableName] = tsModel;
    };
    try {
        // @ts-ignore
        for (var tableMatches_1 = __values(tableMatches), tableMatches_1_1 = tableMatches_1.next(); !tableMatches_1_1.done; tableMatches_1_1 = tableMatches_1.next()) {
            var tableMatch = tableMatches_1_1.value;
            _loop_2(tableMatch);
        }
    }
    catch (e_7_1) { e_7 = { error: e_7_1 }; }
    finally {
        try {
            if (tableMatches_1_1 && !tableMatches_1_1.done && (_a = tableMatches_1.return)) _a.call(tableMatches_1);
        }
        finally { if (e_7) throw e_7.error; }
    }
    var postgresTableMatches = sql.matchAll(/CREATE\s+TABLE\s+((?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)(?:\.(?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*))?)\s*\(([\s\S]*?)\);/gim);
    var _loop_3 = function (tableMatch) {
        var e_11, _k, _l;
        var tableName = normalizeQualifiedSqlIdentifier(tableMatch[1]);
        if (tableData[tableName])
            return "continue";
        var columnDefinitions = tableMatch[2];
        var columns = {};
        try {
            for (var _m = (e_11 = void 0, __values(splitSqlDefinitionList(columnDefinitions))), _o = _m.next(); !_o.done; _o = _m.next()) {
                var line = _o.value;
                if (/^(CONSTRAINT|PRIMARY\s+KEY|FOREIGN\s+KEY|UNIQUE|CHECK|EXCLUDE)\b/i.test(line)) {
                    var inlinePrimaryKeyMatch = line.match(/PRIMARY\s+KEY\s*\(([^)]+)\)/i);
                    if (inlinePrimaryKeyMatch) {
                        rememberPostgresPrimaryKeys(tableName, normalizeSqlIdentifierList(inlinePrimaryKeyMatch[1]));
                    }
                    continue;
                }
                var match = line.match(/^\s*("[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)\s+([\s\S]+?)\s*$/);
                if (!match)
                    continue;
                var name_2 = normalizeSqlIdentifier(match[1]);
                var rest = match[2].replace(/,$/, '').trim();
                var constraintMatch = rest.match(/\s+(?:CONSTRAINT\s+\S+\s+)?(?:NOT\s+NULL|NULL|DEFAULT\b|PRIMARY\s+KEY\b|REFERENCES\b|CHECK\b|GENERATED\b|COLLATE\b)/i);
                var fullTypeRaw = (constraintMatch ? rest.slice(0, constraintMatch.index) : rest).trim();
                var _p = normalizePostgresColumnType(fullTypeRaw), type = _p.type, length_2 = _p.length;
                var defaultMatch = rest.match(/\bDEFAULT\s+((?:'[^']*')|(?:"[^"]*")|[^,\s]+(?:\([^)]*\))?)/i);
                columns[name_2] = {
                    type: type,
                    length: length_2,
                    srid: null,
                    enumValues: null,
                    notNull: /\bNOT\s+NULL\b/i.test(rest) || /\bPRIMARY\s+KEY\b/i.test(rest),
                    autoIncrement: /^(?:serial|bigserial|smallserial)$/i.test(type)
                        || /\bnextval\s*\(/i.test(rest)
                        || /\bGENERATED\s+(?:ALWAYS|BY\s+DEFAULT)\s+AS\s+IDENTITY\b/i.test(rest)
                        || postgresIdentityColumns.has("".concat(tableName, ".").concat(name_2)),
                    defaultValue: defaultMatch ? defaultMatch[1] : '',
                };
                if (/\bPRIMARY\s+KEY\b/i.test(rest)) {
                    rememberPostgresPrimaryKeys(tableName, [name_2]);
                }
            }
        }
        catch (e_11_1) { e_11 = { error: e_11_1 }; }
        finally {
            try {
                if (_o && !_o.done && (_k = _m.return)) _k.call(_m);
            }
            finally { if (e_11) throw e_11.error; }
        }
        var primaryKeys = (_e = postgresPrimaryKeysByTable.get(tableName)) !== null && _e !== void 0 ? _e : [];
        var primaryKeysType = primaryKeys.length > 0
            ? primaryKeys.map(function (pk) { return "'".concat(pk, "'"); }).join(' | ')
            : 'never';
        var REACT_IMPORT = false, CARBON_REACT_INSTANCE = false;
        if (argMap['--react']) {
            var reactArgSplit = argMap['--react'].split(';');
            if (reactArgSplit.length !== 2) {
                console.error("React requires two arguments, the import and the carbon react instance statement. Example: --react 'import CustomCarbonReactApplication from \"src/CustomCarbonReactApplication.tsx\"; CustomCarbonReactApplication.instance'");
                process.exit(1);
            }
            _l = __read(reactArgSplit, 2), REACT_IMPORT = _l[0], CARBON_REACT_INSTANCE = _l[1];
        }
        var tsModel = {
            RELATIVE_OUTPUT_DIR: pathRuntimeReference,
            TABLE_NAME: tableName,
            RELATION_TYPE: 'TABLE',
            READ_ONLY: false,
            TABLE_DEFINITION: tableMatch[0].replace(/\*\//g, '* /'),
            TABLE_CONSTRAINT: references,
            REST_URL_EXPRESSION: argMap['--restUrlExpression'] || '"/rest/"',
            TABLE_NAME_SHORT: tableName.replace(MySQLDump.DB_PREFIX, ''),
            TABLE_NAME_LOWER: tableName.toLowerCase(),
            TABLE_NAME_UPPER: tableName.toUpperCase(),
            TABLE_NAME_PASCAL_CASE: tableName.split('_').map(capitalizeFirstLetter).join('_'),
            TABLE_NAME_SHORT_PASCAL_CASE: tableName.replace(MySQLDump.DB_PREFIX, '').split('_').map(capitalizeFirstLetter).join('_'),
            PRIMARY: primaryKeys.map(function (pk) { return "".concat(tableName, ".").concat(pk); }),
            PRIMARY_SHORT: primaryKeys,
            PRIMARY_KEYS_TYPE: primaryKeysType,
            COLUMNS: {},
            COLUMNS_UPPERCASE: {},
            TYPE_VALIDATION: {},
            REGEX_VALIDATION: {},
            TABLE_REFERENCES: {},
            TABLE_REFERENCED_BY: {},
            HAS_GEOJSON_TYPES: false,
            REACT_IMPORT: REACT_IMPORT,
            CARBON_REACT_INSTANCE: CARBON_REACT_INSTANCE,
        };
        for (var colName in columns) {
            tsModel.COLUMNS["".concat(tableName, ".").concat(colName)] = colName;
            tsModel.COLUMNS_UPPERCASE[toConstantIdentifier(colName)] = tableName + '.' + colName;
            var typescript_type = determineTypeScriptType(columns[colName].type.toLowerCase(), columns[colName].enumValues);
            if (typescript_type.includes('GeoJSON.')) {
                tsModel.HAS_GEOJSON_TYPES = true;
            }
            tsModel.TYPE_VALIDATION["".concat(tableName, ".").concat(colName)] = {
                COLUMN_NAME: colName,
                MYSQL_TYPE: columns[colName].type.toLowerCase(),
                TYPESCRIPT_TYPE: typescript_type,
                TYPESCRIPT_TYPE_IS_STRING: typescript_type === 'string' || typescript_type.includes("'"),
                TYPESCRIPT_TYPE_IS_NUMBER: 'number' === typescript_type,
                MAX_LENGTH: columns[colName].length,
                AUTO_INCREMENT: columns[colName].autoIncrement,
                NOT_NULL: columns[colName].notNull,
                SKIP_COLUMN_IN_POST: !columns[colName].notNull && !columns[colName].defaultValue,
            };
        }
        tableData[tableName] = tsModel;
    };
    try {
        for (var postgresTableMatches_1 = __values(postgresTableMatches), postgresTableMatches_1_1 = postgresTableMatches_1.next(); !postgresTableMatches_1_1.done; postgresTableMatches_1_1 = postgresTableMatches_1.next()) {
            var tableMatch = postgresTableMatches_1_1.value;
            _loop_3(tableMatch);
        }
    }
    catch (e_8_1) { e_8 = { error: e_8_1 }; }
    finally {
        try {
            if (postgresTableMatches_1_1 && !postgresTableMatches_1_1.done && (_b = postgresTableMatches_1.return)) _b.call(postgresTableMatches_1);
        }
        finally { if (e_8) throw e_8.error; }
    }
    var viewDefinitions = new Map();
    var viewColumnNamesFromDump = new Map();
    var tempViewRegex = /\/\*!50001\s+CREATE\s+VIEW\s+(?:`[^`]+`\.)?`?(\w+)`?\s+AS\s+SELECT\s+([\s\S]*?)\*\/;/gim;
    var finalViewRegex = /\/\*!50001\s+VIEW\s+(?:`[^`]+`\.)?`?(\w+)`?\s+AS\s+([\s\S]*?)\s*\*\/;/gim;
    var tempViewMatch;
    while ((tempViewMatch = tempViewRegex.exec(sql))) {
        var viewName = tempViewMatch[1];
        var selectDefinition = tempViewMatch[2];
        var aliases = Array.from(selectDefinition.matchAll(/\bAS\s+`([^`]+)`/gi))
            .map(function (match) { return match[1]; })
            .filter(Boolean);
        if (!viewColumnNamesFromDump.has(viewName)) {
            viewColumnNamesFromDump.set(viewName, aliases);
        }
    }
    var finalViewMatch;
    while ((finalViewMatch = finalViewRegex.exec(sql))) {
        var viewName = finalViewMatch[1];
        var selectDefinition = finalViewMatch[2].trim().replace(/;\s*$/, '');
        viewDefinitions.set(viewName, "CREATE VIEW `".concat(viewName, "` AS ").concat(selectDefinition, ";"));
    }
    var viewNames = new Set(__spreadArray(__spreadArray(__spreadArray([], __read(Object.entries(schemaMetadata)
        .filter(function (_a) {
        var _b = __read(_a, 2), metadata = _b[1];
        return metadata.TABLE_TYPE.toUpperCase().includes('VIEW');
    })
        .map(function (_a) {
        var _b = __read(_a, 1), tableName = _b[0];
        return tableName;
    })), false), __read(viewColumnNamesFromDump.keys()), false), __read(viewDefinitions.keys()), false));
    try {
        for (var viewNames_1 = __values(viewNames), viewNames_1_1 = viewNames_1.next(); !viewNames_1_1.done; viewNames_1_1 = viewNames_1.next()) {
            var viewName = viewNames_1_1.value;
            if (tableData[viewName])
                continue;
            var metadataColumns = (_g = (_f = schemaMetadata[viewName]) === null || _f === void 0 ? void 0 : _f.COLUMNS) !== null && _g !== void 0 ? _g : [];
            var dumpColumnNames = (_h = viewColumnNamesFromDump.get(viewName)) !== null && _h !== void 0 ? _h : [];
            var columns = metadataColumns.length > 0
                ? Object.fromEntries(metadataColumns.map(function (column) { return [
                    column.COLUMN_NAME,
                    columnInfoFromMetadata(column),
                ]; }))
                : Object.fromEntries(dumpColumnNames.map(function (columnName) { return [
                    columnName,
                    fallbackColumnInfo(),
                ]; }));
            if (Object.keys(columns).length === 0) {
                continue;
            }
            var tsModel = {
                RELATIVE_OUTPUT_DIR: pathRuntimeReference,
                TABLE_NAME: viewName,
                RELATION_TYPE: 'VIEW',
                READ_ONLY: true,
                TABLE_DEFINITION: (viewDefinitions.get(viewName) || "VIEW ".concat(viewName)).replace(/\*\//g, '* /'),
                TABLE_CONSTRAINT: {},
                REST_URL_EXPRESSION: argMap['--restUrlExpression'] || '"/rest/"',
                TABLE_NAME_SHORT: viewName.replace(MySQLDump.DB_PREFIX, ''),
                TABLE_NAME_LOWER: viewName.toLowerCase(),
                TABLE_NAME_UPPER: viewName.toUpperCase(),
                TABLE_NAME_PASCAL_CASE: viewName.split('_').map(capitalizeFirstLetter).join('_'),
                TABLE_NAME_SHORT_PASCAL_CASE: viewName.replace(MySQLDump.DB_PREFIX, '').split('_').map(capitalizeFirstLetter).join('_'),
                PRIMARY: [],
                PRIMARY_SHORT: [],
                PRIMARY_KEYS_TYPE: 'never',
                COLUMNS: {},
                COLUMNS_UPPERCASE: {},
                TYPE_VALIDATION: {},
                REGEX_VALIDATION: {},
                TABLE_REFERENCES: {},
                TABLE_REFERENCED_BY: {},
                HAS_GEOJSON_TYPES: false,
                REACT_IMPORT: false,
                CARBON_REACT_INSTANCE: false,
            };
            for (var colName in columns) {
                tsModel.COLUMNS["".concat(viewName, ".").concat(colName)] = colName;
                tsModel.COLUMNS_UPPERCASE[toConstantIdentifier(colName)] = viewName + '.' + colName;
                var typescript_type = determineTypeScriptType(columns[colName].type.toLowerCase(), columns[colName].enumValues);
                if (typescript_type.includes('GeoJSON.')) {
                    tsModel.HAS_GEOJSON_TYPES = true;
                }
                tsModel.TYPE_VALIDATION["".concat(viewName, ".").concat(colName)] = {
                    COLUMN_NAME: colName,
                    MYSQL_TYPE: columns[colName].type.toLowerCase(),
                    TYPESCRIPT_TYPE: typescript_type,
                    TYPESCRIPT_TYPE_IS_STRING: typescript_type === 'string' || typescript_type.includes("'"),
                    TYPESCRIPT_TYPE_IS_NUMBER: 'number' === typescript_type,
                    MAX_LENGTH: columns[colName].length,
                    AUTO_INCREMENT: false,
                    NOT_NULL: columns[colName].notNull,
                    SKIP_COLUMN_IN_POST: true,
                };
            }
            tableData[viewName] = tsModel;
        }
    }
    catch (e_9_1) { e_9 = { error: e_9_1 }; }
    finally {
        try {
            if (viewNames_1_1 && !viewNames_1_1.done && (_c = viewNames_1.return)) _c.call(viewNames_1);
        }
        finally { if (e_9) throw e_9.error; }
    }
    try {
        for (var references_1 = __values(references), references_1_1 = references_1.next(); !references_1_1.done; references_1_1 = references_1.next()) {
            var ref = references_1_1.value;
            var foreignTable = ref.REFERENCES.split('.')[0];
            var foreignColumn = ref.REFERENCES.split('.')[1];
            var tableName = ref.TABLE;
            var columnName = ref.FOREIGN_KEY;
            var constraintName = ref.CONSTRAINT;
            if (!tableData[foreignTable]) {
                console.log("Foreign table ".concat(foreignTable, " not found for ").concat(ref.TABLE, ".").concat(ref.CONSTRAINT));
                continue;
            }
            if (!('TABLE_REFERENCED_BY' in tableData[foreignTable])) {
                tableData[foreignTable].TABLE_REFERENCED_BY = {};
            }
            // @ts-ignore
            if (!tableData[foreignTable].TABLE_REFERENCED_BY[foreignColumn]) {
                // @ts-ignore
                tableData[foreignTable].TABLE_REFERENCED_BY[foreignColumn] = [];
            }
            // @ts-ignore
            tableData[foreignTable].TABLE_REFERENCED_BY[foreignColumn].push({
                TABLE: tableName,
                COLUMN: columnName,
                CONSTRAINT: constraintName
            });
            if (!tableData[tableName].TABLE_REFERENCES) {
                tableData[tableName].TABLE_REFERENCES = {};
            }
            // @ts-ignore
            if (!tableData[tableName].TABLE_REFERENCES[columnName]) {
                // @ts-ignore
                tableData[tableName].TABLE_REFERENCES[columnName] = [];
            }
            // @ts-ignore
            tableData[tableName].TABLE_REFERENCES[columnName].push({
                TABLE: foreignTable,
                COLUMN: foreignColumn,
                CONSTRAINT: constraintName
            });
        }
    }
    catch (e_10_1) { e_10 = { error: e_10_1 }; }
    finally {
        try {
            if (references_1_1 && !references_1_1.done && (_d = references_1.return)) _d.call(references_1);
        }
        finally { if (e_10) throw e_10.error; }
    }
    var relations = Object.values(tableData);
    var tables = relations.filter(function (table) { return table.RELATION_TYPE !== 'VIEW'; });
    var views = relations.filter(function (table) { return table.RELATION_TYPE === 'VIEW'; });
    return {
        C6VERSION: package_json_1.version,
        PREFIX: MySQLDump.DB_PREFIX,
        OBJECT_OVERRIDES: argMap['--objectOverrides'] || '',
        INTERFACE_OVERRIDES: argMap['--interfaceOverrides'] || '',
        CUSTOM_IMPORTS: argMap['--customImports'] || '',
        REST_URL_EXPRESSION: argMap['--restUrlExpression'] || '"/rest/"',
        TABLES: tables,
        VIEWS: views,
        RELATIONS: relations,
        RestTableNames: relations.map(function (table) { return "'" + table.TABLE_NAME + "'"; }).join('\n | '),
        RestShortTableNames: relations.map(function (table) { return "'" + table.TABLE_NAME_SHORT + "'"; }).join('\n | '),
        RestTableInterfaces: relations.map(function (table) { return 'i' + table.TABLE_NAME_SHORT_PASCAL_CASE; }).join('\n | '),
        RestViewNames: views.length
            ? views.map(function (table) { return "'" + table.TABLE_NAME + "'"; }).join('\n | ')
            : 'never',
        RestShortViewNames: views.length
            ? views.map(function (table) { return "'" + table.TABLE_NAME_SHORT + "'"; }).join('\n | ')
            : 'never',
    };
};
var withScopedTableSymbols = function (databaseDefinition, tables) { return tables.map(function (table) { return (__assign(__assign({}, table), { SCOPED_TABLE_CONST: "".concat(databaseDefinition.DATABASE_KEY_IDENTIFIER, "_").concat(table.TABLE_NAME_SHORT), SCOPED_BINDING_CONST: "".concat(databaseDefinition.DATABASE_KEY_PASCAL_CASE, "_").concat(table.TABLE_NAME_SHORT_PASCAL_CASE) })); }); };
var prefixRelativeImportSpecifier = function (specifier, prefix) {
    if (specifier.startsWith("./")) {
        return "".concat(prefix).concat(specifier.slice(2));
    }
    return "".concat(prefix).concat(specifier);
};
var shiftCustomImports = function (customImports, prefix) {
    return customImports
        .replace(/(from\s*["'])(\.{1,2}\/[^"']+)(["'])/g, function (_match, start, specifier, end) {
        return "".concat(start).concat(prefixRelativeImportSpecifier(specifier, prefix)).concat(end);
    })
        .replace(/(import\s*["'])(\.{1,2}\/[^"']+)(["'])/g, function (_match, start, specifier, end) {
        return "".concat(start).concat(prefixRelativeImportSpecifier(specifier, prefix)).concat(end);
    });
};
var applyGeneratedModuleMetadata = function (tableData) {
    var e_12, _a;
    var customImports = tableData.CUSTOM_IMPORTS || "";
    tableData.CUSTOM_IMPORTS_GENERATED = shiftCustomImports(customImports, "../");
    tableData.CUSTOM_IMPORTS_TABLE = shiftCustomImports(customImports, "../../");
    try {
        for (var _b = __values(tableData.SCOPED_DATABASES || []), _c = _b.next(); !_c.done; _c = _b.next()) {
            var scopedDatabase = _c.value;
            scopedDatabase.OBJECT_OVERRIDES = tableData.OBJECT_OVERRIDES;
            scopedDatabase.CUSTOM_IMPORTS_GENERATED = tableData.CUSTOM_IMPORTS_GENERATED;
        }
    }
    catch (e_12_1) { e_12 = { error: e_12_1 }; }
    finally {
        try {
            if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
        }
        finally { if (e_12) throw e_12.error; }
    }
};
var writeGeneratedBindings = function (outputDir, tableData) {
    var e_13, _a, e_14, _b;
    var templatesDir = path.resolve(__dirname, 'assets/handlebars');
    var readTemplate = function (templateName) {
        return fs.readFileSync(path.join(templatesDir, templateName), 'utf-8');
    };
    var c6Template = Handlebars.compile(readTemplate('C6.ts.handlebars'));
    var c6CoreTemplate = Handlebars.compile(readTemplate('C6.core.ts.handlebars'));
    var c6ScopedTemplate = Handlebars.compile(readTemplate('C6.scoped.ts.handlebars'));
    var c6TableTemplate = Handlebars.compile(readTemplate('C6.table.ts.handlebars'));
    var c6ViewTemplate = Handlebars.compile(readTemplate('C6.view.ts.handlebars'));
    var c6TablesIndexTemplate = Handlebars.compile(readTemplate('C6.tablesIndex.ts.handlebars'));
    var c6ViewsIndexTemplate = Handlebars.compile(readTemplate('C6.viewsIndex.ts.handlebars'));
    var c6TestTemplate = Handlebars.compile(readTemplate('C6.test.ts.handlebars'));
    var generatedDir = path.join(outputDir, 'C6.generated');
    var tablesDir = path.join(generatedDir, 'tables');
    var viewsDir = path.join(generatedDir, 'views');
    fs.rmSync(generatedDir, { recursive: true, force: true });
    fs.rmSync(path.join(outputDir, 'C6.js'), { force: true });
    createDirIfNotExists(tablesDir);
    createDirIfNotExists(viewsDir);
    fs.writeFileSync(path.join(outputDir, 'C6.ts'), c6Template(tableData));
    fs.writeFileSync(path.join(outputDir, 'C6.test.ts'), c6TestTemplate(tableData));
    fs.writeFileSync(path.join(generatedDir, 'core.ts'), c6CoreTemplate(tableData));
    fs.writeFileSync(path.join(generatedDir, 'scoped.ts'), c6ScopedTemplate(tableData));
    fs.writeFileSync(path.join(tablesDir, 'index.ts'), c6TablesIndexTemplate(tableData));
    fs.writeFileSync(path.join(viewsDir, 'index.ts'), c6ViewsIndexTemplate(tableData));
    try {
        for (var _c = __values(tableData.TABLES), _d = _c.next(); !_d.done; _d = _c.next()) {
            var table = _d.value;
            fs.writeFileSync(path.join(tablesDir, "".concat(table.TABLE_NAME_SHORT_PASCAL_CASE, ".ts")), c6TableTemplate(__assign(__assign({}, tableData), table)));
        }
    }
    catch (e_13_1) { e_13 = { error: e_13_1 }; }
    finally {
        try {
            if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
        }
        finally { if (e_13) throw e_13.error; }
    }
    try {
        for (var _e = __values(tableData.VIEWS), _f = _e.next(); !_f.done; _f = _e.next()) {
            var view = _f.value;
            fs.writeFileSync(path.join(viewsDir, "".concat(view.TABLE_NAME_SHORT_PASCAL_CASE, ".ts")), c6ViewTemplate(__assign(__assign({}, tableData), view)));
        }
    }
    catch (e_14_1) { e_14 = { error: e_14_1 }; }
    finally {
        try {
            if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
        }
        finally { if (e_14) throw e_14.error; }
    }
};
var applyConfigDefaultsToArgs = function (config) {
    if (argMap['--prefix'] == null && typeof config.prefix === "string") {
        argMap['--prefix'] = config.prefix;
    }
    if (argMap['--output'] == null && typeof config.output === "string") {
        argMap['--output'] = config.output;
    }
    if (argMap['--restUrlExpression'] == null && typeof config.restUrlExpression === "string") {
        argMap['--restUrlExpression'] = config.restUrlExpression;
    }
    if (argMap['--objectOverrides'] == null && typeof config.objectOverrides === "string") {
        argMap['--objectOverrides'] = config.objectOverrides;
    }
    if (argMap['--interfaceOverrides'] == null && typeof config.interfaceOverrides === "string") {
        argMap['--interfaceOverrides'] = config.interfaceOverrides;
    }
    if (argMap['--customImports'] == null && typeof config.customImports === "string") {
        argMap['--customImports'] = config.customImports;
    }
    if (argMap['--react'] == null && typeof config.react === "string") {
        argMap['--react'] = config.react;
    }
};
var resolveConfigPathFromArgsOrDiscovery = function () { return __awaiter(void 0, void 0, void 0, function () {
    var explicitPath, discovered, rl, answer, generatedConfig, generatedPath;
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0:
                if (typeof argMap['--config'] === "string" && argMap['--config'].trim().length > 0) {
                    explicitPath = path.resolve(process.cwd(), argMap['--config']);
                    if (!fs.existsSync(explicitPath)) {
                        throw new Error("Config not found at '".concat(explicitPath, "'."));
                    }
                    return [2 /*return*/, explicitPath];
                }
                discovered = discoverConfigPath(process.cwd());
                if (discovered) {
                    return [2 /*return*/, discovered];
                }
                if (!process.stdin.isTTY || !process.stdout.isTTY) {
                    throw new Error("No config found. Pass --config <path> or create C6.config.json/C6.config.ts (or .C6.json/.C6.ts) in the current directory tree.");
                }
                rl = readline.createInterface({
                    input: process.stdin,
                    output: process.stdout,
                });
                _a.label = 1;
            case 1:
                _a.trys.push([1, , 3, 4]);
                return [4 /*yield*/, askQuestion(rl, "No config found. Create ./C6.config.json now? (y/N): ")];
            case 2:
                answer = (_a.sent()).toLowerCase();
                if (answer !== "y" && answer !== "yes") {
                    throw new Error("Config is required. Generation cancelled.");
                }
                return [3 /*break*/, 4];
            case 3:
                rl.close();
                return [7 /*endfinally*/];
            case 4: return [4 /*yield*/, buildConfigInteractively()];
            case 5:
                generatedConfig = _a.sent();
                generatedPath = path.resolve(process.cwd(), "C6.config.json");
                fs.writeFileSync(generatedPath, "".concat(JSON.stringify(generatedConfig, null, 2), "\n"));
                console.log("[generateRestBindings] Created config at ".concat(generatedPath));
                return [2 /*return*/, generatedPath];
        }
    });
}); };
var getCurrentPackageName = function () {
    try {
        var packageJsonPath = path.resolve(process.cwd(), "package.json");
        if (!fs.existsSync(packageJsonPath)) {
            return null;
        }
        var packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
        return typeof packageJson.name === "string" ? packageJson.name : null;
    }
    catch (_a) {
        return null;
    }
};
var typeCheckGeneratedC6 = function (outputDir) {
    var c6Path = path.join(outputDir, "C6.ts");
    var tempTsConfigPath = path.join(outputDir, ".C6.generated-typecheck.tsconfig.json");
    var currentPackageName = getCurrentPackageName();
    var localSourceRoot = path.resolve(process.cwd(), "src");
    var compilerOptions = {
        target: "ES2020",
        module: "ES2020",
        moduleResolution: "node",
        esModuleInterop: true,
        resolveJsonModule: true,
        skipLibCheck: true,
        noEmit: true,
    };
    if (currentPackageName === "@carbonorm/carbonnode" &&
        fs.existsSync(path.join(localSourceRoot, "index.ts"))) {
        compilerOptions.baseUrl = localSourceRoot;
        compilerOptions.paths = {
            "@carbonorm/carbonnode": ["."],
        };
    }
    fs.writeFileSync(tempTsConfigPath, "".concat(JSON.stringify({
        compilerOptions: compilerOptions,
        files: [c6Path],
    }, null, 2), "\n"));
    try {
        execFileSync("npx", [
            "tsc",
            "--project",
            tempTsConfigPath,
        ], { encoding: "utf-8" });
    }
    finally {
        fs.rmSync(tempTsConfigPath, { force: true });
    }
};
var resolveScopedDefinitionsFromConfig = function (config) {
    var scopedDefinitions = buildScopedDefinitionsFromConfig(config);
    var requestedPrimaryAlias = typeof argMap['--primaryAlias'] === "string"
        ? argMap['--primaryAlias'].trim()
        : typeof config.primaryAlias === "string"
            ? config.primaryAlias.trim()
            : "";
    var primaryDefinition = requestedPrimaryAlias
        ? scopedDefinitions.find(function (definition) { return definition.DATABASE_KEY === requestedPrimaryAlias; })
        : scopedDefinitions[0];
    if (!primaryDefinition) {
        throw new Error(requestedPrimaryAlias
            ? "primaryAlias '".concat(requestedPrimaryAlias, "' was not found in config databases.")
            : "Config databases resolved to an empty target list.");
    }
    return {
        scopedDefinitions: scopedDefinitions,
        primaryDefinition: primaryDefinition,
    };
};
var main = function () { return __awaiter(void 0, void 0, void 0, function () {
    var configPath, config, _a, scopedDefinitions, primaryDefinition, dumpFileLocation, sql, schemaMetadata, tableData, scopedDatabaseSchemas, outputDir;
    return __generator(this, function (_b) {
        switch (_b.label) {
            case 0: return [4 /*yield*/, resolveConfigPathFromArgsOrDiscovery()];
            case 1:
                configPath = _b.sent();
                return [4 /*yield*/, loadConfigFromPath(configPath)];
            case 2:
                config = _b.sent();
                applyConfigDefaultsToArgs(config);
                _a = resolveScopedDefinitionsFromConfig(config), scopedDefinitions = _a.scopedDefinitions, primaryDefinition = _a.primaryDefinition;
                MySQLDump.DB_USER = primaryDefinition.CONNECTION.user;
                MySQLDump.DB_PASS = primaryDefinition.CONNECTION.pass;
                MySQLDump.DB_HOST = primaryDefinition.CONNECTION.host;
                MySQLDump.DB_PORT = primaryDefinition.CONNECTION.port;
                MySQLDump.DB_NAME = primaryDefinition.DATABASE_NAME;
                MySQLDump.DB_PREFIX = argMap['--prefix'] || '';
                MySQLDump.RELATIVE_OUTPUT_DIR = argMap['--output'] || '/src';
                MySQLDump.OUTPUT_DIR = path.isAbsolute(MySQLDump.RELATIVE_OUTPUT_DIR)
                    ? MySQLDump.RELATIVE_OUTPUT_DIR
                    : path.join(process.cwd(), MySQLDump.RELATIVE_OUTPUT_DIR);
                createDirIfNotExists(MySQLDump.OUTPUT_DIR);
                pathRuntimeReference = MySQLDump.RELATIVE_OUTPUT_DIR.replace(/(^\/(src\/)?)|(\/+$)/g, '');
                dumpFileLocation = primaryDefinition.DIALECT === 'postgresql'
                    ? PostgreSQLDump.PgDump('pg_dump', '', primaryDefinition.DATABASE_NAME, primaryDefinition.CONNECTION)
                    : MySQLDump.MySQLDump('mysqldump', false, true, '', '', '', primaryDefinition.DATABASE_NAME, primaryDefinition.CONNECTION);
                if (!fs.existsSync(dumpFileLocation)) {
                    throw new Error("[generateRestBindings] Missing base dump at ".concat(dumpFileLocation, "."));
                }
                sql = fs.readFileSync(dumpFileLocation, 'utf-8');
                schemaMetadata = primaryDefinition.DIALECT === 'postgresql'
                    ? PostgreSQLDump.LoadInformationSchema(primaryDefinition.DATABASE_NAME, primaryDefinition.CONNECTION)
                    : MySQLDump.LoadInformationSchema(primaryDefinition.DATABASE_NAME, primaryDefinition.CONNECTION);
                tableData = parseSQLToTypeScript(sql, schemaMetadata);
                scopedDatabaseSchemas = scopedDefinitions.map(function (databaseDefinition) {
                    var isPrimaryScopedDefinition = databaseDefinition.DATABASE_NAME === primaryDefinition.DATABASE_NAME
                        && databaseDefinition.CONNECTION.host === primaryDefinition.CONNECTION.host
                        && databaseDefinition.CONNECTION.port === primaryDefinition.CONNECTION.port
                        && databaseDefinition.CONNECTION.user === primaryDefinition.CONNECTION.user
                        && databaseDefinition.CONNECTION.pass === primaryDefinition.CONNECTION.pass;
                    if (isPrimaryScopedDefinition) {
                        return {
                            DATABASE_KEY: databaseDefinition.DATABASE_KEY,
                            DATABASE_NAME: databaseDefinition.DATABASE_NAME,
                            DATABASE_KEY_IDENTIFIER: databaseDefinition.DATABASE_KEY_IDENTIFIER,
                            DATABASE_KEY_PASCAL_CASE: databaseDefinition.DATABASE_KEY_PASCAL_CASE,
                            RELATIONS: withScopedTableSymbols(databaseDefinition, tableData.RELATIONS),
                        };
                    }
                    var scopedDumpFile = path.join(MySQLDump.OUTPUT_DIR, databaseDefinition.DIALECT === 'postgresql'
                        ? "C6.".concat(databaseDefinition.DATABASE_KEY_IDENTIFIER, ".pg_dump.sql")
                        : "C6.".concat(databaseDefinition.DATABASE_KEY_IDENTIFIER, ".mysqldump.sql"));
                    var scopedDumpPath = databaseDefinition.DIALECT === 'postgresql'
                        ? PostgreSQLDump.PgDump('pg_dump', scopedDumpFile, databaseDefinition.DATABASE_NAME, databaseDefinition.CONNECTION)
                        : MySQLDump.MySQLDump('mysqldump', false, true, scopedDumpFile, '', '', databaseDefinition.DATABASE_NAME, databaseDefinition.CONNECTION, databaseDefinition.DATABASE_KEY_IDENTIFIER);
                    if (!fs.existsSync(scopedDumpPath)) {
                        throw new Error("[generateRestBindings] Missing scoped dump for database key '".concat(databaseDefinition.DATABASE_KEY, "' at ").concat(scopedDumpPath, "."));
                    }
                    var scopedSql = fs.readFileSync(scopedDumpPath, 'utf-8');
                    var scopedSchemaMetadata = databaseDefinition.DIALECT === 'postgresql'
                        ? PostgreSQLDump.LoadInformationSchema(databaseDefinition.DATABASE_NAME, databaseDefinition.CONNECTION)
                        : MySQLDump.LoadInformationSchema(databaseDefinition.DATABASE_NAME, databaseDefinition.CONNECTION, databaseDefinition.DATABASE_KEY_IDENTIFIER);
                    var scopedTableData = parseSQLToTypeScript(scopedSql, scopedSchemaMetadata);
                    return {
                        DATABASE_KEY: databaseDefinition.DATABASE_KEY,
                        DATABASE_NAME: databaseDefinition.DATABASE_NAME,
                        DATABASE_KEY_IDENTIFIER: databaseDefinition.DATABASE_KEY_IDENTIFIER,
                        DATABASE_KEY_PASCAL_CASE: databaseDefinition.DATABASE_KEY_PASCAL_CASE,
                        RELATIONS: withScopedTableSymbols(databaseDefinition, scopedTableData.RELATIONS),
                    };
                });
                tableData.SCOPED_DATABASES = scopedDatabaseSchemas;
                applyGeneratedModuleMetadata(tableData);
                // write to file
                fs.writeFileSync(path.join(MySQLDump.OUTPUT_DIR, 'C6.mysqldump.json'), JSON.stringify(tableData));
                outputDir = MySQLDump.OUTPUT_DIR;
                writeGeneratedBindings(outputDir, tableData);
                // type-check generated TypeScript without writing parallel JavaScript artifacts
                if (process.env.C6_SKIP_GENERATED_TSC === "1") {
                    console.log("[generateRestBindings] Skipping generated C6.ts type check (C6_SKIP_GENERATED_TSC=1).");
                }
                else {
                    try {
                        typeCheckGeneratedC6(outputDir);
                    }
                    catch (e) {
                        console.warn('TypeScript type check for generated C6.ts reported errors:', e);
                    }
                }
                console.log('Successfully created CarbonORM bindings!');
                return [2 /*return*/];
        }
    });
}); };
main().catch(function (error) {
    console.error(error instanceof Error ? error.message : error);
    process.exit(1);
});
