import { test, beforeAll, afterAll } from "bun:test"
import assert from 'assert'
import { rm } from "fs/promises"

import { openDB, DB } from "../src/db.ts"

type Message = { message: string }

class User {
  id: number
  name: string

  constructor({ id, name }: { id: number, name: string }) {
    this.id = id
    this.name = name
  }

  displayName() {
    return "Mx. " + this.name
  }
}

const TEST_DBNAME = "test-db1.db"

let db = await openDB({ sqlite: TEST_DBNAME })

beforeAll(async () => {
  await rm(TEST_DBNAME).catch(() => { })  // Clean up any existing db
  db = await openDB({ sqlite: TEST_DBNAME })
  await db.run("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)")
})

afterAll(async () => {
  await rm(TEST_DBNAME)
})

test("db.queryOne", async () => {
  const result = await db.queryOne<Message>("SELECT 'hey there' as message")
  assert.equal(result?.message, "hey there")
})

test("db.query", async () => {
  const result = await db.query<Message>("SELECT 'hey there' as message")
  assert.equal(result[0]?.message, "hey there")
})

test("db.run", async () => {
  await db.run("CREATE TABLE IF NOT EXISTS messages (message TEXT)")
  await db.run("INSERT INTO messages (message) VALUES ('hey there')")
  const result = await db.queryOne<Message>("SELECT message FROM messages")
  assert.equal(result?.message, "hey there")
})

test("User model", async () => {
  await db.run("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)")
  await db.run("INSERT INTO users (name) VALUES ('John')")
  await db.run("INSERT INTO users (name) VALUES ('Jane')")
  await db.run("INSERT INTO users (name) VALUES ('Jimbo')")
  await db.run("INSERT INTO users (name) VALUES ('Jones')")
  await db.run("INSERT INTO users (name) VALUES ('Rogers')")

  const users = await db.query<User>("SELECT * FROM users WHERE name LIKE $search", { search: "J%" })
  assert.equal(users.length, 4)
  assert.equal(users[0]?.name, "John")
  assert.equal(users[1]?.name, "Jane")
  assert.equal(users[2]?.name, "Jimbo")
  assert.equal(users[3]?.name, "Jones")

  const user = await db.queryOne<User>("SELECT * FROM users WHERE name LIKE $search", { search: "J%" }, row => new User(row))
  assert.equal(user?.name, "John")
  assert.equal(user?.displayName(), "Mx. John")
})

test("transactions", async () => {
  await db.run("DROP TABLE IF EXISTS messages")
  await db.run("CREATE TABLE IF NOT EXISTS messages (message TEXT)")

  try {
    await db.transaction(async (tx: DB) => {
      await tx.run("INSERT INTO messages (message) VALUES ('hey there')")
      const result = await tx.queryOne<Message>("SELECT message FROM messages")
      assert.equal(result?.message, "hey there")
      throw new Error("Kill the transaction")
    })
  } catch (e) {
    // console.error(e)
  }

  const result = await db.queryOne<Message>("SELECT message FROM messages")
  assert.equal(result, null)
})
