package cn.edu.whu.boot.collection.service.impl;

import cn.edu.whu.boot.allocation.services.TaskCellDataService;
import cn.edu.whu.boot.allocation.services.TaskUserCellService;
import cn.edu.whu.boot.collection.domain.MultimediaItem;
import cn.edu.whu.boot.collection.dto.SqlEnity;
import cn.edu.whu.boot.collection.mapper.BaseMapper;
import cn.edu.whu.boot.collection.mapper.UpdateMapper;
import cn.edu.whu.boot.collection.service.IBaseService;
import cn.edu.whu.boot.collection.service.IMultimediaService;
import cn.edu.whu.boot.common.domain.CommonConstant;
import cn.edu.whu.boot.common.service.CacheService;
import cn.edu.whu.boot.common.utils.collection.PostgreSqlUtil;
import cn.edu.whu.boot.common.utils.collection.StoreMediaFileUtil;
import cn.edu.whu.boot.common.utils.collection.TableOperationUtil;
import cn.edu.whu.boot.common.utils.xml.CheckTableDataUtil;
import cn.edu.whu.boot.common.utils.xml.GenerateSqlUtil;
import cn.edu.whu.boot.xml.domain.Task;
import cn.edu.whu.boot.xml.service.ITaskService;
import cn.edu.whu.boot.xml.xmlreader.bean.Config;
import cn.edu.whu.boot.xml.xmlreader.bean.Field;
import cn.edu.whu.boot.xml.xmlreader.bean.Table;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;

@Service
public class BaseServiceImpl implements IBaseService{

    private final BaseMapper baseMapper;
    private final UpdateMapper updateMapper;
    private final IMultimediaService multimediaService;
    private final ITaskService taskService;
    private final TaskCellDataService taskCellDataService;
    private final TaskUserCellService taskUserCellService;
    private final CacheService cacheService;

    @Value("${MediaPath}")
    private String mediaPath;

    @Value("${HistoryMediaPath}")
    private String historyMediaPath;

    private static Logger logger = LoggerFactory.getLogger(IBaseService.class);

    @Autowired
    public BaseServiceImpl(BaseMapper baseMapper, UpdateMapper updateMapper, IMultimediaService multimediaService,
                           ITaskService taskService, TaskCellDataService taskCellDataService, TaskUserCellService taskUserCellService, CacheService cacheService){
        this.baseMapper = baseMapper;
        this.updateMapper = updateMapper;
        this.multimediaService = multimediaService;
        this.taskService = taskService;
        this.taskCellDataService = taskCellDataService;
        this.taskUserCellService = taskUserCellService;
        this.cacheService = cacheService;
    }

