package com.persagy.proxy.migration.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.common.constant.ResponseCode;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.common.model.response.CommonResult;
import com.persagy.dmp.common.utils.JsonHelper;
import com.persagy.dmp.migrate.client.DigitalMigrateLogFacade;
import com.persagy.dmp.rwd.migrate.entity.SyncData;
import com.persagy.proxy.adm.request.AdmQueryCriteria;
import com.persagy.proxy.adm.request.AdmResponse;
import com.persagy.proxy.adm.utils.AdmContextUtil;
import com.persagy.proxy.adm.utils.AdmQueryCriteriaHelper;
import com.persagy.proxy.common.entity.DmpResult;
import com.persagy.proxy.common.entity.InstanceUrlParam;
import com.persagy.proxy.migration.constant.MigrationConstant;
import com.persagy.proxy.migration.constant.MigrationObjType;
import com.persagy.proxy.migration.handler.DataMigrationHandler;
import com.persagy.proxy.migration.model.DataMigrationExcel;
import com.persagy.proxy.migration.model.MigrationInfo;
import com.persagy.proxy.migration.service.MigrationLogService;
import com.persagy.proxy.object.model.AdmProject;
import com.persagy.proxy.object.service.IAdmProjectService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author lvxianyun
 * @title: adm 交付的数据同步到现场，TODO 需要提供中台的连接，可以使用接口传，也可以配置在项目中
 * @projectName adm-middleware
 * @description: 该方式会覆盖被同步方数据（以adm交付为主）
 * @date 2021/12/1 16:07
 */
@Slf4j
@RestController
@RequestMapping("/data/migration")
@RequiredArgsConstructor
public class DataMigrationController {
    private final DataMigrationHandler dataMigrationHandler;
    private final IAdmProjectService projectService;
    private final MigrationLogService logService;
    /**
     * 导出待迁移的数据信息
     *
     * @param response
     * @migrationInfo :
     *  targetUrl  现场中台调用的url, eg:http://develop.persagy.com/dmp-rwd
     *  targetGroupCode 现场集团编码
     *  targetProjectId 现场项目id
     *
     *  return excel
     */
    @PostMapping("/excel")
    public void migrateForExcel(HttpServletResponse response, @RequestBody MigrationInfo migrationInfo) {
        //调用服务迁移数据，返回处理结果excel
        try {

            if (migrationInfo == null || StrUtil.isBlank(migrationInfo.getTargetUrl())) {
                throw new BusinessException(ResponseCode.A0402.getCode(), ResponseCode.A0402.getDesc());
            }
            InstanceUrlParam context = AdmContextUtil.toDmpContext();
            //确定集团编码 和 项目id
            ensureParamValue(migrationInfo, context);
            //验证目标项目地址是否正确
            try {
                boolean valid = validTargetUrl(migrationInfo, context);
                if (!valid) {
                    getErrorExcel(response, MigrationConstant.ERROR);
                } else {
                    //导出数据
                    processDataForExcel(response, migrationInfo, context);
                }
            }catch (cn.hutool.http.HttpException e)   {
                getErrorExcel(response, e.getMessage());

            } catch (Exception e) {
                e.printStackTrace();
                getErrorExcel(response, MigrationConstant.ERROR);
            }

        } catch (Exception e) {
            e.printStackTrace();
            getErrorExcel(response, e.getMessage());
        }
    }

