/**
 * Postgres + MySQL adapters built on Bun's native SQL client (`Bun.SQL`) — no
 * external driver dependency. Both speak the same `Driver` interface; only the
 * dialect (and thus the DDL types + placeholder style) differ.
 *
 * `Bun.SQL` pools connections, so transactions use its managed `begin(fn)` to
 * keep BEGIN/…/COMMIT on one reserved connection.
 */

import type { Dialect } from "../sql.ts";
import type { Driver, Row, TxHandle } from "../driver.ts";

interface BunSqlClient {
  unsafe(query: string, params?: unknown[]): Promise<unknown>;
  begin<T>(fn: (tx: BunSqlClient) => Promise<T>): Promise<T>;
  close(): Promise<void>;
}

interface ExecMeta {
  count?: number;
  affectedRows?: number;
  lastInsertRowid?: number | bigint;
  insertId?: number | bigint;
}

/** Convert the framework's portable positional placeholders for PostgreSQL. */
export function sqlForDialect(sql: string, dialect: Dialect): string {
  if (dialect !== "postgres" || !sql.includes("?")) return sql;
  let index = 0;
  return sql.replace(/\?/g, () => `$${++index}`);
}

function runnerFor(client: BunSqlClient, dialect: Dialect): TxHandle {
  return {
    async query(sql, params = []): Promise<Row[]> {
      const rows = (await client.unsafe(sqlForDialect(sql, dialect), params)) as Iterable<Row>;
      return Array.from(rows);
    },
    async exec(sql, params = []) {
      const meta = (await client.unsafe(sqlForDialect(sql, dialect), params)) as ExecMeta;
      const id = meta.lastInsertRowid ?? meta.insertId;
      return {
        changes: Number(meta.affectedRows ?? meta.count ?? 0),
        lastInsertId: id != null ? Number(id) : undefined,
      };
    },
  };
}

/** Create a Bun.sql-backed driver for the given connection URL + dialect. */
export function bunSql(url: string, dialect: Dialect): Driver {
  const Ctor = (Bun as unknown as { SQL: new (u: string) => BunSqlClient }).SQL;
  const client = new Ctor(url);
  const runner = runnerFor(client, dialect);
  return {
    dialect,
    query: runner.query,
    exec: runner.exec,
    transaction(fn) {
      return client.begin((tx) => fn(runnerFor(tx, dialect)));
    },
    close() {
      return client.close();
    },
  };
}

/** PostgreSQL adapter (`postgres://user:pass@host:5432/db`). Placeholders: `$N`. */
export function postgres(url: string): Driver {
  return bunSql(url, "postgres");
}

/** MySQL adapter (`mysql://user:pass@host:3306/db`). Placeholders: `?`. */
export function mysql(url: string): Driver {
  return bunSql(url, "mysql");
}
