/*
 * *********************************************************************************************************************
 *
 *          !!
 *        .F88X
 *        X8888Y
 *      .}888888N;
 *        i888888N;        .:!              .I$WI:
 *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
 *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
 *            +888888N;  .8888888Y                                  "&&8Y.}8,
 *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
 *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
 *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
 *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
 *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
 *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
 *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
 *      .:R888888I
 *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
 *        ~8888'
 *        .!88~                                                                     All rights reserved.
 *
 * *********************************************************************************************************************
 */

package com.persagy.server.datacenter.services.graphtype

import com.persagy.database.SFilter
import com.persagy.mybatis.SMybatisDao
import com.persagy.server.Opt
import com.persagy.server.datacenter.models.entities.graphtype.RelationTypeProject
import com.persagy.server.mappers.IRelationExcelExportMapper
import com.persagy.server.mappers.IRelationExcelImportMapper
import com.persagy.server.datacenter.services.base.RService
import com.persagy.server.datacenter.services.rel.RelationType
import com.persagy.server.utils.IdUtils
import com.persagy.service.SObjectService
import com.persagy.service.SPageContext
import com.persagy.service.models.enums.SResponseType
import com.persagy.service.models.responses.SBaseResponse
import com.persagy.service.utils.SSpringContextUtil
import org.apache.poi.xssf.usermodel.XSSFSheet
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.slf4j.LoggerFactory
import org.springframework.web.multipart.MultipartFile
import java.io.File
import javax.servlet.http.HttpServletResponse

/**
 * 查询总览
 *
 * @author  邓婷婷
 */
object RelationTypeProjectService : RService<RelationTypeProject>(SMybatisDao(RelationTypeProject::class.java))  {

    // 日志
    private val logger = LoggerFactory.getLogger(RelationTypeProjectService::class.java)

    /** 导出excel对应的Mapper */
    private val excelExport by lazy {
        SSpringContextUtil.getBean(IRelationExcelExportMapper::class.java) as IRelationExcelExportMapper
    }

    /** 导入excel对应的Mapper */
    private val excelImport by lazy {
        SSpringContextUtil.getBean(IRelationExcelImportMapper::class.java) as IRelationExcelImportMapper
    }

    /** 导入excel对应的Mapper */
    val downloadsService by lazy {
        SSpringContextUtil.getBean(DownloadsService::class.java) as DownloadsService
    }




    private val codeColMap by lazy {
        val map = HashMap<String, String>()
        map["对象ID"] = "id"
        map["对象名称"] = "name"
        map["对象本地编码"] = "local_id"
        map["对象本地名称"] = "local_name"
        map
    }

    private val codeTableMap by lazy {
        val map = HashMap<String, String>()
        map["Bd"] = "building"
        map["Eq"] = "equipment"
        map["Fl"] = "floor"
        map["Pe"] = "property"
        map["Pj"] = "project"
        map["Sh"] = "shaft"
        map["Sp"] = "zone_space_base"
        map["Sy"] = "general_system"
        map["Tn"] = "tenant"
        map
    }

    val itemList = arrayOf("_sbm", "abc", "name", "id", "local_id", "local_name", "object_type", "building_floor",
            "_sbm", "_abc", "_name", "_id", "_local_id", "_local_name","_object_type", "_building_floor")

    /** 关系记录启始行 */
    private const val rowStart = 5
    /** 主对项ID所在列 */
    private const val id1Col = 3
    /** 从对项ID所在列 */
    private const val id2Col = 11

    /** 主对项识别码所在列 */
    private const val idCode1Col = 0
    /** 从对项识别码所在列 */
    private const val idCode2Col = 8

    /** 导入状态所在列 */
    private const val stateCol = 16

    /**
     * 下载模板
     * @param   response        应答体
     */
    fun templateDownloads(response: HttpServletResponse) {
        val workbook = XSSFWorkbook(RelationTypeProjectService::class.java.classLoader.getResourceAsStream("excel/relation-template.xlsx"))
        response.contentType = "application/octet-stream"
        response.setHeader("Content-disposition", "attachment;filename=relation-template.xlsx")
        response.flushBuffer()
        workbook.write(response.outputStream)
    } // Function templateDownloads()


