package com.persagy.dmp.rwd.migrate.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.persagy.dmp.common.constant.ResponseCode;
import com.persagy.dmp.common.context.AppContext;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.common.model.response.CommonResult;
import com.persagy.dmp.common.utils.ResultHelper;
import com.persagy.dmp.mybatis.helper.DynamicDataSourceHelper;
import com.persagy.dmp.rwd.migrate.service.IDigitalMigrateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.*;

/**
 * 版本定义Controller
 * @author Charlie Yu
 * @date 2021-06-23
 */
@Slf4j
@RestController
@RequestMapping("/migrate/digital")
public class DigitalMigrateController {

    @Autowired
    private IDigitalMigrateService service;

    /**
     * 启动数据迁移
     * 迁移范围：自定义数据字典、对象、关系
     * @param requestMap
     * {
     *     "orgUrl":"数据中台组织服务url前缀（不含最后的/)",
     *     "oldSchema":"数据中台使用的数据库实例",
     *     "groupCode":"指定集团编码【单个或数组】 - 为空表示不限集团",
     *     "projectId":"指定项目ID【单个或数组】 - 为空表示不限项目"
     * }
     */
    @PostMapping("/start")
    public CommonResult<String> query(@RequestBody Map<String, Object> requestMap) {
        // 处理参数
        final String orgUrl = MapUtil.getStr(requestMap, "orgUrl");
        final String oldSchema = MapUtil.getStr(requestMap, "oldSchema");
        if(StrUtil.isBlank(orgUrl) || StrUtil.isBlank(oldSchema) ) {
            throw new BusinessException(ResponseCode.A0400.getCode(), ResponseCode.A0400.getDesc());
        }
        // 集团编码
        final List<String> groupCodes = new ArrayList<>();
        Object groupCode = requestMap.get("groupCode");
        if(groupCode != null) {
            if(groupCode instanceof String) {
                groupCodes.add((String) groupCode);
            } else if(groupCode instanceof List) {
                groupCodes.addAll((List<String>) groupCode);
            }
        }
        // 项目ID
        final List<String> projectIds = new ArrayList<>();
        Object projectId = requestMap.get("projectId");
        if(projectId != null) {
            if(projectId instanceof String) {
                projectIds.add((String) projectId);
            } else if(projectId instanceof List) {
                projectIds.addAll((List<String>) projectId);
            }
        }
        // 启动迁移 执行时间较长，后台线程运行 TODO 需要加分布式锁，并支持查看执行情况
        ThreadUtil.execute(() -> startMigrate(groupCodes, projectIds, orgUrl, oldSchema));
        return ResultHelper.success();
    }

    /**
     * 启动数据迁移
     * @param groupCodes 指定集团编码 - 为空表示不限集团
     * @param projectIds 指定项目ID - 为空表示不限项目
     * @param orgUrl 数据中台组织服务url前缀（不含最后的/)
     * @param oldSchema 数据中台使用的数据库实例
     */
    private void startMigrate(List<String> groupCodes, List<String> projectIds, String orgUrl, String oldSchema) {
        Set<String> groupCodeSet = queryGroupCodes(orgUrl, groupCodes);
        if(CollUtil.isEmpty(groupCodeSet)) {
            log.error("没有查询到集团信息！");
            return;
        }
        log.info("#########数据迁移已启动##########");
        // 按集团循环处理
        for(String groupCode:groupCodeSet) {
            log.info(StrUtil.format("开始处理{}集团", groupCode));
            // 获取待处理的项目
            Set<String> projectIdSet = queryProjectIds(orgUrl, groupCode, projectIds);
            if(CollUtil.isEmpty(projectIdSet)) {
                log.info(StrUtil.format("结束处理{}集团：没有找到需要迁移的项目", groupCode));
                continue;
            }
            // 切换集团数据源
            AppContext.getContext().setGroupCode(groupCode);
            DynamicDataSourceHelper.loadDataSource();
            // 集团数据迁移
            service.doMigrate(oldSchema, groupCode, projectIdSet);
            log.info(StrUtil.format("结束处理{}集团：迁移完成!!!", groupCode));
        }
        log.info("#########数据迁移已结束##########");
    }

    /**
     * 查询项目主键
     * @param orgUrl 组织服务url
     * @param groupCode 集团编码，非空
     * @param ids 指定的项目ID，为空表示所有
     * @return
     */
    private Set<String> queryProjectIds(String orgUrl, String groupCode, List<String> ids) {
        // 调用数据中台接口查询项目信息
        String queryProjectUrl = StrUtil.format(orgUrl + "/org/project/query?groupCode={}&userId=systemId", groupCode);
        String response = HttpUtil.post(queryProjectUrl, "{}");
        JSONArray projects = fetchResult(response);
        if(CollUtil.isEmpty(projects)) {
            return null;
        }
        // 收集项目ID
        Set<String> projectIds = new HashSet<>();
        for(int i = 0;i < projects.size();i++) {
            JSONObject project = projects.getJSONObject(i);
            String id = project.getString("id");
            boolean valid = project.getBooleanValue("status");
            // 已删除或没有编码的不处理
            if(StrUtil.isBlank(id) || !valid) {
                continue;
            }
            projectIds.add(id);
        }
        // 处理返回结果
        if(CollUtil.isEmpty(projectIds)) {
            return null;
        } else if(CollUtil.isNotEmpty(ids)) {
            // 有指定的集团，则取交集
            return CollUtil.intersectionDistinct(projectIds, ids);
        } else {
            // 没有指定集团，则返回所有有效集团
            return projectIds;
        }
    }

    /**
     * 查询集团编码
     * @param orgUrl 组织服务url
     * @param codes 指定集团编码 - 为空表示所有集团
     * @return
     */
    private Set<String> queryGroupCodes(String orgUrl, List<String> codes) {
        // 调用数据中台接口查询集团信息
        String queryGroupUrl = orgUrl + "/org/group/query";
        String groupResponse = HttpUtil.post(queryGroupUrl, "{}");
        JSONArray groups = fetchResult(groupResponse);
        if(CollUtil.isEmpty(groups)) {
            return null;
        }
        // 收集集团编码
        Set<String> groupCodes = new HashSet<>();
        for(int i = 0;i < groups.size();i++) {
            JSONObject group = groups.getJSONObject(i);
            String code = group.getString("code");
            boolean valid = group.getBooleanValue("status");
            // 已删除或没有编码的不处理
            if(StrUtil.isBlank(code) || !valid) {
                continue;
            }
            groupCodes.add(code);
        }
        // 处理返回结果
        if(CollUtil.isEmpty(groupCodes)) {
            return null;
        } else if(CollUtil.isNotEmpty(codes)) {
            // 有指定的集团，则取交集
            return CollUtil.intersectionDistinct(groupCodes, codes);
        } else {
            // 没有指定集团，则返回所有有效集团
            return groupCodes;
        }
    }

    /**
     * 提取结果
     * @param response
     * @return
     */
    private JSONArray fetchResult(String response) {
        JSONObject responseObject = JSONObject.parseObject(response);
        // 如果结果不成功，抛出失败结果
        if(!StrUtil.equals("success", responseObject.getString("result"))) {
            throw new BusinessException(responseObject.getString("message"));
        }
        JSONArray datas = responseObject.getJSONArray("data");
        return CollUtil.isEmpty(datas) ? null : datas;
    }
}
