package cn.sagacloud.server.algorithm.controllers

import cn.sagacloud.server.algorithm.backstage.sync.SyncFloor
import cn.sagacloud.server.algorithm.backstage.sync.SyncUtil
import cn.sagacloud.server.algorithm.common.utils.SpaceAffectUtil
import cn.sagacloud.server.algorithm.models.config.MyProperties
import cn.sagacloud.server.algorithm.models.jsonAnalyzer.AllComponents
import cn.sagacloud.server.algorithm.models.jsonAnalyzer.BaseObj
import cn.sagacloud.server.algorithm.models.message.MessageType
import cn.sagacloud.server.algorithm.models.modelFile.ModelFile
import cn.sagacloud.server.algorithm.models.modelFile.StatusInfo
import cn.sagacloud.server.algorithm.models.monitor.MntSaveData
import cn.sagacloud.server.algorithm.models.monitor.ModelMonitor
import cn.sagacloud.server.algorithm.services.BaseDataService
import cn.sagacloud.server.algorithm.services.CommonService
import cn.sagacloud.server.algorithm.services.datacenter.DataCenterService
import cn.sagacloud.server.algorithm.services.endpoint.ModelFileService
import cn.sagacloud.server.algorithm.services.excel.ExcelService
import cn.sagacloud.server.algorithm.services.json.JSONAnalyzerService
import cn.sagacloud.server.algorithm.services.message.MessageService
import cn.sagacloud.server.algorithm.services.modelCheck.ModelCheckService
import cn.sagacloud.server.algorithm.services.monitor.ModelMonitorService
import com.alibaba.fastjson.JSONObject
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.sybotan.database.SFilter
import com.sybotan.database.enums.SDirection
import com.sybotan.service.models.enums.SResponseType
import com.sybotan.service.models.requests.SUpdateRequest
import com.sybotan.service.utils.SSpringContextUtil
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.util.*

/**
 * 模型解析后数据测试
 *
 * @author wx <zhangweixin@sagacloud.cn>
 * @date 2021/9/1811:10
 */
@Api(tags = ["测试检查模型解析文件"])
@RestController
@RequestMapping("/test/model")
class TestModelController {

    companion object {
        // 日志
        private val logger = LoggerFactory.getLogger(TestModelController::class.java)
    } // Companion object

    @Autowired
    lateinit var myProperties: MyProperties

    @Autowired
    lateinit var syncUtil : SyncUtil

    @Autowired
    lateinit var modelFileService: ModelFileService

    @Autowired
    lateinit var messageService: MessageService

    @Autowired
    lateinit var modelCheckService: ModelCheckService

    @Autowired
    lateinit var jsonAnalyzerService: JSONAnalyzerService

    @Autowired
    lateinit var monitorService: ModelMonitorService

    @Autowired
    lateinit var dataCenterService: DataCenterService

    @Autowired
    lateinit var spaceAffectUtil: SpaceAffectUtil

    val dataService = BaseDataService

    var modelList: ArrayList<ModelFile>? = null

