package org.rocfish.dao

import io.vertx.core.json.JsonObject
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import io.vertx.ext.mongo.MongoClient
import org.boot.XSpringContext
import org.rocfish.IDSpecs
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.data.mongodb.core.MongoTemplate

interface RocfishDao<T:IDSpecs> {
    fun save(t: T)

    /**
     * 直接传递需要更新的数据，执行更新。例如:只更新指定域
     */
    fun updateField(updated:JsonObject, condition:JsonObject)

    fun load(idOrName:String):T?

    fun delete(idOrName:String)

    fun getSearcher():Searcher<T>
}

interface AsyncRocfishDao<T:IDSpecs> {
    suspend fun asyncSave(t: T)

    suspend fun asyncLoad(id:String):T?

    suspend fun asyncDelete(id:String)

    suspend fun asyncGetSearcher():Searcher<T>
}

/**
 * 抽象访问mongo的DAO对象，它是访问数据存储的facade接口,他同时实现了异步dao接口，
 * 通常来说，底层对象应该都使用同步方法，这样便于扩展和利用对象的继承、模式等特性，
 * 在控制器、服务等上层应该使用异步方法。如果底层使用了异步函数，则基本通篇都需要
 * 使用异步！
 */
@Component("abstract-async-dao")
abstract class AbstractDao<T: IDSpecs>:AsyncRocfishDao<T>, RocfishDao<T> {

    companion object {
        val logger: Logger = LoggerFactory.getLogger(AbstractDao::class.java)
    }

    @Autowired
    lateinit var asyncMongo: MongoClient

    @Autowired
    lateinit var mongoTemplate: MongoTemplate

    @Autowired
    lateinit var searcherFactory:SearcherFactory

    lateinit var searcherName:String

    val vertx by lazy {
        XSpringContext.vertx
    }

    override suspend fun asyncSave(t: T) {
        this.save(t)
    }

    override suspend fun asyncDelete(id: String) {
        this.delete(id)
    }

    override suspend fun asyncGetSearcher(): Searcher<T> = this.getSearcher()

    override suspend fun asyncLoad(id: String): T? = this.load(id)

    protected abstract fun getCollection():String

    override fun updateField(updated:JsonObject, condition:JsonObject) {
        this.asyncMongo.updateCollection(getCollection(), condition, updated) {
            if (it.succeeded()) {
                logger.info("更新条件为：$condition 的数据：$updated 成功！")
            } else {
                logger.error("更新条件为：$condition 的数据：$updated 失败！", it.cause())
            }
        }
    }

    override fun getSearcher(): Searcher<T> {
        return XSpringContext.XContext.getBean(this.searcherName) as Searcher<T>
    }
}


