/*
 * ********************************************************************************************************************
 *
 *                      :*$@@%$*:                         ;:                ;;    ;;
 *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
 *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
 *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
 *                   =@*                                  %!              @ $= % %@=   =%@!      %=
 *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
 *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
 *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
 *          $@*   ;@@@%=!:                *@*
 *          =@$    ;;;!=%@@@@=!           =@!
 *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
 *            ;%@@$=$@@%*       *@@@$=%@@%;
 *               ::;::             ::;::                                              All rights reserved.
 *
 * ********************************************************************************************************************
 */
package cn.sagacloud.server.algorithm.controllers.modelFile

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.modelFile.ModelFile
import cn.sagacloud.server.algorithm.models.request.MergeMultipartRequest
import cn.sagacloud.server.algorithm.models.request.UploadModelRequest
import cn.sagacloud.server.algorithm.models.responses.UploadModelResponse
import cn.sagacloud.server.algorithm.services.BaseDataService
import cn.sagacloud.server.algorithm.services.CommonService
import cn.sagacloud.server.algorithm.services.activemq.JMSProducer
import cn.sagacloud.server.algorithm.services.endpoint.ModelFileService
import cn.sagacloud.server.algorithm.services.excel.ExcelService
import cn.sagacloud.server.algorithm.services.http.HttpService
import cn.sagacloud.server.algorithm.services.message.MessageService
import cn.sagacloud.server.algorithm.services.monitor.ModelMonitorService
import cn.sagacloud.server.algorithm.services.json.JSONAnalyzerService
import com.sybotan.database.SFilter
import com.sybotan.service.SPageContext
import com.sybotan.service.models.enums.SResponseType
import com.sybotan.service.models.requests.SCountRequest
import com.sybotan.service.models.requests.SCreateRequest
import com.sybotan.service.models.requests.SQueryRequest
import com.sybotan.service.models.requests.SUpdateRequest
import com.sybotan.service.models.responses.SBaseResponse
import com.sybotan.service.models.responses.SCountResponse
import com.sybotan.service.models.responses.SCreateResponse
import com.sybotan.service.models.responses.SQueryResponse
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.*
import java.util.*

/**
 *
 *
 * @author jxing
 */
@Api(tags = ["模型文件接口"])
@RestController
@RequestMapping("/model-file")
class ModelFileController {
    companion object {
        // 日志
        private val logger = LoggerFactory.getLogger(ModelFileController::class.java)
    } // Companion object


    var dataService = BaseDataService

    @Autowired
    lateinit var properties : MyProperties

    @Autowired
    lateinit var modelFileService: ModelFileService

    @Autowired
    lateinit var messageService: MessageService

    @Autowired
    lateinit var excelService: ExcelService

    @Autowired
    lateinit var monitorService: ModelMonitorService

    @Autowired
    lateinit var jsonAnalyzerService : JSONAnalyzerService

    /**
     * 删除模型文件(只有记录, 文件不动)
     *
     * @param   idList      删除列表
     * @return  删除结果应答信息
     */
    @ApiOperation(value = "删除模型文件(只有记录, 文件不动)", notes = "")
    @PostMapping(value = ["/delete"])
    fun delete(@RequestBody idList: ArrayList<ModelFile>): SBaseResponse {
        return dataService.modelFileService.deleteByKeysList(idList)
    } // Function delete()

    /**
     * 删除模型文件 (真删除模型文件, 模型文件记录做删除标记, 有个别情况不允许删除文件)
     *
     * @param   modelFile      要删除的模型文件
     * @return  删除结果应答信息
     */
    @ApiOperation(value = "删除模型文件 (真删除模型文件, 模型文件记录做删除标记, 有个别情况不允许删除文件)", notes = "")
    @PostMapping(value = ["/delete-file"])
    fun deleteFile(@RequestBody modelFile: ModelFile): SBaseResponse {
        return modelFileService.removeSingle(modelFile, properties, isForce = modelFile.isForce?:false, isMarkDelete = true)
    } // Function delete()


    /**
     * 更新模型记录, 并非文件
     *
     * @param   request     更新请求
     * @return  更新结果应答信息
     */
    @ApiOperation(value = "更新模型记录, 并非文件", notes = "")
    @PostMapping(value = ["/update"])
    fun update(@RequestBody request: SUpdateRequest<ModelFile>): SBaseResponse {
        return dataService.modelFileService.updateList(request)
    } // Function update()

    /**
     * 查询模型文件夹
     *
     * @param   request     查询请求
     * @return  查询结果应答信息
     */
    @ApiOperation(value = "查询模型文件", notes = "")
    @PostMapping(value = ["/query"])
    fun query(@RequestBody request: SQueryRequest): SQueryResponse<ModelFile> {
        if(SPageContext.getHeader("ProjectId") != null && SPageContext.getHeader("ProjectId")!!.isNotEmpty())
            return dataService.modelFileService.pageQuery(request, arrayListOf(SFilter.eq("projectId", SPageContext.getHeader("ProjectId").toString())))
        return dataService.modelFileService.pageQuery(request)
    } // Function query()