    /**
     *
     */
    @ApiOperation(value = "检查模型解析文件", notes = "")
    @PostMapping(value = ["/test/data"])
    fun testModelData(floorModelId: String?, id: String?){
        try {
            val modelFiles = dataService.modelFileService.select(SFilter.eq("id", id!!),
                    SFilter.eq("removed", false)).exec()
            val it = modelFiles[0]
//            if (it.floorModelId == null || it.id == null)
//                return@forEach
//            val floorModelArr = dataService.floorModelService.select(SFilter.eq("id", it.floorModelId!!)).exec()
//            if (floorModelArr.size != 1)
//                return@forEach
//            val floor = floorModelArr[0]
//            val folderFloorName = CommonService.getReportPrefix(floor)
//            val fileInputStream = FileInputStream(File("D:\\testModel\\b52612021546a9431eb1a38ca02d3b1b.rvt_20210917181820.json"))
            //var file = FileInputStream("D:\\testModel\\b91a63278a322ca15f138d25133c9b94.rvt_20211018100336.json")
            //var byteArr = file.readBytes()

            //val str = String(byteArr, charset("utf-8"))
            //val objData =  handleSingleJSON(JSONObject.parseObject(str)) //JSONObject(str)

//            logger.debug("model = ${obj.toJson()}")


              // 导入数据进数据库
//            val bimIdPrefix = CommonService.generateBIMIDPrefix(it.projectId!!, floor.folderId!!, floor.floorName!!, true)
            try {
                if (it.floorModelId == null || it.id == null)
                    return
                val floorModelArr = dataService.floorModelService.select(SFilter.eq("id", it.floorModelId!!)).exec()
                if (floorModelArr.size != 1)
                    return
                val floor = floorModelArr[0]
                val folderFloorName = CommonService.getReportPrefix(floor)
                // 获取模型导出的zip文件
                val objData = monitorService.analyzeJsonz(it.id!!)
                // 导入数据进数据库
                val bimIdPrefix = CommonService.generateBIMIDPrefix(it.projectId!!, floor.folderId!!, floor.floorName!!, true)
                monitorService.persistenceData(it.id!!, objData!!, bimIdPrefix)
                // excel报告
                val excelService = SSpringContextUtil.getBean(ExcelService::class.java) as ExcelService
                excelService.readBlockingExcel2007()
                // 多楼层的模型检查
                var isPassed = monitorService.doMultiFloorCheck(it, excelService, floor.projectId!!, floor.folderId!!, floor.floorName)
                // 如果标高和轴网检查没通过, 则删除所有数据, 并回退到上个版本
                /************************************************** 轴网计算 处理标志 *********************************************************************/
                if(!isPassed){
                    modelFileService.removeSingle(it, myProperties, isForce = true, isMarkDelete = false)
                    //发送标高和轴网检查没通过的消息
                    val message = messageService.buildMultiModelCheckErrorMessage(floor.projectId!!, it.userId?: "",
                            "${folderFloorName.first}的${folderFloorName.second}未通过模型检查，请修改后重新上传", excelService.getDownloadUrl(it.id!!), folderFloorName)
                    messageService.sendMessage(message)
                    return
                }

                val syncFloor = SSpringContextUtil.getBean(SyncFloor::class.java) as SyncFloor
                syncFloor.init(floor).prepareDataCenterData()
                // 开始同步设备, 部件, 元空间
                syncUtil.syncDataWithBindingFloor(syncFloor, it, floor)
                // 转状态到4
                it.status = 4
                dataService.modelFileService.update(it, arrayListOf("status"))
                val message = messageService.buildMessageEntityWithoutLink("${folderFloorName.first}的${folderFloorName.second}模型上传成功", MessageType.Success, it.projectId, arrayListOf(it.userId?:""), "")
                messageService.sendMessage(message)
            } catch (e: Exception) {
                // 转状态到31
                try {
                    it.status = 31
                    it.syncRetryDate = Date()
                    it.syncErrorMsg = e.message + CommonService.getStackTrace(e)
                    dataService.modelFileService.update(it, arrayListOf("status", "syncRetryDate", "syncErrorMsg"))
                }catch (e : Exception){}
                e.printStackTrace()
            }
//            // excel报告
//            val excelService = SSpringContextUtil.getBean(ExcelService::class.java) as ExcelService
//            excelService.readBlockingExcel2007()
//            // 多楼层的模型检查
//            var isPassed = monitorService.doMultiFloorCheck(it, excelService, floor.projectId!!, floor.folderId!!, floor.floorName)
//            // 如果标高和轴网检查没通过, 则删除所有数据, 并回退到上个版本
//            /************************************************** 轴网计算 处理标志 *********************************************************************/
//            if(!isPassed){
//                modelFileService.removeSingle(it, myProperties, isForce = true, isMarkDelete = false)
//                //发送标高和轴网检查没通过的消息
//                val message = messageService.buildMultiModelCheckErrorMessage(floor.projectId!!, it.userId?: "",
//                    "${folderFloorName.first}的${folderFloorName.second}未通过模型检查，请修改后重新上传", excelService.getDownloadUrl(it.id!!), folderFloorName)
//                messageService.sendMessage(message)
//                return@forEach
//            }
//
//            val syncFloor = SSpringContextUtil.getBean(SyncFloor::class.java) as SyncFloor
//            syncFloor.init(floor).prepareDataCenterData()
//            // 开始同步设备, 部件, 元空间
//            syncUtil.syncDataWithBindingFloor(syncFloor, it, floor)
//            // 转状态到4
//            it.status = 4
//            dataService.modelFileService.update(it, arrayListOf("status"))
//            val message = messageService.buildMessageEntityWithoutLink("${folderFloorName.first}的${folderFloorName.second}模型上传成功", MessageType.Success, it.projectId, arrayListOf(it.userId?:""), "")
//            messageService.sendMessage(message)
        } catch (e: Exception) {
            // 转状态到31
            try {
//                it.status = 31
//                it.syncRetryDate = Date()
//                it.syncErrorMsg = e.message + CommonService.getStackTrace(e)
//                dataService.modelFileService.update(it, arrayListOf("status", "syncRetryDate", "syncErrorMsg"))
            }catch (e : Exception){}
            e.printStackTrace()
//            ExportPassedState_3.logger.error(e.message)
        }


    }

