package aceboot::orm.common

/**
 * 事务作用域(对标 TypeORM EntityManager 在 `transaction(em => ...)` 回调里暴露的句柄)。
 * 由 EntityManager.transaction 注入回调,内部操作共享同一数据源/连接/事务边界:
 * 任一操作抛异常即整体回滚(由外层 DataSource.transaction 兜底)。
 * 这里的 save/insert/update/deleteById 仅是跨实体仓储的统一委托——仓储必须绑定到同一数据源
 * (@Entity 宏生成的仓储默认用全局数据源,与 EntityManager.global 一致)。
 */
public class TransactionScope {
    let ds: DataSource

    init(ds: DataSource) {
        this.ds = ds
    }

    /** 暴露事务所在数据源(需要直接走 driver/自定义 SQL 时用)。 */
    public func dataSource(): DataSource {
        ds
    }

    /** 在事务内持久化(空主键 insert,否则 update),返回主键。 */
    public func save<T>(repo: Repository<T>, e: T): Int64 {
        repo.save(e)
    }

    /** 在事务内插入,返回自增主键。 */
    public func insert<T>(repo: Repository<T>, e: T): Int64 {
        repo.insert(e)
    }

    /** 在事务内按主键更新。 */
    public func update<T>(repo: Repository<T>, e: T): Unit {
        repo.update(e)
    }

    /** 在事务内按主键删除。 */
    public func deleteById<T>(repo: Repository<T>, id: DbValue): Unit {
        repo.deleteById(id)
    }
}

/**
 * 实体管理器(对标 TypeORM EntityManager):跨实体在同一事务里编排写操作。
 * `em.transaction { tx => tx.save(repoA, a); tx.save(repoB, b) }` 把回调包进数据源事务,
 * 全部操作共用同一连接/事务边界——任一步抛异常则整体回滚。
 * 通过 EntityManager.global() 绑定全局数据源(与宏生成的无参仓储一致)。
 */
public class EntityManager {
    let ds: DataSource

    public init(ds: DataSource) {
        this.ds = ds
    }

    /** 绑定全局数据源的管理器(与 @Entity 宏生成的无参仓储同源)。 */
    public static func global(): EntityManager {
        EntityManager(globalDataSource())
    }

    /** 暴露底层数据源。 */
    public func dataSource(): DataSource {
        ds
    }

    /**
     * 在一个事务里执行 body(注入 TransactionScope):成功 commit,异常 rollback 并重抛。
     * 跨实体原子性由共享连接保证(各仓储须绑定同一数据源)。
     */
    public func transaction(body: (TransactionScope) -> Unit): Unit {
        let scope = TransactionScope(ds)
        ds.transaction({=> body(scope)})
    }
}

/**
 * 查询执行器(对标 TypeORM QueryRunner):手动 begin/commit/rollback,绑定一条连接。
 * 当前实现绑定数据源的共享连接(单线程/共享连接驱动适用);连接获取/释放接口为
 * 未来连接池(F7)预留——`release()` 现为空操作,池化后从池借/还连接。
 * 典型用法:
 *   let qr = QueryRunner.global()
 *   qr.startTransaction()
 *   try { repo.insert(a); repo.insert(b); qr.commitTransaction() }
 *   catch (e: Exception) { qr.rollbackTransaction(); throw e }
 *   finally { qr.release() }
 */
public class QueryRunner {
    let ds: DataSource
    var active: Bool = false

    public init(ds: DataSource) {
        this.ds = ds
    }

    /** 绑定全局数据源的执行器。 */
    public static func global(): QueryRunner {
        QueryRunner(globalDataSource())
    }

    /** 绑定的连接驱动(后续操作经它执行,保证同一事务边界)。 */
    public func connection(): Driver {
        ds.driver
    }

    /** 数据源句柄。 */
    public func dataSource(): DataSource {
        ds
    }

    /** 当前是否处于活动事务中。 */
    public func isTransactionActive(): Bool {
        active
    }

    /** 开启事务(driver.begin)。重复开启抛 OrmException。 */
    public func startTransaction(): Unit {
        if (active) {
            throw OrmException("QueryRunner: 事务已开启,不能重复 startTransaction")
        }
        ds.driver.begin()
        active = true
    }

    /** 提交事务(driver.commit)。未开启事务抛 OrmException。 */
    public func commitTransaction(): Unit {
        if (!active) {
            throw OrmException("QueryRunner: 无活动事务,无法 commit")
        }
        ds.driver.commit()
        active = false
    }

    /** 回滚事务(driver.rollback)。未开启事务抛 OrmException。 */
    public func rollbackTransaction(): Unit {
        if (!active) {
            throw OrmException("QueryRunner: 无活动事务,无法 rollback")
        }
        ds.driver.rollback()
        active = false
    }

    /**
     * 释放连接(归还连接池)。当前为共享连接,空操作;连接池(F7)接入后从此归还。
     * 若仍在事务中释放,先回滚以免悬挂事务。
     */
    public func release(): Unit {
        if (active) {
            ds.driver.rollback()
            active = false
        }
    }
}