/**
 * SQLite3 module.
 *
 * This module borrows a lot of inspiration from [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) and
 * the [Bun sqlite module](https://bun.sh/docs/api/sqlite).
 *
 * ```js
 * import { Database } from 'tjs:sqlite';
 *
 * const db = new Database(':memory:');
 * db.exec('CREATE TABLE greetings (message TEXT)');
 * const ins = db.prepare('INSERT INTO greetings (message) VALUES (?)');
 * ins.run('Hello world!');
 * const rows = db.prepare('SELECT * FROM greetings').all();
 * console.log(rows); // [{ message: 'Hello world!' }]
 * db.close();
 * ```
 *
 * @module tjs:sqlite
 */
declare module 'tjs:sqlite'{
    export interface IStatement extends Disposable {
        /**
         * Runs the SQL statement, ignoring the result. This is commonly used for
         * CREATE, INSERT and statement of that sort.
         *
         * @param args The bound parameters for the statement.
         */
        run(...args: any[]): void;

        /**
         * Runs the SQL statement, returning an array of objects with the name of the
         * columns and matching values.
         *
         * @param args The bound parameters for the statement.
         */
        all(...args: any[]): any[];

        /**
         * Free all resources associated with this statement. No other function
         * can be called on it afterwards. Idempotent.
         *
         * Aliased as `Symbol.dispose`, so `using stmt = db.prepare(...)`
         * finalizes the statement at scope exit.
         */
        finalize(): void;

        /**
         * Stringify the statement by expanding the SQL query.
         */
        toString(): string;
    }

    export interface IDatabaseOptions {
        /**
         * Whether the database needs to be created if it doesn't exist.
         * Defaults to `true`.
         */
        create: boolean;

        /**
         * Whether the database should be open in read-only mode or not.
         * Defaults to `false`.
         */
        readOnly: boolean;
    }

    export interface ITransaction extends Function {
        deferred: Function;
        immediate: Function;
        exclusive: Function;
    }

    export class Database {
        /**
         * Opens a SQLite database.
         *
         * @param dbName The path of the database. Defaults to `:memory:`, which
         * opens an in-memory database.
         * @param options Options when opening the database.
         */
        constructor(dbName: string, options: IDatabaseOptions);

        /**
         * Execute the given SQL statement(s).
         *
         * @param sql - The SQL statement(s) that will run.
         */
        exec(sql: string): void;

        /**
         * Create a prepared statement, to run SQL queries.
         *
         * @param sql - The SQL query that will run.
         */
        prepare(sql: string): IStatement;

        /**
         * Wrap the given function so it runs in a [transaction](https://sqlite.org/lang_transaction.html).
         * When the (returned) function is invoked, it will start a new transaction. When the function returns,
         * the transaction will be committed. If an exception is thrown, the transaction will be rolled back.
         *
         * ```js
         * const ins = db.prepare('INSERT INTO test (txt, int) VALUES(?, ?)');
         * const insMany = db.transaction(datas => {
         *     for (const data of datas) {
         *         ins.run(data);
         *     }
         * });
         *
         * insMany([
         *     [ '1234', 1234 ],
         *     [ '4321', 4321 ],
         * ]);
         * ```
         * Transaction functions can be called from inside other transaction functions. When doing so,
         * the inner transaction becomes a [savepoint](https://www.sqlite.org/lang_savepoint.html). If an error
         * is thrown inside of a nested transaction function, the nested transaction function will roll back
         * to the state just before the savepoint. If the error is not caught in the outer transaction function,
         * this will cause the outer transaction function to roll back as well.
         *
         * Transactions also come with deferred, immediate, and exclusive versions:
         *
         * ```js
         * insertMany(datas); // uses "BEGIN"
         * insertMany.deferred(datas); // uses "BEGIN DEFERRED"
         * insertMany.immediate(datas); // uses "BEGIN IMMEDIATE"
         * insertMany.exclusive(datas); // uses "BEGIN EXCLUSIVE"
         * ```
         *
         * NOTE: This implementation was mostly taken from [better-sqlite3](https://github.com/WiseLibs/better-sqlite3/blob/6acc3fcebe469969aa29319714b187a53ada0934/docs/api.md#transactionfunction---function).
         *
         * @param fn - The function to be wrapped in a transaction.
         */
        transaction(fn: Function): ITransaction;

        /**
         * Closes the database. No further operations can be performed afterwards.
         * Idempotent.
         *
         * Aliased as `Symbol.dispose`, so `using db = new Database(...)`
         * closes the database at scope exit.
         */
        close(): void;

        /**
         * Load an extension from file
         * @param file location of the shared library
         * @param entrypoint entrypoint, if left empty a guess is made by sqlite
         */
        loadExtension(file:string, entrypoint?:string): undefined;

    }
    export interface Database extends Disposable {}
}
