import { test, expect } from "bun:test";
import {
  createDb,
  setDb,
  getDb,
  hasDb,
  registerDb,
  registerLazyDb,
  databaseNames,
  closeDatabases,
} from "../src/index.ts";
import { sqlite } from "../src/adapters/sqlite.ts";
import type { Db } from "../src/index.ts";

test("multi-database registry: default + named connections", async () => {
  await closeDatabases(); // isolate from any prior state

  const main = createDb(sqlite(":memory:"));
  const analytics = createDb(sqlite(":memory:"));

  setDb(main); // default
  registerDb("analytics", analytics); // named

  expect(getDb()).toBe(main);
  expect(getDb("analytics")).toBe(analytics);
  expect(hasDb()).toBe(true);
  expect(hasDb("analytics")).toBe(true);
  expect(hasDb("missing")).toBe(false);
  expect(databaseNames().sort()).toEqual(["analytics", "default"]);

  // Each connection is independent — a table in one is not in the other.
  await main.exec("CREATE TABLE a (id INTEGER)");
  await analytics.exec("CREATE TABLE b (id INTEGER)");
  await getDb().exec("INSERT INTO a (id) VALUES (1)");
  await getDb("analytics").exec("INSERT INTO b (id) VALUES (2)");
  expect((await getDb().all("SELECT id FROM a")).length).toBe(1);
  expect((await getDb("analytics").all("SELECT id FROM b")).length).toBe(1);

  await closeDatabases();
  expect(hasDb()).toBe(false);
  expect(databaseNames()).toEqual([]);
});

test("getDb throws a helpful error for an unknown named database", async () => {
  await closeDatabases();
  setDb(createDb(sqlite(":memory:")));
  expect(() => getDb("nope")).toThrow(/No database named 'nope'/);
  await closeDatabases();
});

test("named databases can be registered without opening a connection", async () => {
  await closeDatabases();
  let calls = 0;
  const analytics = createDb(sqlite(":memory:"));

  registerLazyDb("analytics", () => {
    calls++;
    return analytics;
  });

  expect(hasDb("analytics")).toBe(true);
  expect(databaseNames()).toEqual(["analytics"]);
  expect(calls).toBe(0);
  expect(getDb("analytics")).toBe(analytics);
  expect(getDb("analytics")).toBe(analytics);
  expect(calls).toBe(1);

  await closeDatabases();
});

test("closing the registry does not instantiate unused lazy databases", async () => {
  await closeDatabases();
  let calls = 0;
  registerLazyDb("unused", () => {
    calls++;
    return createDb(sqlite(":memory:"));
  });

  await closeDatabases();
  expect(calls).toBe(0);
});

test("registry closes every database and clears itself when one close fails", async () => {
  await closeDatabases();
  let secondClosed = false;
  const broken = { close: async () => Promise.reject(new Error("close failed")) } as Db;
  const healthy = { close: () => void (secondClosed = true) } as Db;
  setDb(broken);
  registerDb("healthy", healthy);

  await expect(closeDatabases()).rejects.toThrow("databases failed to close");
  expect(secondClosed).toBe(true);
  expect(databaseNames()).toEqual([]);
});
