package cn.sagacloud.server.algorithm.backstage.state

import cn.sagacloud.server.algorithm.backstage.request.task.TaskCmd
import cn.sagacloud.server.algorithm.models.excel.report.BlockingTotalReport
import cn.sagacloud.server.algorithm.models.message.MessageEntity
import cn.sagacloud.server.algorithm.models.message.MessageType
import cn.sagacloud.server.algorithm.models.modelFile.FloorModel
import cn.sagacloud.server.algorithm.models.modelFile.ModelFile
import cn.sagacloud.server.algorithm.models.modelFile.TaskEntity
import cn.sagacloud.server.algorithm.models.scheduler.DataCheckResult
import cn.sagacloud.server.algorithm.models.scheduler.SagaCheckResult
import cn.sagacloud.server.algorithm.models.scheduler.UnitCheckResult
import cn.sagacloud.server.algorithm.services.BaseDataService
import cn.sagacloud.server.algorithm.services.CommonService
import cn.sagacloud.server.algorithm.services.excel.ExcelService
import cn.sagacloud.server.algorithm.services.message.MessageService
import cn.sagacloud.server.algorithm.services.modelCheck.TaskService
import cn.sagacloud.server.algorithm.services.monitor.ModelMonitorService
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.sybotan.database.SFilter
import com.sybotan.service.utils.SSpringContextUtil
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap

/**
 *
 *
 * @author jxing
 */
@Component
class DataCheckState_10 : ModelState {
    companion object {
        // 日志
        private val logger = LoggerFactory.getLogger(DataCheckState_10::class.java)
    } // Companion object

    val dataService = BaseDataService

    @Autowired
    lateinit var taskService: TaskService

    @Autowired
    lateinit var messageService: MessageService

    @Autowired
    lateinit var monitorService: ModelMonitorService

    var modelList: ArrayList<ModelFile>? = null

    /**
     * 准备数据
     */
    override fun prepareModel() {
        modelList = dataService.modelFileService.select(SFilter.eq("status", 10), SFilter.eq("removed", false)).exec()
    }

