import { CompiledQuery, SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler, } from "kysely";
class EnvironmentConnection {
    #backend;
    // Track nested transaction depth for savepoint emulation
    _txDepth = 0;
    _spNames = [];
    constructor(backend) {
        this.#backend = backend;
    }
    async executeQuery(compiledQuery) {
        const { sql, parameters } = compiledQuery;
        let res;
        try {
            res = await this.#backend.call("lix_execute_sync", {
                sql,
                parameters: (parameters ?? []),
            });
        }
        catch (err) {
            const previewParam = (v) => {
                try {
                    if (v instanceof Uint8Array)
                        return `Uint8Array(len=${v.byteLength})`;
                    if (typeof v === "string")
                        return JSON.stringify(v);
                    if (v && typeof v === "object")
                        return JSON.stringify(v);
                    return String(v);
                }
                catch {
                    return String(v);
                }
            };
            const submittedSql = sql;
            const originalSql = typeof err?.originalSql === "string" && err.originalSql
                ? err.originalSql
                : undefined;
            const displaySql = originalSql ?? submittedSql;
            const rewrittenSql = typeof err?.rewrittenSql === "string" && err.rewrittenSql
                ? err.rewrittenSql
                : undefined;
            const parameterValues = Array.isArray(err?.parameters)
                ? err.parameters
                : (parameters ?? []);
            // Example formatted error for quick reference:
            //
            // executeQuery Error
            //
            //   - cause: "version_id is required for state mutation"
            //   - sql:   insert into "file" ("id","path","data") values (?, ?, ?)
            //   - params:
            //       1: "WghuxhzbSR0XM7wdw5Pdd"
            //       2: "/x.md"
            //       3: Uint8Array(len=0)
            const formatParams = (target, values, indent) => {
                if (values.length === 0) {
                    target.push(`${indent}(none)`);
                    return;
                }
                values.forEach((p, i) => {
                    target.push(`${indent}${i + 1}. ${previewParam(p)}`);
                });
            };
            const lines = [];
            lines.push("executeQuery Error");
            lines.push(`- cause: "${String(err?.message ?? err)}"`);
            lines.push("");
            lines.push("user query");
            lines.push(`  - sql: ${submittedSql}`);
            lines.push("  - parameters:");
            formatParams(lines, parameters ?? [], "      ");
            if (displaySql === submittedSql) {
                lines.push("  - status: failed");
                if (rewrittenSql && rewrittenSql !== displaySql) {
                    lines.push(`  - rewrittenSql: ${rewrittenSql}`);
                }
            }
            else {
                lines.push("");
                lines.push("internal query (failed)");
                lines.push(`  - sql: ${displaySql}`);
                lines.push("  - parameters:");
                formatParams(lines, parameterValues, "      ");
                if (rewrittenSql) {
                    lines.push(`  - rewrittenSql: ${rewrittenSql}`);
                }
            }
            const wrapped = new Error(lines.join("\n"), { cause: err });
            throw wrapped;
        }
        // Map to Kysely QueryResult shape (no affected rows/insert id available)
        return {
            rows: (res?.rows ?? []),
        };
    }
    // eslint-disable-next-line require-yield
    async *streamQuery() {
        throw new Error("not supported for engine driver yet");
    }
}
export class EnvironmentDriver {
    #config;
    // Global transaction coordination for a single engine.
    // Ensures only one top‑level transaction owner at a time.
    #owner;
    #depth = 0; // total nesting depth for owner
    #waiters = [];
    constructor(config) {
        this.#config = config;
    }
    async init() {
        // no-op
    }
    async acquireConnection() {
        // Return a lightweight connection wrapper per acquire to isolate
        // transaction state (savepoints) across concurrent transactions.
        return new EnvironmentConnection(this.#config.backend);
    }
    async beginTransaction(connection) {
        const conn = connection;
        // Wait our turn if another connection owns the top‑level transaction
        while (this.#depth > 0 && this.#owner !== conn) {
            await new Promise((resolve) => this.#waiters.push(resolve));
        }
        if (this.#depth === 0) {
            // Acquire ownership and start top‑level txn
            this.#owner = conn;
            this.#depth = 1;
            conn._txDepth = 1;
            await connection.executeQuery(CompiledQuery.raw("begin"));
        }
        else {
            // Nested transaction owned by same connection → savepoint
            this.#depth++;
            conn._txDepth++;
            const spName = `lix_sp_${this.#depth}`;
            conn._spNames.push(spName);
            await connection.executeQuery(CompiledQuery.raw(`savepoint ${spName}`));
        }
    }
    async commitTransaction(connection) {
        const conn = connection;
        if (this.#owner !== conn) {
            throw new Error("commit from non-owner connection");
        }
        if (conn._txDepth > 1) {
            const spName = conn._spNames.pop();
            await connection.executeQuery(CompiledQuery.raw(`release savepoint ${spName}`));
            conn._txDepth--;
            this.#depth--;
        }
        else {
            await connection.executeQuery(CompiledQuery.raw("commit"));
            conn._txDepth = 0;
            this.#depth = 0;
            this.#owner = undefined;
            const w = this.#waiters.shift();
            if (w)
                w();
        }
    }
    async rollbackTransaction(connection) {
        const conn = connection;
        if (this.#owner !== conn) {
            throw new Error("rollback from non-owner connection");
        }
        if (conn._txDepth > 1) {
            const spName = conn._spNames.pop();
            await connection.executeQuery(CompiledQuery.raw(`rollback to savepoint ${spName}`));
            conn._txDepth--;
            this.#depth--;
        }
        else {
            await connection.executeQuery(CompiledQuery.raw("rollback"));
            conn._txDepth = 0;
            this.#depth = 0;
            this.#owner = undefined;
            const w = this.#waiters.shift();
            if (w)
                w();
        }
    }
    async releaseConnection() {
        // no-op
    }
    async destroy() {
        await this.#config.backend.close();
    }
}
export function createEnvironmentDialect(args) {
    return {
        createAdapter: () => new SqliteAdapter(),
        createDriver: () => new EnvironmentDriver({ backend: args.environment }),
        createIntrospector: (db) => new SqliteIntrospector(db),
        createQueryCompiler: () => new SqliteQueryCompiler(),
    };
}
//# sourceMappingURL=environment-dialect.js.map