    /**
     * 迁移数据,结果入库
     *
     * @migrationInfo :
     *  targetUrl  现场中台调用的url, eg:http://develop.persagy.com/dmp-rwd
     *  targetGroupCode 现场集团编码
     *  targetProjectId 现场项目id
     *
     *  return AdmResponse
     */
    @PostMapping("/start")
    public AdmResponse migrateForLog(@RequestBody MigrationInfo migrationInfo) {
        InstanceUrlParam context = AdmContextUtil.toDmpContext();

        if(migrationInfo == null || StrUtil.isBlank(migrationInfo.getTargetUrl())){
            throw new BusinessException(ResponseCode.A0402.getCode(), ResponseCode.A0402.getDesc());
        }
        //确定集团编码 和 项目id
        ensureParamValue(migrationInfo, context);
        //验证目标项目地址是否正确
        try {
            boolean valid = validTargetUrl(migrationInfo, context);
            if(!valid){
                return AdmResponse.failure(MigrationConstant.ERROR);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return AdmResponse.failure(MigrationConstant.ERROR +":"+ e.getMessage());
        }

        dataMigrationHandler.handleAndSaveLog(context, migrationInfo);

        AdmQueryCriteria admQueryCriteria = new AdmQueryCriteria();
        admQueryCriteria.addFilters("targetId = 'error'");
        admQueryCriteria.addFilters("projectId = '" + context.getProjectId() + "'");
        admQueryCriteria.setOnlyCount(true);
        AdmResponse response = queryMigrateForLog(admQueryCriteria);
        if(response.getMessage().equals(DmpResult.SUCCESS) && response.getCount() > 5){
            return AdmResponse.failure("出现错误次数："+ response.getCount()+"次，请使用导出检查数据是否迁移成功");
        }
        return AdmResponse.success();
    }

    /**
     * 迁移数据,执行脚本获取
     */
    public void getMigrateSqlFile(){

    }

    /**
     * 获取迁移数据日志
     *
     * @AdmQueryCriteria
     * {
     *     "order": "lastUpdate asc, objId asc",
     *     "pageNumber": 1,
     *     "pageSize": 15,
     *     "filters": "projectId='Pj9909990004';sign=2;type='create';objectType='building'"
     * }
     *
     *  return AdmResponse
     */
    @PostMapping("/log")
    public AdmResponse queryMigrateForLog(@RequestBody AdmQueryCriteria request) {
        InstanceUrlParam context = AdmContextUtil.toDmpContext();
        return logService.queryMigrationLog(context, request);
    }




    private void ensureParamValue(MigrationInfo migrationInfo, InstanceUrlParam context){
        if(migrationInfo.getTargetSign() == 0){
            migrationInfo.setTargetGroupCode(context.getGroupCode());
            migrationInfo.setTargetProjectId(context.getProjectId());
        }
        if(migrationInfo.getTargetSign() == 1){
            if(StrUtil.isBlank(migrationInfo.getTargetGroupCode())){
                migrationInfo.setTargetGroupCode(context.getGroupCode());
            }
            if(StrUtil.isBlank(migrationInfo.getTargetProjectId())){
                migrationInfo.setTargetProjectId(context.getProjectId());
            }
        }

    }

    private boolean validTargetUrl(MigrationInfo migrationInfo, InstanceUrlParam context) throws Exception{
        StringBuffer url = new StringBuffer(migrationInfo.getTargetUrl() + MigrationConstant.DT_OBJECT);
        url.append("groupCode=").append(migrationInfo.getTargetGroupCode());
        url.append("&projectId=").append(migrationInfo.getTargetProjectId());
        if(StrUtil.isNotEmpty(context.getUserId())){
            url.append("&userId=").append(context.getUserId());
        }
        if(StrUtil.isNotEmpty(context.getAppId())) {
            url.append("&appId=").append(context.getAppId());
        }
        QueryCriteria queryCriteria = new QueryCriteria();
        queryCriteria.setOnlyCount(true);
        ObjectNode criteria = JsonNodeFactory.instance.objectNode();
        criteria.put("valid", 1);
        criteria.put("objType", MigrationObjType.project.getCode());
        queryCriteria.setCriteria(criteria);

        String requestBody = JsonHelper.toJsonStr(queryCriteria);
        String response = HttpUtil.post(url.toString(), requestBody, 3000);
        if(StrUtil.isNotEmpty(response) && response.contains("<!DOCTYPE html>")|| response.equals("Bad Gateway")){
            return false;
        }
        CommonResult<List> commonResult = JSONUtil.toBean(response, CommonResult.class);
        if(commonResult.getResult().equals(DmpResult.SUCCESS)){
            return true;
        }
        return false;
    }

    private void getErrorExcel(HttpServletResponse response, String errorMsg){
        response.setContentType("application/vnd.ms-excel");
        String encode = StandardCharsets.UTF_8.name();
        response.setCharacterEncoding(encode);

        List<DataMigrationExcel> dataMigrationExcels = new ArrayList<>(1);
        DataMigrationExcel dataMigrationExcel = new DataMigrationExcel();
        dataMigrationExcel.setId("error");
        dataMigrationExcel.setMark(errorMsg);
        dataMigrationExcels.add(dataMigrationExcel);
        response.setHeader("Content-disposition", "attachment;filename=error.xlsx");
        try {
            EasyExcel.write(response.getOutputStream(), DataMigrationExcel.class).sheet("sheet1").doWrite(dataMigrationExcels);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void processDataForExcel (HttpServletResponse response, MigrationInfo migrationInfo, InstanceUrlParam context) throws IOException{
        response.setContentType("application/vnd.ms-excel");
        String encode = StandardCharsets.UTF_8.name();
        response.setCharacterEncoding(encode);
        AdmQueryCriteria projectRequest = new AdmQueryCriteria();
        projectRequest.setName(AdmProject.OBJ_TYPE);
        projectRequest.setFilters("id = '" + context.getProjectId() + "';");
        AdmResponse responsePro = projectService.doQuery(AdmContextUtil.toDmpContext(), projectRequest, AdmProject.class);

        if (CollUtil.isEmpty(responsePro.getContent())) {
            throw new BusinessException(ResponseCode.A0402.getCode(), ResponseCode.A0402.getDesc());
        } else {
            AdmProject admProject = (AdmProject) responsePro.getContent().get(0);
            // 防止中文乱码
            String fileName = admProject.getLocalName() + "_数据迁移";

            fileName = URLEncoder.encode(fileName, encode);

            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            //验证字典 defineType defineInfo
            Map<String, List<DataMigrationExcel>> validRes = dataMigrationHandler.handleDicData(context, migrationInfo);
            //字典验证失败
            try {
                if(CollUtil.isNotEmpty(validRes) && validRes.size()>0){
                    returnExcel(response, validRes);
                }else{
                    Map<String, List<DataMigrationExcel>> results = dataMigrationHandler.handleAndGetExcelList(context, migrationInfo);
                    //每个迁移的表写成一个sheet
                    if (CollUtil.isNotEmpty(results)) {
                        returnExcel(response, results);
                    }
                }
            }catch (Exception e){
                throw new BusinessException(e);
            }
        }
    }

    private void returnExcel(HttpServletResponse response, Map<String, List<DataMigrationExcel>> results) throws Exception{
        int sheetNo = 0;
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), DataMigrationExcel.class).build();
        try {
            for (String sheet : results.keySet()) {
                WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, sheet).build();
                excelWriter.write(results.get(sheet), writeSheet);
                sheetNo++;
            }
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }
}