    @ApiOperation(value = "检查模型解析文件", notes = "")
    @PostMapping(value = ["/test/finishState"])
    fun finishState(floorModelId: String?, id: String?){
        try {
            val modelList = dataService.modelFileService.select(SFilter.eq("id", id!!),
                    SFilter.eq("removed", false)).exec()
            if (modelList.isNullOrEmpty()) {
                return
            }
            val it = modelList[0]
            // 先给模型文件版本号和接收时间
            addVersionChangeName(modelList!!)
            modelList!!.forEach {
                try {
                    // 获取模型的statusInfo的affectedSpace是否是true
                    // 如果不是true
                    if (it.statusInfo == null || it.statusInfo!!.affectedSpace != true) {
                        if (it.floorModelId == null) {
                            return@forEach
                        }
                        val floorModel = dataService.floorModelService.select(SFilter.and(SFilter.eq("currentModelId", it.id!!), SFilter.eq("id", it.floorModelId!!))).entity()
                                ?: return@forEach
                        // 判断数据中心是否有楼层绑定了该模型
                        val floors = dataCenterService.getFloorWithModelId(it.floorModelId!!, it.projectId!!)
                        // 如果有绑定, 则执行受影响的业务空间函数
                        if (!floors.isNullOrEmpty()) {
                            val calcAffected = spaceAffectUtil.affectedSpace(it.projectId!!, it.floorModelId!!)
//                                floorMapper.calcAffected(it.floorModelId!!)
                            // 如果返回true, 更新模型的statusInfo信息点
                            if (calcAffected == true) {
                                it.statusInfo = StatusInfo()
                                it.statusInfo!!.affectedSpace = true
                                dataService.modelFileService.update(it, arrayListOf("statusInfo"))
                            }
                        }
                    }
                }catch (ex : java.lang.Exception){
                    ex.printStackTrace()
                }
            }
        } catch (e: Exception) {
            // 转状态到31
            try {
            }catch (e : Exception){}
            e.printStackTrace()
        }


    }

    private val projection = arrayListOf("version", "acceptTime")
    private fun addVersionChangeName(modelList: java.util.ArrayList<ModelFile>) {
        modelList.forEach {
            // 检验数据合法性
            if(it.floorModelId == null || it.floorModelId!!.isEmpty())
                return@forEach
            if(it.version != null)  // 已经改过的忽略掉
                return@forEach
            val largestVersion = dataService.modelFileService.select(SFilter.and(SFilter.eq("floorModelId", it.floorModelId!!), SFilter.not(SFilter.isNull("version")), SFilter.eq("removed", false))).order("version", SDirection.DESC).limit(1).entity()
            if(largestVersion == null){
                it.version = 1
            }else {
                it.version = largestVersion.version!! + 1
            }
            it.acceptTime = Date()
            val request = SUpdateRequest<ModelFile>()
            request.content = arrayListOf(it)
            request.projection = projection
            dataService.modelFileService.updateList(request)
        }
    }

    fun handleSingleJSON(json: JSONObject): BaseObj<AllComponents>? {
        val mapper = ObjectMapper()
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        val baseObj = mapper.readValue<BaseObj<AllComponents>>(json.toString(), mapper.typeFactory.constructParametricType(
            BaseObj::class.java, AllComponents::class.java))
        return baseObj
    }


    /**
     * 持久化数据
     */
    fun persistenceData(id: String, objData: BaseObj<AllComponents>, bimIdPrefix : String) {
        beginPersistenceData(id)

        try {
            val importResult = jsonAnalyzerService.importModelData(objData, bimIdPrefix)
            if(importResult.result == SResponseType.failure){
                endPersistenceDataExceptionally(id, importResult.message)
                throw Exception(importResult.message)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        endPersistenceData(id)
    }

    /**
     * 开始持久化数据
     */
    private fun beginPersistenceData(id: String) {
        val modelMonitor = getModelMonitorById(id)
        if(modelMonitor.saveData!= null && modelMonitor.saveData!!.beginTime != null)
            return
        modelMonitor.saveData = MntSaveData()
        modelMonitor.saveData!!.beginTime = Date()
        dataService.modelMonitorService.update(modelMonitor, arrayListOf("saveData"))
    }

    /**
     * 持久化数据异常
     */
    private fun endPersistenceDataExceptionally(id: String, errorInfo: String) {
        val modelMonitor = getModelMonitorById(id)
        modelMonitor.saveData!!.hasError = true
        modelMonitor.saveData!!.errorMsg = errorInfo
        dataService.modelMonitorService.update(modelMonitor, arrayListOf("saveData"))
    }

    /**
     * 获取后台监控信息by id
     */
    fun getModelMonitorById(modelId : String): ModelMonitor {
        var modelMonitor = dataService.modelMonitorService.select(SFilter.Companion.eq("id", modelId)).entity()
        if(modelMonitor == null)
        {
            modelMonitor = ModelMonitor()
            modelMonitor.id = modelId
            dataService.modelMonitorService.insert(modelMonitor)
        }
        return modelMonitor
    }

    /**
     * 正常结束持久化
     */
    private fun endPersistenceData(id: String) {
        val modelMonitor = getModelMonitorById(id)
        if(modelMonitor.saveData != null && modelMonitor.saveData!!.endTime != null) {
            return
        }
        modelMonitor.saveData!!.endTime = Date()
        modelMonitor.saveData!!.hasError = false
        dataService.modelMonitorService.update(modelMonitor, arrayListOf("saveData"))
    }


}