    // 返回的结果为插入记录的自增ID号
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Long insertSqlEnity(SqlEnity sqlEnity){
        int result = baseMapper.insertSqlEnity(sqlEnity);
        if(result == 1){
            return sqlEnity.getId();
        }
        else{
            return null;
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String insertSqlString(String sqlString){
        int result = baseMapper.insertSql(sqlString);
        if(result >= 1){
            return CommonConstant.SUCCEED;
        }
        else{
            return CommonConstant.FAILED;
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String updateSqlString(String sqlString){
        int result = -1;
        result = updateMapper.updateSql(sqlString);
        if(result != -1){
            return CommonConstant.SUCCEED;
        }
        else{
            return CommonConstant.FAILED;
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Integer getVersionByLongId(String tableName, String primaryKeyName, Long primaryKeyValue){
        int result = -1;
        result = baseMapper.getVersionByPolygonId(tableName, primaryKeyName, primaryKeyValue);
        return result;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public List<Map<String, Object>> selectData(String selectSql){
        List<Map<String, Object>> result = baseMapper.selectData(selectSql);
        if(!result.isEmpty()){
            return result;
        }
        else{
            return null;
        }
    }

//    @Override
//    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
//    public List<Map<String, Object>> selectData(String selectSql){
//        List<Map<String, Object>> result = baseMapper.selectData(selectSql);
//        return result == null || result.isEmpty() ? Collections.emptyList() : result;
//    }
    @Override
    public Integer getUserWorkRole(Integer taskId, Integer userId){
        return baseMapper.getUserWorkRole(taskId, userId);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String delete(String deleteSql){
        int result = -1;
        result = updateMapper.delete(deleteSql);
        if(result != -1){
            return CommonConstant.SUCCEED;
        }
        return CommonConstant.FAILED;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Long insertData(Task task, Table table, int userId, JSONArray multimediaItemJsonArray, MultipartFile file){
        // 插入根表数据
        Long polygonId;
        // 20250724---------------------------------------
        encapPhotoInTable(table.getFields(), task.getId(), userId);
        //------------------------------------------------
        String insertSql = GenerateSqlUtil.generateInsertSql(task.getDatabaseName(), table, task.getId(), userId);
        System.out.println(insertSql);
        SqlEnity sqlEnity = new SqlEnity();
        sqlEnity.setSql(insertSql);
        String primaryKeySeq = GenerateSqlUtil.generatePrimaryKeySeq(task.getDatabaseName(), table);
        sqlEnity.setPrimaryKeySeq(primaryKeySeq);
        polygonId = insertSqlEnity(sqlEnity);
        if(polygonId == null){
            throw new RuntimeException("获取" + table.getKey() + "表记录插入的ID号失败");
        }

        // 储存多媒体信息到指定的本地文件夹中,前提是判断 MultipartFile 是否为空
        if(file != null && !file.isEmpty()){
            encapMediaInfoMethod(task.getId(), file, task.getDatabaseName(), table.getKey(), userId,
                                 multimediaItemJsonArray, polygonId, 0);
        }

        // 检查该表是否具有关联表
        Map<String, Object> assoAndCollTableMap = TableOperationUtil.hasAssoAndCollField(table);
        if(assoAndCollTableMap.size() != 0){
            insertAssoAndCollData(task.getDatabaseName(), table, polygonId, 0, task.getConfig(),task.getId(),userId);
        }

        // 更新数据和网格对应关系
        taskCellDataService.updateAfterDataCreatedOrUpdated(task, table, polygonId);

        return polygonId;
    }

    /**
     * 将多媒体信息包括本地存储和表记录插入封装成一个方法，因为插入数据和复核数据时都用到该方法
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void encapMediaInfoMethod(Integer taskId, MultipartFile file, String databaseName, String tableName, int userId, JSONArray multimediaItemJsonArray, Long polygonId, int versionCode){
        String path =  "/task_" + taskId + "/user_" + userId;
        String storeMediaFileResult = StoreMediaFileUtil.storeFileAndUnzip(file, mediaPath + path);
        if(storeMediaFileResult.equals(CommonConstant.FAILED)){
            logger.info("多媒体信息压缩包储存失败");
        }
        String mediaTableName = PostgreSqlUtil.buildTableNameString(databaseName, tableName, true, false);
        if(multimediaItemJsonArray != null && multimediaItemJsonArray.size() != 0){
            for(int i = 0; i < multimediaItemJsonArray.size(); i++){
                MultimediaItem media = JSON.toJavaObject(multimediaItemJsonArray.getJSONObject(i), MultimediaItem.class);
                media.setID(polygonId);
                media.setMediaPath(path + "/" + media.getFile());
                media.setMediaTableName(mediaTableName);
                media.setVersion(versionCode);
                String result = multimediaService.insertMultiMedia(media);
                if(result.equals(CommonConstant.FAILED)){
                    throw new RuntimeException("插入多媒体记录失败");
                }
            }
        }
    }
    /**
     * 将多媒体信息包括表记录插入子表
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void encapSubMediaInfoMethod(Integer taskId,  String databaseName, String tableName, int userId, JSONArray multimediaItemJsonArray, Long polygonId, int versionCode){
        String path =  "/task_" + taskId + "/user_" + userId;
        String mediaTableName = PostgreSqlUtil.buildTableNameString(databaseName, tableName, true, false);
        if(multimediaItemJsonArray != null && multimediaItemJsonArray.size() != 0){
            for(int i = 0; i < multimediaItemJsonArray.size(); i++){
                MultimediaItem media = JSON.toJavaObject(multimediaItemJsonArray.getJSONObject(i), MultimediaItem.class);
                media.setID(polygonId);
                media.setMediaPath(path + "/" + media.getFile());
                media.setMediaTableName(mediaTableName);
                media.setVersion(versionCode);
                String result = multimediaService.insertMultiMedia(media);
                if(result.equals(CommonConstant.FAILED)){
                    throw new RuntimeException("插入多媒体记录失败");
                }
            }
        }
    }
    // 将Photo组件记录的照片名称拼接上路径地址
    public void encapPhotoInTable(Map<String, Field> data, Integer taskId,  int userId){
        if(data==null)
            return;
        String path =  "/task_" + taskId + "/user_" + userId;
        for(Field field:data.values()){
            if(field.getType()!=null && field.getType().equalsIgnoreCase("photo")){
                field.setValue(path + "/" + field.getValue());
            }else if((field.getAssociate()!=null && !field.getAssociate().isEmpty()) || (field.getCollection()!=null && !field.getCollection().isEmpty())){
                List<Map<String, Field>> values = (List<Map<String, Field>>)field.getValue();
                if(values!=null){
                    for(Map<String, Field> value : values){
                    if (value != null && !value.isEmpty()) {
                        encapPhotoInTable(value, taskId, userId);
                    }
                }
                }
            }
        }
    }
//    public void encapPhotoInTable(Map<String, Field> data, Integer taskId, int userId) {
//        if (data == null) return;
//
//        String path = "/task_" + taskId + "/user_" + userId;
//
//        for (Field field : data.values()) {
//            if ("photo".equalsIgnoreCase(field.getType())) {
//                field.setValue(path + "/" + field.getValue());
//            }
//            else if (field.getAssociate() != null && !field.getAssociate().isEmpty()) {
//                Object val = field.getValue();
//                if (val instanceof List) {
//                    // 正确处理关联字段转成 List<Map<String, Field>> 的情况
//                    List<Map<String, Field>> list = (List<Map<String, Field>>) val;
//                    for (Map<String, Field> item : list) {
//                        encapPhotoInTable(item, taskId, userId);
//                    }
//                }
//            }
//            else if (field.getCollection() != null && !field.getCollection().isEmpty()) {
//                Object val = field.getValue();
//                if (val instanceof List) {
//                    List<Map<String, Field>> values = (List<Map<String, Field>>) val;
//                    for (Map<String, Field> value : values) {
//                        encapPhotoInTable(value, taskId, userId);
//                    }
//                }
//            }
//        }
//    }

    public void decapPhotoInTable(Map<String, Field> data){
        if(data==null)
            return;
        for(Field field:data.values()){
            if(field.getType()!=null && field.getType().equalsIgnoreCase("photo")){
                if(field.getValue()!=null)
                    field.setValue(new File(field.getValue().toString()).getName());
            }else if(field.getAssociate()!=null && !field.getAssociate().isEmpty()){
                decapPhotoInTable((Map<String, Field>)field.getValue());
            }else if(field.getCollection()!=null && !field.getCollection().isEmpty()){
                List<Map<String, Field>> values = (List<Map<String, Field>>)field.getValue();
                if(values!=null){
                    for(Map<String, Field> value : values){
                        decapPhotoInTable(value);
                    }
                }

            }
        }
    }

//    /**
//     * 将Table中存在关联表(associate、collector)数据存储到指定的关联表中
//     */
//    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
//    public void insertAssoAndCollData(String databaseName, Table table, Long polygonId, int versionCode, String configString){
//        Map<String, Object> assoAndCollTableMap = TableOperationUtil.hasAssoAndCollField(table);
//        JSONObject configJsonObject = JSON.parseObject(configString);
//        Config config = JSON.toJavaObject(configJsonObject, Config.class);
//        Map<String, Table> tables = config.getTables();
//        for(Map.Entry<String, Object> entry : assoAndCollTableMap.entrySet()){
//            if(entry.getValue() == null || entry.getValue().equals("")){
//                continue;
//            }
//            List<Map<String, Field>> collectoreFieldValueList = (List<Map<String, Field>>) entry.getValue();
//            for(Map<String, Field> collectoreFieldValue : collectoreFieldValueList){
//                Table assoAndCollTable = tables.get(entry.getKey());
//                assoAndCollTable.setFields(collectoreFieldValue);
//                Map<String, Object> map = TableOperationUtil.hasAssoAndCollField(assoAndCollTable);
//                if(map.size() != 0){
//                    String assoAndCollInsertSql = GenerateSqlUtil.generateInsertSql(databaseName, entry.getKey(), collectoreFieldValue, polygonId, versionCode);
//                    Long assoAndCollPolygonId; // 子表插入数据的polygonId
//                    SqlEnity sqlEnity = new SqlEnity();
//                    sqlEnity.setSql(assoAndCollInsertSql);
//                    String primaryKeySeq = GenerateSqlUtil.generatePrimaryKeySeq(databaseName, assoAndCollTable);
//                    sqlEnity.setPrimaryKeySeq(primaryKeySeq);
//                    assoAndCollPolygonId = insertSqlEnity(sqlEnity);
//                    if(polygonId == null){
//                        logger.info("获取" + assoAndCollTable.getKey() + "表记录插入的ID号失败");
//                        throw new RuntimeException("获取" + assoAndCollTable.getKey() + "表记录插入的ID号失败");
//                    }
//                    insertAssoAndCollData(databaseName, assoAndCollTable, assoAndCollPolygonId, versionCode, configString);
//                }
//                else{
//                    String collectorInsertSql = GenerateSqlUtil.generateInsertSql(databaseName, entry.getKey(), collectoreFieldValue, polygonId, versionCode);
//                    String collectorInsertSqlResult = insertSqlString(collectorInsertSql);
//                    if(collectorInsertSqlResult.equals(CommonConstant.FAILED)){
//                        logger.info(assoAndCollTable.getKey() + "对应的关联表" + entry.getKey() + "记录插入失败");
//                        throw new RuntimeException(assoAndCollTable.getKey() + "对应的关联表" + entry.getKey() + "记录插入失败");
//                    }
//                }
//            }
//        }
//    }

public void insertAssoAndCollData(String databaseName, Table table, Long polygonId, int versionCode, String configString, int taskId,int UserId) {
    Map<String, Object> assoAndCollTableMap = TableOperationUtil.hasAssoAndCollField(table);
    JSONObject configJsonObject = JSON.parseObject(configString);
    Config config = JSON.toJavaObject(configJsonObject, Config.class);
    Map<String, Table> tables = config.getTables();

    for (Map.Entry<String, Object> entry : assoAndCollTableMap.entrySet()) {
        if (entry.getValue() == null || entry.getValue().equals("")) {
            continue;
        }
        // 20250728------------------------------------------------------------
        List<Map<String, Field>> collectoreFieldValueList = (List<Map<String, Field>>) entry.getValue();

//        List<Map<String, Field>> collectoreFieldValueList = new ArrayList<>();
//        if(entry.getValue() instanceof List){
//            collectoreFieldValueList.addAll((List<Map<String, Field>>) entry.getValue());
//        }else{
//            collectoreFieldValueList.add((Map<String, Field>) entry.getValue());
//        }
        // -------------------------------------------------------------------
        for (int i = 0; i < collectoreFieldValueList.size(); i++) {
            Map<String, Field> collectoreFieldValue = collectoreFieldValueList.get(i);
            Table assoAndCollTable = tables.get(entry.getKey());
            assoAndCollTable = JSON.parseObject(JSON.toJSONString(assoAndCollTable), Table.class);
            assoAndCollTable.setFields(collectoreFieldValue);

            //----------------------------------------
            String insertSql = GenerateSqlUtil.generateInsertSql(databaseName, entry.getKey(), collectoreFieldValue, polygonId, versionCode);
            SqlEnity sqlEnity = new SqlEnity();
            sqlEnity.setSql(insertSql);

            String primaryKeySeq = GenerateSqlUtil.generatePrimaryKeySeq(databaseName, assoAndCollTable);
            sqlEnity.setPrimaryKeySeq(primaryKeySeq);

            Long polygonIdInserted = insertSqlEnity(sqlEnity);
            if (polygonIdInserted == null) {
                logger.info(assoAndCollTable.getKey() + " 对应的关联表 " + entry.getKey() + " 记录插入失败，未获取到ID");
                throw new RuntimeException(assoAndCollTable.getKey() + " 对应的关联表 " + entry.getKey() + " 记录插入失败，未获取到ID");
            }
            //20250730-------------  子表多媒体信息查询
            JSONArray submultimediaItemJsonArray =
                    (JSONArray) TableOperationUtil.getFieldValue(assoAndCollTable , TableOperationUtil.getCameraFieldName(assoAndCollTable));
            // ----------
            handleMediaFields(taskId,databaseName, entry.getKey(), collectoreFieldValue, polygonIdInserted,submultimediaItemJsonArray,UserId);
            // 递归插入嵌套的子表
            insertAssoAndCollData(databaseName, assoAndCollTable, polygonIdInserted, versionCode, configString, taskId,UserId);


//            Map<String, Object> subMap = TableOperationUtil.hasAssoAndCollField(assoAndCollTable);
//            Long assoAndCollPolygonId;
//
//            if (subMap.size() != 0) {
//                String subInsertSql = GenerateSqlUtil.generateInsertSql(databaseName, entry.getKey(), collectoreFieldValue, polygonId, versionCode);
//                SqlEnity sqlEnity = new SqlEnity();
//                sqlEnity.setSql(subInsertSql);
//
//                String primaryKeySeq = GenerateSqlUtil.generatePrimaryKeySeq(databaseName, assoAndCollTable);
//                sqlEnity.setPrimaryKeySeq(primaryKeySeq);
//
//                assoAndCollPolygonId = insertSqlEnity(sqlEnity);
//                if (assoAndCollPolygonId == null) {
//                    logger.info("获取 " + assoAndCollTable.getKey() + " 表记录插入的 ID 号失败");
//                    throw new RuntimeException("获取 " + assoAndCollTable.getKey() + " 表记录插入的 ID 号失败");
//                }
//
//                // 插入成功后处理 media 类型字段
//                handleMediaFields(taskId, databaseName, entry.getKey(), collectoreFieldValue, assoAndCollPolygonId);
//
//                // 递归插入嵌套的子表
//                insertAssoAndCollData(databaseName, assoAndCollTable, assoAndCollPolygonId, versionCode, configString, taskId);
//            } else {
//                String insertSql = GenerateSqlUtil.generateInsertSql(databaseName, entry.getKey(), collectoreFieldValue, polygonId, versionCode);
//
//                SqlEnity sqlEnity = new SqlEnity();
//                sqlEnity.setSql(insertSql);
//
//                String primaryKeySeq = GenerateSqlUtil.generatePrimaryKeySeq(databaseName, assoAndCollTable);
//                sqlEnity.setPrimaryKeySeq(primaryKeySeq);
//
//                Long polygonIdInserted = insertSqlEnity(sqlEnity);
//                if (polygonIdInserted == null) {
//                    logger.info(assoAndCollTable.getKey() + " 对应的关联表 " + entry.getKey() + " 记录插入失败，未获取到ID");
//                    throw new RuntimeException(assoAndCollTable.getKey() + " 对应的关联表 " + entry.getKey() + " 记录插入失败，未获取到ID");
//                }
//
//                // 插入成功后处理 media 类型字段
//                handleMediaFields(taskId, databaseName, entry.getKey(), collectoreFieldValue, polygonIdInserted);
//            }
        }
    }
}

    /**
     * 处理字段中 type 为 "media" 的，调用 encapMediaInfoMethod 插入 _media 表
     */

    private void handleMediaFields(int taskId, String dbName, String tableName, Map<String, Field> fieldMap, Long polygonId, JSONArray submultimediaItemJsonArray, int userId) {
        for (Field field : fieldMap.values()) {
//            System.out.println(field.getType());
//            if ("media".equalsIgnoreCase(field.getType()) && field.getValue() != null) {
//                JSONArray fakeJsonArray = new JSONArray(); // 如果你有实际结构，可以替换
//                encapMediaInfoMethod(taskId, null, dbName, tableName, -1, fakeJsonArray, polygonId, 0);
//            }
            if ("camera".equalsIgnoreCase(field.getType()) && field.getValue() != null) {
                //JSONArray fakeJsonArray = new JSONArray(); // 如果你有实际结构，可以替换
                encapSubMediaInfoMethod(taskId, dbName, tableName, userId,submultimediaItemJsonArray, polygonId, 0);
            }
        }
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Long updateRecheckData(Task task, Table table, int userId, JSONArray multimediaItemJsonArray,
                                  Long polygonIdFromAndroid, MultipartFile file) throws IOException{
        // 20250724---------------------------------------
        encapPhotoInTable(table.getFields(), task.getId(), userId);
        //------------------------------------------------
        String primaryKey = TableOperationUtil.getPrimaryKey(table);
        String selectRootTableByPolygonIdSql = GenerateSqlUtil.generateSelectSqlByField(task.getDatabaseName(), table.getKey(), primaryKey, polygonIdFromAndroid);

        List<Map<String, Object>> rootTableDataList = selectData(selectRootTableByPolygonIdSql);
        String insertRootDataToHisResult = insertDataToTable(rootTableDataList, task.getDatabaseName(), table.getKey(), task.getConfig(), 1);
        if(!insertRootDataToHisResult.equals(CommonConstant.SUCCEED)){
            logger.info("根表" + table.getKey() + "向历史数据库中插入失败");
        }

        // 关联表及多媒体表插入历史数据库
        recursiveInsertAssoAndCollDataToTable(task, table, polygonIdFromAndroid, 1, -1);

        // 多媒体表信息插入历史表不用上述方法，因为多媒体表有对应的实体类
        String mediaTableNameInDB = PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), table.getKey(), true, false);
        // 如果从数据库中没查到对应的多媒体数据，hisMultimediaItemList == null
        List<MultimediaItem> hisMultimediaItemList = multimediaService.selectMultiMedia(mediaTableNameInDB, polygonIdFromAndroid);
        if(hisMultimediaItemList != null){
            for(MultimediaItem multimediaItem : hisMultimediaItemList){
                String mediaHisTableNameInDB = PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), table.getKey(), true, true);
                multimediaItem.setMediaTableName(mediaHisTableNameInDB);
                String oldMediaPath = multimediaItem.getMediaPath();
                String newMediaPath = oldMediaPath.replaceFirst(mediaPath, historyMediaPath);
                File outFile = new File(newMediaPath);
                if(!outFile.getParentFile().exists()){
                    outFile.getParentFile().mkdirs();
                }
                if(!outFile.exists()){
                    outFile.createNewFile();
                }
                try(FileInputStream fileInputStream = new FileInputStream(oldMediaPath); FileOutputStream fileOutputStream = new FileOutputStream(outFile)){
                    int copyResult = IOUtils.copy(fileInputStream, fileOutputStream);
                    if(copyResult == -1){
                        logger.info("多媒体文件复制到历史文件夹中失败");
                    }
                } catch(Exception e){
                    e.printStackTrace();
                }
                multimediaItem.setMediaPath(newMediaPath);
                String insertMediaToHisResult = multimediaService.insertMultiMedia(multimediaItem);
                if(insertMediaToHisResult.equals(CommonConstant.FAILED)){
                    logger.info("历史数据多媒体表" + table.getKey() + "_media_history插入失败");
                }
            }
        }

        // 根表数据更新
        String updateSqlString = GenerateSqlUtil.generateUpdateSql(task, userId, table, polygonIdFromAndroid);
        String updateResult = updateSqlString(updateSqlString);
        if(updateResult.equals(CommonConstant.FAILED)){
            logger.info("更新根表记录失败");
            throw new RuntimeException("更新根表记录失败");
        }
        updateTableStatus(PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), table.getKey(), false, false),
                          PostgreSqlUtil.transformString(primaryKey), polygonIdFromAndroid, 1, null);
        // 关联表、多媒体表及本地文件更新
        String deleteResult = deleteDataByPolygonId(task.getDatabaseName(), table, polygonIdFromAndroid, task.getConfig(), false);
        if(!deleteResult.equals(CommonConstant.SUCCEED)){
            logger.info("删除根表的关联表和多媒体表记录失败");
        }
        if(file != null && !file.isEmpty()){
            encapMediaInfoMethod(task.getId(), file, task.getDatabaseName(), table.getKey(), userId,
                                 multimediaItemJsonArray, polygonIdFromAndroid,
                                 Integer.valueOf(table.getFields().get(CommonConstant.FIX_FIELD_VERSION).getValue().toString()) + 1);
        }

        Map<String, Object> map = TableOperationUtil.hasAssoAndCollField(table);
        if(map.size() != 0){
            insertAssoAndCollData(task.getDatabaseName(), table, polygonIdFromAndroid, Integer.valueOf(table.getFields().get(CommonConstant.FIX_FIELD_VERSION).getValue().toString()) + 1, task.getConfig(),task.getId(),userId);
        }
        return polygonIdFromAndroid;
    }

    /**
     * 获取表的数据插入在另一个表中，既包括关联表数据插入历史数据库表中,也有将历史数据表中数据放进关联表中
     * 依据operateType值进行判断
     *
     * @param dataList 数据,Map<String, Object>中key为字段名，value为字段值,
     * 这是没有固定实体类获取数据库的数据方法
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String insertDataToTable(List<Map<String, Object>> dataList, String databaseName, String tableName, String configString, int operateType){
        if(dataList != null && dataList.size() != 0){
            for(Map<String, Object> assoAndCollTableData : dataList){
                String insertSqlByAllFieldsSql;
                if(operateType == 1){
                    insertSqlByAllFieldsSql = GenerateSqlUtil.generateInsertSqlByAllFields(assoAndCollTableData, databaseName, tableName + CommonConstant.HISTORY_SUFFIX, configString);
                }
                else{
                    insertSqlByAllFieldsSql = GenerateSqlUtil.generateInsertSqlByAllFields(assoAndCollTableData, databaseName, tableName, configString);
                }
                String insertResultByAllFields = insertSqlString(insertSqlByAllFieldsSql);
                if(insertResultByAllFields.equals(CommonConstant.FAILED)){
                    throw new RuntimeException("根表" + tableName + "向历史数据库中插入失败");
                }
            }
        }
        return CommonConstant.SUCCEED;
    }

    /**
     * 递归地将根表中关联表数据插入历史数据库表中,或者将历史数据表中数据放进关联表中
     * operateType值为1表示从关联表数据插入历史数据库表中，2表示将历史数据表中数据放进关联表中
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void recursiveInsertAssoAndCollDataToTable(Task task, Table table, Long polygonId, int operateType, int versionCode){
        Config config = JSON.toJavaObject(JSON.parseObject(task.getConfig()), Config.class);
        Map<String, Table> tables = config.getTables();
        Map<String, String> assoAndCollTableMap = CheckTableDataUtil.getAssoAndCollField(table.getKey(), task.getConfig());
        for(Map.Entry<String, String> entry : assoAndCollTableMap.entrySet()){
            String selectAssoAndCollTableByPolygonIdSql;
            if(operateType == 1){
                selectAssoAndCollTableByPolygonIdSql = GenerateSqlUtil.generateSelectSqlByField(task.getDatabaseName(), entry.getKey(), entry.getValue(), polygonId);
            }
            else{
                selectAssoAndCollTableByPolygonIdSql = GenerateSqlUtil.genSqlByPolygonIdAndVersionId(task.getDatabaseName(), entry.getKey() + CommonConstant.HISTORY_SUFFIX, entry.getValue(), polygonId, versionCode);
            }

            List<Map<String, Object>> assoAndCollTableDataList = selectData(selectAssoAndCollTableByPolygonIdSql);
            String insertAssoAndCollToHisResult = insertDataToTable(assoAndCollTableDataList, task.getDatabaseName(), entry.getKey(), task.getConfig(), operateType);
            if(!insertAssoAndCollToHisResult.equals(CommonConstant.SUCCEED)){
                throw new RuntimeException("历史数据表" + insertAssoAndCollToHisResult + "插入失败");
            }
            // 判断子表是否也具有关联表
            Table assoAndCollTable = tables.get(entry.getKey());
            Map<String, String> map = CheckTableDataUtil.getAssoAndCollField(assoAndCollTable.getKey(), task.getConfig());
            if(map.size() != 0){
                String primaryKey = null;
                Map<String, Field> fields = assoAndCollTable.getFields();
                for(Map.Entry<String, Field> fieldEntry : fields.entrySet()){
                    if(fieldEntry.getValue().isIdentity()){
                        primaryKey = fieldEntry.getValue().getKey();
                        break;
                    }
                }
                if (assoAndCollTableDataList != null) {
                    for(Map<String, Object> assoAndCollTableData : assoAndCollTableDataList){
                        assert primaryKey != null;
                        Long assoAndCollPolygonId = Long.valueOf(assoAndCollTableData.get(primaryKey).toString());
                        recursiveInsertAssoAndCollDataToTable(task, assoAndCollTable, assoAndCollPolygonId, operateType, versionCode);
                    }
                }
            }
        }
    }

    /**
     * 将根表Table中polygonId对应记录删除,根表的关联表及多媒体表中对应数据也删除
     *
     * @param isIncludeRootTable 删除是否包括根表数据，主要是方便复核数据时更新根表数据，而删除再插入关联、多媒体表
     * @return 若全部polygonId对应的数据删除成功返回succeed，否则返回failed
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String deleteDataByPolygonId(String databaseName, Table table, Long polygonId, String configString, boolean isIncludeRootTable){

        Map<String, String> assoAndCollFieldMap = CheckTableDataUtil.getAssoAndCollField(table.getKey(), configString);
        String primaryKey = TableOperationUtil.getPrimaryKey(table);
        // 如果删除根表数据的话
        if(isIncludeRootTable){
            String rootTableName = PostgreSqlUtil.buildTableNameString(databaseName, table.getKey(), false, false);
            String deleteRootTableSql = GenerateSqlUtil.genDeleteSql(rootTableName, PostgreSqlUtil.transformString(primaryKey), polygonId);
            String deleteResult = delete(deleteRootTableSql);
            if(deleteResult.equals(CommonConstant.FAILED)){
                throw new RuntimeException("删除根表数据失败");
            }
        }

        // 删除关联表中数据(如果存在的话)
        if(assoAndCollFieldMap.size() != 0){
            recursiveDeleteAssoAndCollData(assoAndCollFieldMap, polygonId, configString, databaseName, false, null);
        }

        // 删除多媒体表数据及本地多媒体文件(如果存在的话)
        String mediaTableName = PostgreSqlUtil.buildTableNameString(databaseName, table.getKey(), true, false);
        List<MultimediaItem> multimediaItemList = multimediaService.selectMultiMedia(mediaTableName, polygonId);
        if(multimediaItemList != null){
            for(MultimediaItem multimediaItem : multimediaItemList){
                File file = new File(multimediaItem.getMediaPath());
                if(file.exists()){
                    file.delete();
                }
            }
            String deleteAssoAndCollTableResult = multimediaService.deleteMultiMedia(mediaTableName, polygonId);
            if(deleteAssoAndCollTableResult.equals(CommonConstant.FAILED)){
                throw new RuntimeException("删除根表数据失败");
            }
        }
        return CommonConstant.SUCCEED;
    }

    /**
     * 递归地将根表中关联表数据删除,注意这里也包括删除历史表的数据，通过isHistoryTable参数判定是不是历史表，
     * 如果是历史表需要提供version参数，如果非历史表version参数为null
     * 该方法主要供deleteDataByPolygonId()和rollBackDataForVersion()方法使用
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void recursiveDeleteAssoAndCollData(Map<String, String> assoAndCollTableMap, Long polygonId, String configString, String databaseName, boolean isHistoryTable, Integer version){
        Config config = JSON.toJavaObject(JSON.parseObject(configString), Config.class);
        String suffixString;
        if(isHistoryTable){
            suffixString = CommonConstant.HISTORY_SUFFIX;
        }
        else{
            suffixString = "";
        }

        for(Map.Entry<String, String> assoAndCollTableEntry : assoAndCollTableMap.entrySet()){
            String parentIdFieldName = null;
            Table assoAndCollTable = config.getTables().get(assoAndCollTableEntry.getKey());
            Map<String, String> map = CheckTableDataUtil.getAssoAndCollField(assoAndCollTable.getKey(), configString);
            if(map.size() != 0){
                Long assoAndCollPolygonId;
                String selectSql = GenerateSqlUtil.generateSelectSqlByField(databaseName, assoAndCollTable.getKey() + suffixString, assoAndCollTableEntry.getValue(), polygonId);
                List<Map<String, Object>> dataList = selectData(selectSql);
                String primaryKey = TableOperationUtil.getPrimaryKey(assoAndCollTable);
                if(dataList != null){
                    for(Map<String, Object> data : dataList){
                        assoAndCollPolygonId = Long.valueOf(data.get(primaryKey).toString());
                        recursiveDeleteAssoAndCollData(map, assoAndCollPolygonId, configString, databaseName, isHistoryTable, version);
                    }
                }
            }

            Map<String, Field> assoAndCollFieldsMap = assoAndCollTable.getFields();
            for(Map.Entry<String, Field> entry : assoAndCollFieldsMap.entrySet()){
                Field field = entry.getValue();
                if(field.isParentID()){
                    parentIdFieldName = field.getKey();
                }
            }
            String assoAndCollTableName = PostgreSqlUtil.buildTableNameString(databaseName, assoAndCollTableEntry.getKey(), false, isHistoryTable);
            String deleteAssoAndCollTableDataSql;
            if(isHistoryTable){
                deleteAssoAndCollTableDataSql = "delete from " + PostgreSqlUtil.buildTableNameString(databaseName, assoAndCollTableEntry.getKey(), false, true) + " where " + PostgreSqlUtil.transformString(assoAndCollTableEntry.getValue()) + " = " + polygonId + " and " + CommonConstant.FIX_FIELD_VERSION + " >= " + version + ";";
            }
            else{
                deleteAssoAndCollTableDataSql = GenerateSqlUtil.genDeleteSql(assoAndCollTableName, PostgreSqlUtil.transformString(parentIdFieldName), polygonId);
            }
            String deleteAssoAndCollTableResult = delete(deleteAssoAndCollTableDataSql);
            if(deleteAssoAndCollTableResult.equals(CommonConstant.FAILED)){
                throw new RuntimeException("删除" + assoAndCollTableEntry.getKey() + "关联表的数据失败");
            }
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String rollBackDataForVersion(Task task, Table table, String updateSqlForRootTable, Long polygonId, int version) throws IOException{
        String updateResult = updateSqlString(updateSqlForRootTable);
        String tableName = table.getKey();
        if(updateResult.equals(CommonConstant.FAILED)){
            logger.info("回滚更新根表" + tableName + "主键为" + polygonId + "版本为" + version + "的数据失败");
            throw new RuntimeException("回滚更新根表" + tableName + "主键为" + polygonId + "版本为" + version + "的数据失败");
        }

        // 删除关联表和多媒体表的数据 (这些数据不能更新只能先删除再增加)
        String deleteResult = deleteDataByPolygonId(task.getDatabaseName(), table, polygonId, task.getConfig(), false);
        if(deleteResult.equals(CommonConstant.FAILED)){
            logger.info("删除根表" + tableName + "的关联表和多媒体表数据失败");
            throw new RuntimeException("删除根表" + tableName + "的关联表和多媒体表数据失败");
        }
        String hisMediaTableNameInDB = PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), tableName, true, true);
        List<MultimediaItem> multimediaItemList = multimediaService.selectHistoryMultiMedia(hisMediaTableNameInDB, polygonId, version);
        if(multimediaItemList != null && multimediaItemList.size() != 0){
            for(MultimediaItem multimediaItem : multimediaItemList){
                String mediaTableNameInDB = PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), tableName, true, false);
                multimediaItem.setMediaTableName(mediaTableNameInDB);
                String oldMediaPath = multimediaItem.getMediaPath();
                String newMediaPath = oldMediaPath.replaceFirst(historyMediaPath, mediaPath);
                File outFile = new File(newMediaPath);
                if(!outFile.getParentFile().exists()){
                    outFile.getParentFile().mkdirs();
                }
                if(!outFile.exists()){
                    outFile.createNewFile();
                }
                try(FileInputStream fileInputStream = new FileInputStream(oldMediaPath); FileOutputStream fileOutputStream = new FileOutputStream(outFile)){
                    int copyResult = IOUtils.copy(fileInputStream, fileOutputStream);
                    if(copyResult == -1){
                        logger.info("从历史多媒体文件夹复制到文件夹中失败");
                    }
                } catch(Exception e){
                    e.printStackTrace();
                }
                multimediaItem.setMediaPath(newMediaPath);
                String insertHisToMediaResult = multimediaService.insertMultiMedia(multimediaItem);
                if(insertHisToMediaResult.equals(CommonConstant.FAILED)){
                    logger.info("历史数据多媒体表" + tableName + "_media_history插入失败");
                    throw new RuntimeException("历史数据多媒体表" + tableName + "_media_history插入失败");
                }
            }
        }

        Map<String, Object> map = TableOperationUtil.hasAssoAndCollField(table);
        if(map.size() != 0){
            recursiveInsertAssoAndCollDataToTable(task, table, polygonId, 2, version);
        }

        // 删除历史表中主键为polygonId且版本号大于等于versionId的记录,关联表及多媒体表相应的记录也要删除
        String deleteRootTableDataSql = "delete from " + PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), tableName, false, true) + " where " + PostgreSqlUtil.transformString(TableOperationUtil.getPrimaryKey(table)) + " = " + polygonId + " and " + CommonConstant.FIX_FIELD_VERSION + " >= " + version + ";";
        String deleteRootTableDataResult = delete(deleteRootTableDataSql);
        if(deleteRootTableDataResult.equals(CommonConstant.FAILED)){
            logger.info("历史数据根表" + table.getKey() + CommonConstant.HISTORY_SUFFIX + "删除主键为" + polygonId + "版本号大于" + version + "的数据错误");
            throw new RuntimeException("删除错误");
        }
        Map<String, String> assoAndCollFieldMap = CheckTableDataUtil.getAssoAndCollField(tableName, task.getConfig());
        recursiveDeleteAssoAndCollData(assoAndCollFieldMap, polygonId, task.getConfig(), task.getDatabaseName(), true, version);
        String deleteMediaResult = multimediaService.deleteMulitMediaGreaterThanVersion(hisMediaTableNameInDB, polygonId, version);
        if(deleteMediaResult.equals(CommonConstant.FAILED)){
            logger.info("历史数据根表的多媒体表" + table.getKey() + CommonConstant.MEDIA_HISTORY_SUFFIX + "删除id为" + polygonId + "版本号大于" + version + "的数据错误");
            throw new RuntimeException("删除错误");
        }
        return CommonConstant.SUCCEED;
    }

    /**
     * 根据审核数据对应的userId进行根表数据reviewer字段的更新
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String updateRootTableDataForReviewer(Task task, Map<String, Map<Long, Integer>> reviewerInfo){
        Config config = JSON.toJavaObject(JSON.parseObject(task.getConfig()), Config.class);
        Map<String, Table> tables = config.getTables();
        for(Map.Entry<String, Map<Long, Integer>> entry : reviewerInfo.entrySet()){
            String tableName = entry.getKey();
            Table table = tables.get(tableName);
            String primaryKey = TableOperationUtil.getPrimaryKey(table);
            Map<Long, Integer> reviewerDataMap = entry.getValue();
            if(reviewerDataMap != null){
                for(Map.Entry<Long, Integer> reviewerDataEntry : reviewerDataMap.entrySet()){
                    updateDataAndReviewer(PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), tableName, false, false),
                            PostgreSqlUtil.transformString(primaryKey), reviewerDataEntry.getKey(), reviewerDataEntry.getValue(), 0);
                }
            }

        }
        return CommonConstant.SUCCEED;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void updateTableStatus(String tableName, String primaryKey, Long polygonId, int status,
                                  String reason){
        updateMapper.updateStatus(tableName, primaryKey, polygonId, status, reason);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String updateDataReviewer(String tableName, String primaryKey, Long polygonId, Integer reviewer){
        int result = updateMapper.updateDataReviewer(tableName, primaryKey, polygonId, reviewer);
        if(result == 1){
            return CommonConstant.SUCCEED;
        }
        else{
            return CommonConstant.FAILED;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateDataAndReviewer(String tableName, String primaryKey, Long polygonId, Integer reviewer, int status) {
        updateMapper.updateDataReviewerAndStatus(tableName, primaryKey, polygonId, reviewer, status);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public List<Map<String, Object>> getNotPassDataId(String tableNameInDB, String primaryKey, Integer userId){
        return baseMapper.getNotPassDataId(tableNameInDB, primaryKey, userId);
    }

    @Override
    public Long getStatisticDataNumByGeoJson(String tableName, String geometryName, String geoJson){
        return baseMapper.getStatisticDataNum(tableName, geometryName, "\'" + geoJson + "\'");
    }

    @Override
    public Long getDataCount(String tableName){
        return baseMapper.getDataCount(tableName);
    }

    @Override
    public Long getHasCollectAndPatrolDataCount(String tableName){
        return baseMapper.getHasCollectAndPatrolDataCount(tableName);
    }

    @Override
    public List<Map<String, Object>> getDataCountAndTimeByUser(String tableName, Integer userId){
        return baseMapper.getDataCountAndTimeByUser(tableName, userId);
    }

    @Override
    public List<Map<String, Object>> getAllFuzzySearchData(String tableNameInDB, String fieldNameInDB, String searchKey){
        return baseMapper.getAllFuzzySearchData(tableNameInDB, fieldNameInDB, searchKey);
    }

    @Override
    public Page<Map<String, Object>> getAllFuzzySearchDataCenterPoint(String tableNameInDB, String fieldNameInDB, String geometryFieldNameInDB, String searchKey){
        return baseMapper.getAllFuzzySearchDataCenterPoint(tableNameInDB, fieldNameInDB, geometryFieldNameInDB, searchKey);
    }

    @Override
    public List<Map<String, Object>> randomSelectDataIdNotInList(String tableNameInDB, String primaryKey, List selectIdList, Integer selectDataNum, String whereClause){
        return baseMapper.randomSelectDataIdNotInList(tableNameInDB, primaryKey, selectIdList, selectDataNum, whereClause);
    }

    @Override
    public List<Integer> getIdsByReviewer(int taskId, String tableName, int reviewerId){
        Task task = taskService.getTaskById(taskId);
        Table table = JSON.toJavaObject(JSON.parseObject(task.getConfig()), Config.class).getTables().get(tableName);
        return baseMapper.getIdsByReviewer(PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), tableName,
                                                                               false, false),
                                           reviewerId,
                                           PostgreSqlUtil.transformString(TableOperationUtil.getPrimaryKey(table)));
    }

    @Override
    public Integer getReviewerByDataId(int taskId, String tableName, int dataId){
        Task task = taskService.getTaskById(taskId);
        Table table = JSON.toJavaObject(JSON.parseObject(task.getConfig()), Config.class).getTables().get(tableName);
        return baseMapper.getReviewerByDataId(PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), tableName,
                                                                                  false, false),
                                              PostgreSqlUtil.transformString(TableOperationUtil.getPrimaryKey(table)),
                                              dataId);
    }
    @Override
    public List<String> getTableColumns(String databaseName, String tableName) {
        return baseMapper.selectSingleColumn(databaseName, tableName);
    }

    @Override
    public Map<String, Map<Long, String>> getIdAndGeojsonByGeohash(int taskId, String geohash){
        Task task = taskService.getTaskById(taskId);
        Map<String, Table> rootTables = JSON.toJavaObject(JSON.parseObject(task.getConfig()), Config.class).getRootTables();
        Map<String, Map<Long, String>> result = new HashMap<>();
        for (String tableName : rootTables.keySet()) {
            Table table = rootTables.get(tableName);
            String primaryKey = TableOperationUtil.getPrimaryKey(table);
            String primaryKeyInDb = PostgreSqlUtil.buildColumnNameString(task.getDatabaseName(), tableName,primaryKey);
            String geometry = TableOperationUtil.getShapeFieldName(table);
            String geometryInDb = PostgreSqlUtil.buildColumnNameString(task.getDatabaseName(), tableName,geometry);
            String sql = "SELECT {0},{1} FROM {2} " +
                    " INNER JOIN public.task_cell_data on {0} = public.task_cell_data.data_id" +
                    " where public.task_cell_data.task_id = {3} and public.task_cell_data.table_name = {4}" +
                    " AND public.task_cell_data.cell_geohash = {5}";
            String selectIdAndGeometryDataSql = MessageFormat.format(sql,
                                                                     primaryKeyInDb, geometryInDb,
                                                                     PostgreSqlUtil.buildTableNameString(task.getDatabaseName(), tableName, false, false),
                                                                     taskId,
                                                                     PostgreSqlUtil.buildString(tableName),
                                                                     PostgreSqlUtil.buildString(geohash));
            List<Map<String, Object>> rootTableDataList = selectData(selectIdAndGeometryDataSql);
            Map<Long, String> resultMap = new HashMap<>();
            if (rootTableDataList == null) {
                result.put(tableName, null);
                continue;
            }
            for (Map<String, Object> rootTableData : rootTableDataList) {
                resultMap.put(Long.parseLong(rootTableData.get(primaryKey).toString()),
                              rootTableData.get(geometry).toString());
            }
            result.put(tableName, resultMap);
        }

        return result;
    }
}