    /**
     * 检查模型文件在revit调度器内执行的状态, 做相应处理
     */
    override fun handle() {
        try {
            if (modelList.isNullOrEmpty()) {
                return
            }
            val taskList = taskService.queryTask(modelList!!, TaskCmd.DataCheck)
            if (taskList == null) {
                logger.warn("状态10处理失败!")
                return
            }
            //val map = composeModelIdTaskIdMap(modelList!!, taskList)
            var map = HashMap<String, TaskEntity>() // <modelId, taskStatus>
            val updateList = ArrayList<ModelFile>()
            taskList.forEach {
                if (it.modelId != null && it.status != null)
                    map[it.modelId!!] = it
            }
            modelList!!.forEach {
                try {
                    if (!map.containsKey(it.id)) {
                        return@forEach
                    }
                    // 0 Waiting, 1 Sending, 2 Sent, 3 FileDownloadException, 4 CommandExecuteException, 5 Finished
                    val excelService = SSpringContextUtil.getBean(ExcelService::class.java) as ExcelService
                    excelService.readBlockingExcel2007()
                    // excel总的第一页, 总报告
                    val totalReport = SSpringContextUtil.getBean(BlockingTotalReport::class.java) as BlockingTotalReport
                    // 获取楼层
                    val floorList = dataService.floorModelService.select(SFilter.eq("id", it.floorModelId!!)).exec()
                    val floor = floorList[0]

                    val task = map[it.id]
                    it.taskInfo!!.checkTask = task
                    var noReport = false   // 是否出报告
                    var noReportMsg: String? = null   // 不出报告的原因
                    val checkTime = Date()
                    var singleModelCheckBegin : Date? = null
                    var singleModelCheckEnd : Date? = null
                    monitorService.endSchedulerCheckDownload(it.id!!, task!!.downloadFinishTime)
                    when (task!!.status) {
                        2 -> {
                            //monitorService.beginCheck(it.id!!, checkTime)
                        }
                        3 -> {
                            // 失败, 文件下载异常
                            // 将被删除, 因此不记录日志
                            it.status = 11
                            updateList.add(it)
                            noReport = true
                            noReportMsg = "检查中下载模型文件失败!"
                        }
                        4 -> {
                            // 失败, 命令执行异常
                            // 将被删除, 因此不记录日志
                            it.status = 11
                            updateList.add(it)
                            noReport = true
                            noReportMsg = "检查异常!"
                        }
                        5 -> {
                            // 完成
                            val str = task.resultJson
                            var json = JSON.parseObject(str)
                            if (json == null || json.keys.size == 0) {
                                json = JSONObject()
                                json["info"] = str
                                noReport = true
                                noReportMsg = "处理模型文件异常"
                                it.status = 11
                            } else {
                                // 检查DataCheck的结果 (关键检查为: saga标记检查, 项目长度单位检查, 设备/部件的本地名称和本地编码是否完整)
                                if ("success".equals(json["Result"] as String?, ignoreCase = true)) {
                                    // 从调度器获取任务检查结果
                                    val taskResultContent = taskService.queryTaskResult(task.id!!)
                                            ?: throw Exception("访问调度器异常")
                                    if (taskResultContent.isEmpty()) {
                                        noReport = true
                                        noReportMsg = "模型文件数据异常"
                                        it.status = 11  // 数据异常
                                    } else {
                                        try {
                                            var checkItemsCount = 0  // 检查项的个数
                                            var isCheckPassed = true  // 是否都通过了检查
                                            val resultJSON = JSON.parseObject(taskResultContent)
                                            it.dataCheckResult = taskService.hasAllDataCheckResultError(resultJSON)
                                            val result = JSON.toJavaObject(resultJSON, DataCheckResult::class.java)
                                            val iterator = result.content!!.iterator()
                                            while (iterator.hasNext()) {
                                                val singleResult = iterator.next() as JSONObject
                                                // 处理检查结果
                                                singleModelCheckBegin = Date()
                                                val pair = processCheckResult(singleResult, checkItemsCount, isCheckPassed, excelService, floor, totalReport)
                                                singleModelCheckEnd = Date()
                                                checkItemsCount = pair.first
                                                isCheckPassed = pair.second
                                            }
                                            // 写总体报告
                                            totalReport.writeSingleCheckResult(floor.floorName, excelService)
                                            // 如果强制检查都查了
                                            if (checkItemsCount != 2) {
                                                noReport = true
                                                noReportMsg = "模型文件检查异常"
                                            } else {
                                                if (isCheckPassed) {
                                                    it.status = 2
                                                } else
                                                    it.status = 11
                                            }
                                        } catch (e: Exception) {
                                            e.printStackTrace()
                                            return@forEach
                                        }
                                    }
                                } else {
                                    noReport = true
                                    val folderFloorName = CommonService.getReportPrefix(floor)
                                    noReportMsg = "${folderFloorName.first}的${folderFloorName.second}模型检查失败: " + json["ResultMsg"] as String?
                                    it.status = 11
                                }
                            }
                            updateList.add(it)
                        }
                        else -> {
                        }
                    }
                    // 如果检查都通过了
                    val folderFloorName = CommonService.getReportPrefix(floor)
                    val message: MessageEntity
                    if (it.status == 11) {
                        if (noReport) {
                            message = messageService.buildMessageEntityWithoutLink("${folderFloorName.first}的${folderFloorName.second}模型上传失败， 请重新上传", MessageType.Error, it.projectId, arrayListOf(it.userId
                                    ?: ""), "${noReportMsg}, 请联系开发人员")
                        } else {
                            // 将报告保存到文件服务器
                            excelService.saveToFileServer(it.id!!)
                            // 发送检查未通过通知
                            message = messageService.buildCheckErrorMessage(it.projectId, it.userId
                                    ?: "", "${folderFloorName.first}的${folderFloorName.second}未通过模型检查， 请修改后重新上传", "平面图命名和项目长度检查未通过", excelService.getDownloadUrl(it.id!!), folderFloorName)
                        }
                        dataService.modelFileService.update(it, UploadedState_1.projection)
                        monitorService.endCheck(it.id!!, checkTime)
                        monitorService.modelCheck(it.id!!, noReportMsg, true)
                        messageService.sendMessage(message)
                    } else if (it.status == 2) {
                        message = messageService.buildRefreshMessage(it.projectId, it.userId ?: "")
                        dataService.modelFileService.update(it, UploadedState_1.projection)
                        monitorService.endCheck(it.id!!, checkTime)
                        monitorService.modelCheck(it.id!!, singleModelCheckBegin!!, singleModelCheckEnd!!)
                        messageService.sendMessage(message)
                    }
                } catch (e: Exception) {
                    monitorService.modelCheck(it.id!!, e.message + CommonService.getStackTrace(e), false)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            logger.error(e.message)
        }
    }

    /**
     * 处理检查结果, 写excel
     */
    private fun processCheckResult(singleResult: JSONObject, checkItemsCount: Int, isCheckPassed: Boolean, excelService: ExcelService, floor: FloorModel, totalReport: BlockingTotalReport): Pair<Int, Boolean> {
        var checkItemsCount1 = checkItemsCount
        var isCheckPassed1 = isCheckPassed
        when (singleResult.getString("Name")) {
            // saga标记检查
            "SagaCheck" -> {
                // 代码是否挪到SagaCheck里
                checkItemsCount1++
                val sagaCheck = JSON.toJavaObject(singleResult, SagaCheckResult::class.java)
                // 写sagaCheck
                isCheckPassed1 = isCheckPassed1 && sagaCheck.writeReport(excelService, floor.floorName, totalReport)
            }
            // 单位检查, 是否是mm
            "UnitCheck" -> {
                checkItemsCount1++
                val unitCheck = JSON.toJavaObject(singleResult, UnitCheckResult::class.java)
                isCheckPassed1 = isCheckPassed1 && unitCheck.writeReport(excelService, floor.floorName, totalReport)
            }
        }
        return Pair(checkItemsCount1, isCheckPassed1)
    }
}