    /**
     * 创建模型文件
     *
     * @param   entity     实体
     * @return  创建结果应答信息
     */
    @ApiOperation(value = "创建模型文件", notes = "")
    @PostMapping(value = ["/create"])
    fun create(@RequestBody entity: SCreateRequest<ModelFile>): SCreateResponse<ModelFile> {
        entity.content.stream().forEach { x ->
            x.id = CommonService.generateUUID("", true)
            x.projectId = SPageContext.getHeader("ProjectId")
        }
        return dataService.modelFileService.createList(entity)
    } // Function create()

//    /**
//     * 上传模型文件接口
//     */
//    @PostMapping("/uploadaa")
//    @ResponseBody
//    @ApiOperation(value = "上传模型文件接口", notes = "")
//    fun multiUpload(@RequestParam("file") file: MultipartFile, @RequestParam("model") modelFile: ModelFile): SBaseResponse {
//        try {
//            // 检查是否可以上传(所属楼层模型是否存在, 是否有人正在上传模型), 检测文件是否正常
//            var boolResult = modelFileService.checkUpdatable(file, modelFile)
//            if (!boolResult) {
//                return SBaseResponse(SResponseType.failure, "当前楼层有正在处理的模型文件, 请等待完成后再上传")
//            }
//            // 添加模型文件记录, 并修改楼层模型的当前模型文件id, 额外添加信息包括md5, 模型url, status
//            if (modelFile.modelName == null)
//                modelFile.modelName = file.originalFilename
//            modelFile.md5 = CommonService.getMD5(file.bytes)
//            modelFile.size = file.bytes.size.toLong()
//            boolResult = modelFileService.addRecord(modelFile, properties)
//            if(!boolResult)
//                throw Exception("服务器异常, 上传失败")
//            // 转发到文件服务器, result = true表示上传成功, false失败
//            val result = modelFileService.uploadFile(file, modelFile.upLoadUrl)
//            val userId = modelFileService.getUserId(modelFile.id!!)
//            if (result) {
//                // 修改模型文件状态到已上传(1)
//                modelFile.status = 1
//                dataService.modelFileService.update(modelFile, arrayListOf("status"))
//                if(modelFile.floorModelId != null) {
//                    val floor = dataService.floorModelService.select(SFilter.eq("id", modelFile.floorModelId!!)).exec()[0]
//                }
//                //return SBaseResponse(SResponseType.success)
//                return SCreateResponse(arrayListOf(modelFile))
//            } else {
//                // 删除本条模型记录
//                // 获取该楼层模型最大的版本号的模型id, 修改到模型楼层的当前模型文件id上
//                modelFileService.backToLastVersion(modelFile)
//                if(modelFile.floorModelId != null) {
//                    val floor = dataService.floorModelService.select(SFilter.eq("id", modelFile.floorModelId!!)).exec()[0]
//                    val message = messageService.buildMessage("上传结果", false, SPageContext.getHeader("ProjectId"), userId
//                            ?: "", "${floor.floorName}模型上传失败, 请重新上传", excelService.getDownloadUrl(modelFile.id!!), floor.floorName)
//                    messageService.sendMessage(message)
//                }
//                return SBaseResponse(SResponseType.failure, "上传出错, 请稍后重新上传")
//            }
//        }catch (e : Exception){
//            return SBaseResponse(SResponseType.failure, e.message?:"")
//        }
//    }

    /**
     * 准备分片上传模型文件接口
     *   if has no floor
     *      createFloor()
     *   if floor.currentModel status not in (21, 4)
     *       throw 当前楼层有正在处理的模型文件, 请等待完成后再上传
     *   createModel()
     *   result = updateFloorCurrentModelId() update where currentModelId = old
     *   if result == failure
     *       return "有其他人正在上传模型"
     *   register uploadId
     *   return uploadId
     */
    @PostMapping("/upload")
    @ResponseBody
    @ApiOperation(value = "准备分片上传模型文件接口", notes = "")
    fun multipartUpload(@RequestBody request: UploadModelRequest): SBaseResponse {
        try {
            modelFileService.checkUploadInputValid(request)
            var floor = dataService.floorModelService.select(SFilter.eq("projectId", request.projectId!!),
                    SFilter.eq("floorName", request.floorName!!), SFilter.eq("folderId", request.folderId!!)).entity()
            // 如果楼层不存在, 则创建
            if(floor == null){
                floor = modelFileService.addFloor(request)
            }
            // 检查是否可以上传新模型
            modelFileService.checkUpdatable(floor, request)

            // 创建模型记录
            var model = modelFileService.addRecord(request, floor, properties)

            // 注册uploadId
            val uploadId = modelFileService.registerMultipartUpload(model, properties)
            var uploadModelResponse = UploadModelResponse()
            uploadModelResponse.result = SResponseType.success
            uploadModelResponse.uploadId = uploadId
            uploadModelResponse.modelId = model.id
            uploadModelResponse.floorId = floor.id

            // 增加监控数据
            monitorService.beginUpload(model.id!!)

            // 更新楼层, 失败则被抢占
            modelFileService.updateCurrentModelId(floor, model.id!!)
            return uploadModelResponse
        }catch (e : Exception){
            return SBaseResponse(SResponseType.failure, e.message?:"")
        }finally {

        }
    }