    /**
     * 下载模板
     * @param   response        应答体
     */
    fun downloadsEquip(response: HttpServletResponse) {
        val workbook = XSSFWorkbook(RelationTypeProjectService::class.java.classLoader.getResourceAsStream("excel/信息点汇总表(汇总)帮助文档.xlsx"))
        response.contentType = "application/octet-stream"
        response.setCharacterEncoding("utf-8")
        response.setHeader("Content-disposition", "attachment;filename=信息点汇总表(汇总)帮助文档.xlsx")
        response.flushBuffer()
        workbook.write(response.outputStream)
    } // Function templateDownloads()

    /**
     * 下载报告
     *
     * @param   response        应答体
     * @param   relType         关系类型
     * @param   zoneType        业务空间类型
     */
    fun reportDownloads(response: HttpServletResponse, relType: String, zoneType: String? = null) {
        val zone = if (zoneType != null) {
            "_${zoneType}"
        } else {
            ""
        }
        val projectId = SPageContext.getHeader("projectId")
        val file = File("/tmp/relation-$projectId-${relType}${zone}.xlsx")
        val workbook = if (file.exists()) {
            XSSFWorkbook(file)
        } else {
            XSSFWorkbook(RelationTypeProjectService::class.java.classLoader.getResourceAsStream("excel/relation-template.xlsx"))
        }
        response.contentType = "application/octet-stream"
        response.setHeader("Content-disposition", "attachment;filename=report-${relType}${zone}.xlsx")
        response.flushBuffer()
        workbook.write(response.outputStream)
    } // Function templateDownloads()

    /**
     * 导出excel
     *
     * @param   response        应答体
     * @param   relType         关系类型
     * @param   code            识别码类型
     * @param   zoneType        业务空间类型
     */
    fun exportExcel(response: HttpServletResponse, projectId: String,relType: String, code: String = "对象ID", zoneType: String? = null,graphCode: String) {
        val workbook = XSSFWorkbook(RelationTypeProjectService::class.java.classLoader.getResourceAsStream("excel/relation-template.xlsx"))
        response.contentType = "application/octet-stream"
        response.setHeader("Content-disposition", "attachment;filename=relation-$relType.xlsx")
        response.flushBuffer()

        val sheet = workbook.getSheet("关系维护")

//        val projectId = SPageContext.getHeader("projectId")

        try {
            writeExcelRelation(sheet, projectId, relType, code, zoneType,graphCode)
        }catch (e: Exception) {
            // DO NOTHING
        }

        workbook.write(response.outputStream)
    } // Function exportExcel()

    /**
     * 向excel表中写关系
     *
     * @param   sheet           Excel Sheet
     * @param   projectId       项目ID
     * @param   relType         关系类型
     * @param   code            识别码类型
     * @param   zoneType        业务空间类型
     */
    @Suppress("UNCHECKED_CAST")
    private fun writeExcelRelation(sheet: XSSFSheet, projectId: String, relType: String, code: String, zoneType: String? = null,graphCode: String) {
//        val len = relType.indexOf("_")
//        val cmd = if (len > 0) {
//            relType.substring(0, len)
//        } else {
//            relType
//        }
        val type = relType.toLowerCase()
//        val type = if (len > 0) {
//            relType.substring(len + 1)
//        } else {
//            ""
//        }

        val list = if (relType == RelationType.Eq2Bd || relType == RelationType.Eq2Fl) {
            val exportMethod = excelExport.javaClass.getDeclaredMethod("${type}In", String::class.java)
            exportMethod!!.invoke(excelExport, projectId) as List<HashMap<String, Any?>>
        }
        else if (relType == RelationType.Bd2Sp || relType == RelationType.Fl2Sp) {
            val exportMethod = excelExport.javaClass.getDeclaredMethod(type, String::class.java)
            exportMethod!!.invoke(excelExport, projectId) as List<HashMap<String, Any?>>
        }
        else {
            if(zoneType.isNullOrEmpty()) {
                val exportMethod = excelExport.javaClass.getDeclaredMethod(type, String::class.java, String::class.java,String::class.java)
                exportMethod!!.invoke(excelExport, projectId, relType, graphCode) as List<HashMap<String, Any?>>
            } else {
                val exportMethod = excelExport.javaClass.getDeclaredMethod(type, String::class.java, String::class.java, String::class.java,String::class.java)
                exportMethod!!.invoke(excelExport, projectId, relType, zoneType, graphCode) as List<HashMap<String, Any?>>
            }
        }

        var rowNum = rowStart

        // 填写识别码对应
        sheet.getRow(2).getCell(1).setCellValue(code)

        for (it in list) {
            val row = sheet.createRow(rowNum)
            for ((i, item) in itemList.withIndex()) {
                val cellValue = it[item]
                val cell = row.createCell(i)
                if (cellValue != null) {
                    cell.setCellValue(cellValue.toString())
                }
            }

            // 添加识别码
            row.createCell(0).setCellValue(row.getCell(id1Col).toString())
            row.createCell(8).setCellValue(row.getCell(id2Col).toString())
            rowNum++
        }
    } // Fun writeExcelRelation()

