package aceboot::orm.tests

import aceboot::orm.common.*
import aceboot::orm.sqlite.*

import std.unittest.*
import std.unittest.testmacro.*

// 复用 repository_test.cj 的 _T/_TMapper(同包)。

func mkSeeded(): (Repository<_T>, DataSource) {
    let ds = DataSource(SqliteDriver(":memory:"))
    setGlobalDataSource(ds)
    let repo = Repository<_T>(ds, _TMapper())
    repo.createTable()
    for ((name, n) in [("alice", 10), ("bob", 20), ("carol", 30), ("dave", 40)]) {
        let e = _T()
        e.name = name
        e.n = n
        repo.insert(e)
    }
    return (repo, ds)
}

@Test
func test_findby_eq() {
    let (repo, ds) = mkSeeded()
    let rows = repo.findBy([eq("name", DbText("bob"))])
    @Expect(rows.size, 1)
    @Expect(rows[0].name, "bob")
    @Expect(rows[0].n, 20)
    ds.driver.close()
}

@Test
func test_findby_gt() {
    let (repo, ds) = mkSeeded()
    let rows = repo.findBy([gt("n", DbInt(20))])
    @Expect(rows.size, 2) // carol(30), dave(40)
    ds.driver.close()
}

@Test
func test_findby_like() {
    let (repo, ds) = mkSeeded()
    let rows = repo.findBy([like("name", DbText("%a%"))])
    @Expect(rows.size, 3) // alice, carol, dave
    ds.driver.close()
}

@Test
func test_findby_inlist() {
    let (repo, ds) = mkSeeded()
    let rows = repo.findBy([inList("name", [DbText("alice"), DbText("dave")])])
    @Expect(rows.size, 2)
    // 空列表 → 永假
    @Expect(repo.findBy([inList("name", Array<DbValue>())]).size, 0)
    ds.driver.close()
}

@Test
func test_findby_between() {
    let (repo, ds) = mkSeeded()
    let rows = repo.findBy([between("n", DbInt(15), DbInt(35))])
    @Expect(rows.size, 2) // bob(20), carol(30)
    ds.driver.close()
}

@Test
func test_findby_isnull() {
    let (repo, ds) = mkSeeded()
    // 全部行 name 非空 → IS NULL 命中 0,IS NOT NULL 命中 4
    @Expect(repo.findBy([isNull("name")]).size, 0)
    @Expect(repo.findBy([isNotNull("name")]).size, 4)
    ds.driver.close()
}

@Test
func test_findby_multi_and() {
    let (repo, ds) = mkSeeded()
    let rows = repo.findBy([gt("n", DbInt(10)), lt("n", DbInt(40))])
    @Expect(rows.size, 2) // bob(20), carol(30)
    ds.driver.close()
}

@Test
func test_findonebimplement() {
    let (repo, ds) = mkSeeded()
    let one = repo.findOneBy([eq("name", DbText("carol"))])
    @Expect(one.getOrThrow().n, 30)
    @Expect(repo.findOneBy([eq("name", DbText("nobody"))]).isNone(), true)
    ds.driver.close()
}

@Test
func test_countby_existsby() {
    let (repo, ds) = mkSeeded()
    @Expect(repo.countBy([gte("n", DbInt(20))]), 3)
    @Expect(repo.existsBy([eq("name", DbText("bob"))]), true)
    @Expect(repo.existsBy([eq("name", DbText("zzz"))]), false)
    ds.driver.close()
}

@Test
func test_find_and_count() {
    let (repo, ds) = mkSeeded()
    let (list, total) = repo.findAndCount([gt("n", DbInt(15))])
    @Expect(list.size, 3) // bob, carol, dave
    @Expect(total, 3)
    ds.driver.close()
}