    @PostMapping("/merge")
    @ResponseBody
    @ApiOperation(value = "在完成分片上传之后, 合并所有已上传的分片接口", notes = "")
    fun mergeMultipartUpload(@RequestBody request: MergeMultipartRequest): SBaseResponse {
        try{
            modelFileService.checkMergeUploadInputValid(request)
            val model = dataService.modelFileService.select(SFilter.eq("id", request.modelId!!)).entity()
            if(model == null)
                return SBaseResponse(SResponseType.failure, "模型不存在")
            if(model.status != 0)
                return SBaseResponse(SResponseType.failure, "状态${model.status}, 不能合并")
            // 合并上传的分片文件
            val mergeResult = modelFileService.mergeMultipart(request.uploadId!!, properties)
            if (mergeResult) {
                // 修改模型文件状态到已上传(1)
                model.status = 1
                model.md5 = request.md5
                dataService.modelFileService.update(model, arrayListOf("status", "md5"))
                monitorService.endUpload(request.modelId!!)
                return SCreateResponse(arrayListOf(model))
            } else {
                // 删除本条模型记录
                // 获取该楼层模型最大的版本号的模型id, 修改到模型楼层的当前模型文件id上
                val floor = dataService.floorModelService.select(SFilter.eq("id", model.floorModelId!!)).entity()
                val folderFloorName = CommonService.getReportPrefix(floor!!)
                modelFileService.backToLastVersion(model)
                if(model.floorModelId != null) {
                    val message = messageService.buildMessage("${folderFloorName.first}的${folderFloorName.second}模型上传失败, 请重新上传", false, SPageContext.getHeader("ProjectId"), model.userId
                            ?: "", "上传过程异常中断", excelService.getDownloadUrl(model.id!!), folderFloorName)
                    messageService.sendMessage(message)
                }
                return SBaseResponse(SResponseType.failure, "上传出错, 请稍后重新上传")
            }
        }catch (e : Exception){
            return SBaseResponse(SResponseType.failure, e.message?:"")
        }
    }


    @Autowired
    lateinit var producer: JMSProducer

    @ApiOperation(value = "测试发送JMS消息", notes = "")
    @PostMapping(value = ["/sendmsg"])
    fun testJMS(@RequestBody request: String) : SBaseResponse{
        try {
            producer.sendMessage(request)
            return SBaseResponse(SResponseType.success)
        }catch (e : Exception){
            return SBaseResponse(SResponseType.failure, e.message?: "")
        }
    }

    /**
     * 根据条件查询统计数量
     */
    @ApiOperation(value = "根据条件查询统计数量", notes = "")
    @PostMapping(value = ["/count"])
    fun count(@RequestBody request: SCountRequest): SCountResponse {
        val sb = StringBuilder()
        if(request.filters == null){
            sb.append("ProjectId = '${SPageContext.getHeader("ProjectId")}'")
        }else{
            sb.append(request.filters)
            if(!sb.trim().endsWith(";"))
                sb.append(" and ")
            sb.append("ProjectId = '${SPageContext.getHeader("ProjectId")}'")
        }
        request.filters = sb.toString()
        return dataService.modelFileService.count(request)
    } // Function count()

//    /**
//     * 上传模型文件接口
//     */
//    @PostMapping("/upload")
//    @ResponseBody
//    @ApiOperation(value = "上传模型文件接口", notes = "")
//    fun multiUpload(request : HttpServletRequest, @RequestParam("note") note: String) : String {
//        val multipartRequest = request as MultipartHttpServletRequest
//        val jsonTxt = note
//        val i = 0
//        for ((key, file) in multipartRequest.multiFileMap) {//for((i, file) in files.withIndex())//for (int i = 0; i < files.size(); i++) {
//            //val file = files.get(i)//MultipartFile file = files.//  ..get(i);
//            if (file.isEmpty()) {
//                return "上传第" + (i + 1) + "个文件失败";
//            }
//            val single = file.get(0)
//            if (!single.isEmpty) {
//                val json = single.name
//                val fastj = JSONObject(java.net.URLDecoder.decode(json, "UTF-8"))
//                return fastj.getString("type")
//            }
//        }
//        return "上传成功"
//    }

    /**
     * 模型文件json数据导入
     */
    @ApiOperation(value = "模型文件json数据导入", notes = "")
    @GetMapping(value = ["/export"])
    fun export(@RequestParam modelId: String): SBaseResponse {
        try {
            var jsonData : BaseObj<AllComponents>?

            val url = CommonService.getExportJSONDownloadUrl(modelId, properties)
            val fileStream = HttpService.getInputStream(url)
            // 解析json
            jsonData = jsonAnalyzerService.analyzeJsonzFile(fileStream)
            val importResult = jsonAnalyzerService.importModelData(jsonData!!, "")
            return importResult
        }catch (e : Exception){
            return SBaseResponse(SResponseType.failure, e.message?: "")
        }
    }

}