/**
 * SQLite adapter, built on Bun's zero-dependency `bun:sqlite`. Use a file URL
 * (`file:./dev.db`) or the default in-memory database (great for tests). This is
 * the reference adapter — it needs no external service to run.
 */

import { Database } from "bun:sqlite";
import type { Driver, ExecResult, Row, TxHandle } from "../driver.ts";

/** SQLite can only bind numbers/strings/bigints/null/blobs — coerce JS values. */
function bind(params: unknown[]): unknown[] {
  return params.map((p) => {
    if (p === true) return 1;
    if (p === false) return 0;
    if (p === undefined) return null;
    if (p instanceof Date) return p.toISOString();
    return p;
  });
}

/** Create a SQLite driver. `url` may be `file:./x.db`, a path, or `:memory:`. */
export function sqlite(url = ":memory:"): Driver {
  const path = url.replace(/^(file:|sqlite:)/, "") || ":memory:";
  const database = new Database(path);
  database.exec("PRAGMA foreign_keys = ON;");

  const runner: TxHandle = {
    async query(sql, params = []): Promise<Row[]> {
      return database.query(sql).all(...(bind(params) as never[])) as Row[];
    },
    async exec(sql, params = []): Promise<ExecResult> {
      if (params.length === 0) {
        database.exec(sql); // DDL / multi-statement
        return { changes: 0 };
      }
      const result = database.query(sql).run(...(bind(params) as never[]));
      return { changes: result.changes, lastInsertId: Number(result.lastInsertRowid) };
    },
  };

  return {
    dialect: "sqlite",
    query: runner.query,
    exec: runner.exec,
    async transaction(fn) {
      database.exec("BEGIN");
      try {
        const result = await fn(runner);
        database.exec("COMMIT");
        return result;
      } catch (err) {
        database.exec("ROLLBACK");
        throw err;
      }
    },
    close() {
      database.close();
    },
  };
}