    /**
     * 导入excel
     *
     * @param   file            上传的Excel文件
     * @param   relType         关系类型
     * @param   zoneType        业务空间类型
     * @return  应答体
     */
    fun importExcel(file: MultipartFile, relType: String, zoneType: String? = null, graphCode: String): SBaseResponse {
        try {
            if (file.isEmpty) {
                throw Exception("未选择文件")
            }

            val zone = if (zoneType != null) {
                "_${zoneType}"
            } else {
                ""
            }
            val projectId = SPageContext.getHeader("projectId")
            try {

                val relationTypeProjectService = SObjectService(SMybatisDao(RelationTypeProject::class.java))
                val relationType = relationTypeProjectService.select(SFilter.eq("projectId", projectId!!),SFilter.eq("relationType", relType)).entity()
                if (relationType!=null){
                    relationType.computingTime = IdUtils.time()
                    relationTypeProjectService.update(relationType)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }

            val workbook = XSSFWorkbook(file.inputStream)
            val sheet = workbook.getSheet("关系维护")

            // 填写识别码对应
            val code = sheet.getRow(2).getCell(1).toString()
            logger.debug("识别编码：${code}  ******************************************")
            preExcelRelation(sheet, projectId!!, relType, code, zoneType)

            readExcelRelation(sheet, projectId, relType, zoneType,graphCode)

            val outFile = File("/tmp/relation-$projectId-${relType}${zone}.xlsx")
            workbook.write(outFile.outputStream())
            /** 发送消息 */
            rabbitMqService.sendRel(Opt.projectId!!, relType,"20","123","456")
        } catch (e: Exception) {
            e.printStackTrace()
            return SBaseResponse(SResponseType.failure, e.message!!)
        }
        return SBaseResponse(SResponseType.success)
    } // Fun importExcel()
    /**
     * 预处理Excel数据
     *
     * @param   sheet           Excel Sheet
     * @param   projectId       项目ID
     * @param   relType         关系类型
     * @param   code            识别码类型
     * @param   zoneType        业务空间类型
     */
    private fun preExcelRelation(sheet: XSSFSheet, projectId: String, relType: String, code: String, zoneType: String? = null): Boolean {
        var result = true
        val rowNum = rowStart


        for (i in rowNum .. sheet.lastRowNum) {
            try {

                val row = sheet.getRow(i)
                if (row?.getCell(idCode1Col) == null ||row.getCell(idCode2Col) == null){
                    continue
                }
                val idCode1ColValue = row.getCell(idCode1Col).toString()
                val idCode2ColValue = row.getCell(idCode2Col).toString()
                logger.debug("idCode1ColValue=${idCode1ColValue}")
                logger.debug("idCode2ColValue=${idCode2ColValue}")
                // 主对象识别码为空，则不能导入数据
                if (idCode1ColValue.isEmpty() || idCode2ColValue.isEmpty()) {
                    row.createCell(stateCol).setCellValue("失败，主对象or从对象的识别编码为空")
                    // result = false
                    continue
                }

                if (code.compareTo("对象ID", true) == 0) {

                    row.createCell(id1Col).setCellValue(idCode1ColValue)
                    row.createCell(id2Col).setCellValue(idCode2ColValue)
                } else  {

                    val id1List = excelImport.findObjectId(projectId, codeTableMap[relType.substring(0, 2)]!!, codeColMap[code]!!, idCode1ColValue)
                    val id2List = excelImport.findObjectId(projectId, codeTableMap[relType.substring(3, 5)]!!, codeColMap[code]!!, idCode2ColValue)

                    if (id1List.count() < 1) {
                        row.createCell(stateCol).setCellValue("未找到主对象")
                        continue
                    }
                    if (id1List.count() > 1) {
                        row.createCell(stateCol).setCellValue("匹配到多个主对象")
                        continue
                    }
                    if (id2List.count() < 1) {
                        row.createCell(stateCol).setCellValue("未找到从对象")
                        continue
                    }
                    if (id2List.count() > 1) {
                        row.createCell(stateCol).setCellValue("匹配到多个从对象")
                        continue
                    }
                    row.createCell(id1Col).setCellValue(id1List[0])
                    row.createCell(id2Col).setCellValue(id2List[0])
                }
            } catch (e: Exception) {
                e.printStackTrace()
                // DO NOTHING
            }
        }

        return result
    } // Fun preExcelRelation()
    /**
     * 从Excel读取关系并存入数据库中
     *
     * @param   sheet           Excel Sheet
     * @param   projectId       项目ID
     * @param   relType         关系类型
     * @param   zoneType        业务空间类型
     */
    private fun readExcelRelation(sheet: XSSFSheet, projectId: String, relType: String, zoneType: String? = null, graphCode: String) {
        try {
//            val len = relType.indexOf("_")
//            val cmd = if (len > 0) {
//                relType.substring(0, len)
//            } else {
//                relType
//            }
//        val type = if (len > 0) {
//            relType.substring(len + 1)
//        } else {
//            ""
//        }

            val type = relType

            val importMethod = if (relType == RelationType.Eq2Bd || relType == RelationType.Eq2Fl) {
                excelImport.javaClass.getDeclaredMethod("${relType}In", String::class.java, String::class.java, String::class.java, String::class.java)
            }else if(relType == RelationType.Bd2Sp || relType == RelationType.Fl2Sp){
                excelImport.javaClass.getDeclaredMethod(relType, String::class.java, String::class.java, String::class.java, String::class.java)
            }
            else {
                if(zoneType.isNullOrEmpty()) {
                    excelImport.javaClass.getDeclaredMethod(relType, String::class.java, String::class.java, String::class.java, String::class.java, String::class.java)
                } else {
                    excelImport.javaClass.getDeclaredMethod(relType, String::class.java, String::class.java, String::class.java, String::class.java, String::class.java, String::class.java)
                }
            }

            val rowNum = rowStart
            for (i in rowNum .. sheet.lastRowNum) {
                val row = sheet.getRow(i)
                try {
                if (row == null||row.getCell(id1Col) == null||row.getCell(id2Col) == null){
                    sheet.getRow(0).createCell(stateCol).setCellValue("第$i 行数据为空")
                    continue
                }
                val id1 = row.getCell(id1Col).toString()
                val id2 = row.getCell(id2Col).toString()
                // 任意一个ID为空，则不能导入数据
                if (id1.isEmpty() || id2.isEmpty()) {
                    return
                }
                    if(zoneType.isNullOrEmpty()) {
                        importMethod.invoke(excelImport, projectId, type, id1, id2, graphCode)
                    } else {
                        importMethod.invoke(excelImport, projectId, type, id1, id2, zoneType, graphCode)
                    }
                    row.createCell(stateCol).setCellValue("关联成功")
                } catch (e: Exception) {
                    e.printStackTrace()
                    row.createCell(stateCol).setCellValue("关联失败")
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    } // Fun readExcelRelation()

} // Class RelationTypeProjectService

