package aceboot::orm.tests

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

import std.collection.*
import std.unittest.*
import std.unittest.testmacro.*
import aceboot::orm.macros.*

// ===== F3:嵌套点号关系 findOne(id, ["author.profile"]) 测试 =====
// 实体链:Post --author(ManyToOne)--> User --profile(OneToOne owner)--> Profile;
// Post 另有 comments(OneToMany)集合,验证混合路径。

@Entity["nr_profiles"]
public class _NrProfile {
    @Id[]
    public var id: Int64 = 0
    @Column[]
    public var bio: String = ""
}

@Entity["nr_users"]
public class _NrUser {
    @Id[]
    public var id: Int64 = 0
    @Column[]
    public var name: String = ""
    // owner 侧一对一:本表持 FK profileId 指向 profile.id。
    @OneToOne[_NrProfile, "nr_profiles", "profileId"]
    public var profile: ?_NrProfile = None
}

@Entity["nr_comments"]
public class _NrComment {
    @Id[]
    public var id: Int64 = 0
    @Column[]
    public var text: String = ""
    @Column["postId"]
    public var postId: Int64 = 0
}

@Entity["nr_posts"]
public class _NrPost {
    @Id[]
    public var id: Int64 = 0
    @Column[]
    public var title: String = ""
    // to-one:本表持 FK authorId 指向 user.id。
    @ManyToOne[_NrUser, "nr_users", fk = "authorId"]
    public var author: ?_NrUser = None
    // 集合:comments 的 postId 反向指回本帖。
    @OneToMany[_NrComment, "postId", "nr_comments"]
    public var comments: Array<_NrComment> = Array<_NrComment>()
}

func _nrSetup(): (DataSource, Repository<_NrUser>, Repository<_NrProfile>, Repository<_NrPost>, Repository<_NrComment>) {
    let ds = DataSource(SqliteDriver(":memory:"))
    let userRepo = Repository<_NrUser>(ds, _NrUserMapper())
    let profileRepo = Repository<_NrProfile>(ds, _NrProfileMapper())
    let postRepo = Repository<_NrPost>(ds, _NrPostMapper())
    let commentRepo = Repository<_NrComment>(ds, _NrCommentMapper())
    profileRepo.createTable()
    userRepo.createTable()
    postRepo.createTable()
    commentRepo.createTable()
    return (ds, userRepo, profileRepo, postRepo, commentRepo)
}

// 造一条 profile→user(持 profileId)→post(持 authorId)的到一链,返回各 id。
func _nrSeedChain(userRepo: Repository<_NrUser>, profileRepo: Repository<_NrProfile>, postRepo: Repository<_NrPost>): (
    Int64, Int64, Int64) {
    let prof = _NrProfile()
    prof.bio = "loaded-bio"
    let profId = profileRepo.insert(prof)

    let u = _NrUser()
    u.name = "Alice"
    u.profile = Some(prof) // owner 持 FK profileId
    let uid = userRepo.insert(u)

    let post = _NrPost()
    post.title = "Hello"
    post.author = Some(u) // 本表持 FK authorId
    let pid = postRepo.insert(post)
    return (profId, uid, pid)
}

@Test
func test_nested_two_level_to_one() {
    let (ds, userRepo, profileRepo, postRepo, _) = _nrSetup()
    let (_, uid, pid) = _nrSeedChain(userRepo, profileRepo, postRepo)

    // 两层 to-one 装填:author + author.profile。
    let loaded = postRepo.findOne(DbInt(pid), ["author.profile"]).getOrThrow()
    @Expect(loaded.title, "Hello")
    @Expect(loaded.author.isSome(), true)
    let author = loaded.author.getOrThrow()
    @Expect(author.name, "Alice")
    @Expect(author.id, uid)
    // 嵌套段:author.profile 也被装填。
    @Expect(author.profile.isSome(), true)
    @Expect(author.profile.getOrThrow().bio, "loaded-bio")

    ds.driver.close()
}

@Test
func test_nested_single_level_backward_compat() {
    let (ds, userRepo, profileRepo, postRepo, _) = _nrSetup()
    let (_, _, pid) = _nrSeedChain(userRepo, profileRepo, postRepo)

    // 单层(无点号):只装 author,不误装 author.profile。
    let loaded = postRepo.findOne(DbInt(pid), ["author"]).getOrThrow()
    @Expect(loaded.author.isSome(), true)
    @Expect(loaded.author.getOrThrow().name, "Alice")
    // 未请求嵌套段 → profile 保持 None。
    @Expect(loaded.author.getOrThrow().profile.isNone(), true)

    ds.driver.close()
}

@Test
func test_nested_first_segment_dedup() {
    let (ds, userRepo, profileRepo, postRepo, _) = _nrSetup()
    let (_, _, pid) = _nrSeedChain(userRepo, profileRepo, postRepo)

    // 首段分组去重:["author.profile","author"] 只装 author 一次且嵌套仍正确。
    let loaded = postRepo.findOne(DbInt(pid), ["author.profile", "author"]).getOrThrow()
    @Expect(loaded.author.isSome(), true)
    @Expect(loaded.author.getOrThrow().name, "Alice")
    @Expect(loaded.author.getOrThrow().profile.isSome(), true)
    @Expect(loaded.author.getOrThrow().profile.getOrThrow().bio, "loaded-bio")

    ds.driver.close()
}

@Test
func test_nested_missing_intermediate() {
    let (ds, _, _, postRepo, _) = _nrSetup()
    // 无 author(FK 为空)的帖子:嵌套路径不抛异常、author 保持 None。
    let post = _NrPost()
    post.title = "Orphan"
    let pid = postRepo.insert(post)

    let loaded = postRepo.findOne(DbInt(pid), ["author.profile"]).getOrThrow()
    @Expect(loaded.title, "Orphan")
    @Expect(loaded.author.isNone(), true)

    ds.driver.close()
}

@Test
func test_nested_mixed_path_with_collection() {
    let (ds, userRepo, profileRepo, postRepo, commentRepo) = _nrSetup()
    let (_, _, pid) = _nrSeedChain(userRepo, profileRepo, postRepo)
    for (t in ["c1", "c2"]) {
        let c = _NrComment()
        c.text = t
        c.postId = pid
        commentRepo.insert(c)
    }

    // 混合路径:to-one 链(author.profile)+ 集合叶子(comments)同时装填。
    let loaded = postRepo.findOne(DbInt(pid), ["author.profile", "comments"]).getOrThrow()
    @Expect(loaded.author.isSome(), true)
    @Expect(loaded.author.getOrThrow().profile.getOrThrow().bio, "loaded-bio")
    @Expect(loaded.comments.size, 2)

    ds.driver.close()
}