package com.malk.service.teambition.impl;

import cn.hutool.core.util.ObjectUtil;
import com.auth0.jwt.algorithms.Algorithm;
import com.malk.server.common.McException;
import com.malk.server.dingtalk.DDConf;
import com.malk.server.teambition.TBConf;
import com.malk.server.teambition.TBR;
import com.malk.service.teambition.TBClient;
import com.malk.utils.UtilHttp;
import com.malk.utils.UtilList;
import com.malk.utils.UtilMap;
import com.malk.utils.UtilToken;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class TBClientImpl implements TBClient {

    @Autowired
    private TBConf tbConf;

    private static final Long EXPIRES_IN = 2 * 3600 * 1000L;
    private static final String TOKEN_APP_ID = "_appId";

    // 获取访问授权
    @Synchronized
    private String getAccessToken() {
        String key = "token:" + tbConf.getAppID();
        String accessToken = UtilToken.get(TBConf.NS, key);
        if (StringUtils.isNotBlank(accessToken)) return accessToken;
        Algorithm algorithm = Algorithm.HMAC256(tbConf.getAppSecret());
        long timestamp = System.currentTimeMillis();
        Date issuedAt = new Date(timestamp);
        Date expiresAt = new Date(timestamp + EXPIRES_IN);
        accessToken = com.auth0.jwt.JWT.create()
                .withClaim(TOKEN_APP_ID, tbConf.getAppID())
                .withIssuedAt(issuedAt)
                .withExpiresAt(expiresAt)
                .sign(algorithm);
        log.info("响应token, {}", accessToken);
        // token失效自动重置: TB传递过期时间2h, 重复调用就会刷新
        UtilToken.put(TBConf.NS, key, accessToken, EXPIRES_IN);
        return accessToken;
    }

    // 初始化参数: 加载token
    private Map<String, String> initHeaderToken() {
        Map header = new HashMap();
        header.put("Authorization", getAccessToken());
        header.put("X-Tenant-Id", tbConf.getTenantId());
        header.put("X-Tenant-Type", TBConf.TENANT_TYPE);
        return header;
    }

    // 操作者ID: 若是查询, 以操作人视角作为权限
    private Map<String, String> initHeaderToken(String operatorId) {
        Map header = initHeaderToken();
        header.put("x-operator-id", operatorId);
        return header;
    }

    // 加载token, 老版本接口
    private Map<String, String> initHeaderToken_Bearer() {
        Map header = initHeaderToken();
        header.put("Authorization", "Bearer " + header.get("Authorization"));
        return header;
    }

//    // 操作者ID: 若是查询, 以操作人视角作为权限
//    private Map<String, String> initHeaderToken_Bearer(String operatorId) {
//        Map header = initHeaderToken_Bearer();
//        header.put("x-operator-id", operatorId);
//        return header;
//    }

    @Override
    public List<Map> templateSearch(String ptIds, String q) {
        Map param = new HashMap();
        param.put("ptIds", ptIds);
        param.put("q", q);
        param.put("organizationId", TBConf.TENANT_TYPE);
        TBR r = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project-template/search", initHeaderToken(), param, TBR.class);
        return (List<Map>) r.getResult();
    }

    @Override
    public Map projectCreate(String name, String operatorId) {
        Map param = new HashMap();
        param.put("organizationId", TBConf.TENANT_TYPE);
        Map body = new HashMap();
        body.put("name", name);
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "/v3/project/create", initHeaderToken(operatorId), param, body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public Map projectCreateWithTemplate(String name, String templateId, String operatorId) {
        Map param = new HashMap();
        param.put("organizationId", TBConf.TENANT_TYPE);
        Map body = new HashMap();
        body.put("name", name);
        body.put("templateId", templateId);
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "/v3/project/create-from-template", initHeaderToken(operatorId), param, body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public List<Map> projectDetail(String projectIds, String name, String sourceId) {
        Map param = UtilMap.map("projectIds, name", projectIds, name);
        UtilMap.putNotEmpty(param, "sourceId", sourceId);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project/query", initHeaderToken(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<String> queryProjectTag(String projectId, String operatorId) {
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project/" + projectId + "/project-tag", initHeaderToken(operatorId), null, TBR.class);
        return (List<String>) tbr.getResult();
    }

    @Override
    public Map projectUpdate(String projectId, Map<String, String> data, String operatorId) {
        TBR tbr = (TBR) UtilHttp.doPut(tbConf.getApiHost() + "/v3/project/" + projectId, initHeaderToken(operatorId), data, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public Map updateProjectTag(String projectId, List<String> projectTagIds, String operatorId) {
        Map body = UtilMap.map("projectTagIds", projectTagIds);
        Map result = null;
        try {
            TBR tbr = (TBR) UtilHttp.doPut(tbConf.getApiHost() + "/v3/project/" + projectId + "/project-tag", initHeaderToken(operatorId), body, TBR.class);
            result = (Map) tbr.getResult();
        } catch (McException e) {
            // ppExt: 若修改分组相同, 不返回204, 会报错400, 已兼容
            if (!"400".equals(e.getCode())) {
                // 记录错误信息
                e.printStackTrace();
            }
        }
        return result;
    }

    @Deprecated
    @Override
    public List<Map> userSearch(String query) {
        Map param = UtilMap.map("orgId, query, pageSize", tbConf.getTenantId(), query, TBConf.PAGE_SIZE_LIMIT);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/org/member/search", initHeaderToken_Bearer(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> idMapQuery(String tbId, String refer, String refId) {
        Map param = UtilMap.map("tbId, refer, refId", tbId, refer, refId);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/idmap/query", initHeaderToken_Bearer(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Autowired
    private DDConf ddConf;

    @Override
    public List<Map> idMapQuery(String userId, boolean isTbID) {
        Map param = UtilMap.map(" refer, refId", "dingTalk-user", ddConf.getCorpId());
        param.put(isTbID ? "tbId" : "extraUserId", userId);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/idmap/query", initHeaderToken_Bearer(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public Map updateUser(String operatorId, List<Map> members) {
        Map body = new HashMap();
        body.put("operatorId", operatorId);
        body.put("orgId", tbConf.getTenantId());
        body.put("members", members);
        TBR tbr = (TBR) UtilHttp.doPut(tbConf.getApiHost() + "/org/member/update", initHeaderToken_Bearer(), body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public List<Map> createProjectMember(String projectId, List<String> userIds, String operatorId) {
        Map body = UtilMap.map("userIds", userIds);
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "/v3/project/".concat(projectId).concat("/member/create-v2"), initHeaderToken(operatorId), body, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> updateProjectMember(List<String> userIds, List<String> roleIds, String projectId) {
        Map body = UtilMap.map("userIds", userIds);
        body.put("roleIds", roleIds); // 合并字段避免map拆分异常, todo 待优化 map 方法
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "/v3/project/".concat(projectId).concat("/member/role/assign"), initHeaderToken(), body, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryProjectMember(String projectId, Map param) {
        param = UtilMap.put(param, "pageSize", TBConf.PAGE_SIZE_LIMIT);
        param.put("limit", TBConf.PAGE_SIZE_LIMIT);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project/" + projectId + "/member", initHeaderToken(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryProjectRoles(String projectId) {
        Map param = UtilMap.map("pageSize", TBConf.PAGE_SIZE_LIMIT);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project/" + projectId + "/role", initHeaderToken(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryTaskDetail(String taskId, String shortIds, String parentTaskId) {
        Map param = UtilMap.mapNotNull("taskId, shortIds, parentTaskId", taskId, shortIds, parentTaskId);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/task/query", initHeaderToken(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryTaskGroupList(String projectId, Map param) {
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project/" + projectId + "/stage/search", initHeaderToken(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryProjectTaskList(String projectId, Map param, QueryAll lambda) {
        param = UtilMap.put(param, "pageSize", TBConf.PAGE_SIZE_LIMIT);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project/" + projectId + "/task/query", initHeaderToken(), param, TBR.class);
        List<Map> dataList = (List<Map>) tbr.getResult();
        if (ObjectUtil.isNotNull(lambda)) {
            boolean isAll = lambda.dataList(dataList);
            if (!isAll) {
                return dataList;
            }
            if (StringUtils.isNotBlank(tbr.getNextPageToken())) {
                param.put("pageToken", tbr.getNextPageToken());
                queryProjectTaskList(projectId, param, lambda);
            }
        }
        return dataList;
    }

    @Override
    public Map updateTaskDueDate(String taskId, String dueDate, String operatorId) {
        Map body = UtilMap.map("dueDate", dueDate);
        TBR tbr = (TBR) UtilHttp.doPut(tbConf.getApiHost() + "/v3/task/" + taskId + "/dueDate", initHeaderToken(operatorId), body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public Map updateTaskExecutor(String taskId, String operatorId, String executorId, boolean disableActivity, boolean disableNotification) {
        Map body = UtilMap.map("executorId, disableActivity, disableNotification", executorId, disableActivity, disableNotification);
        TBR tbr = (TBR) UtilHttp.doPut(tbConf.getApiHost() + "/v3/task/" + taskId + "/executor", initHeaderToken(operatorId), body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public Map updateTaskInvolveMembers(String taskId, String operatorId, List<String> involveMembers, List<String> delInvolvers, boolean disableActivity, boolean disableNotification) {
        Map body = UtilMap.map("disableActivity, disableNotification", disableActivity, disableNotification);
        if (UtilList.isNotEmpty(involveMembers)) {
            body.put("involveMembers", involveMembers);
        }
        if (UtilList.isNotEmpty(delInvolvers)) {
            body.put("delInvolvers", delInvolvers);
        }
        TBR tbr = (TBR) UtilHttp.doPut(tbConf.getApiHost() + "/v3/task/" + taskId + "/involveMembers", initHeaderToken(operatorId), body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public Map updateTaskCustomField(String taskId, String operatorId, Map body) {
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "/v3/task/" + taskId + "/customfield/update", initHeaderToken(operatorId), body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public Map updateTaskFlowStatus(String taskId, String operatorId, String taskflowstatusId, String tfsUpdateNote) {
        Map body = UtilMap.map("taskflowstatusId, tfsUpdateNote", taskflowstatusId, tfsUpdateNote);
        TBR tbr = (TBR) UtilHttp.doPut(tbConf.getApiHost() + "/v3/task/" + taskId + "/taskflowstatus", initHeaderToken(operatorId), body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public List<Map> queryProjectCustomField(String projectId, Map body) {
        body = UtilMap.put(body, "pageSize", TBConf.PAGE_SIZE_LIMIT);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project/" + projectId + "/customfield/search", initHeaderToken(), body, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryProjectCustomFlowStatus(String projectId, Map body) {
        body = UtilMap.put(body, "pageSize", TBConf.PAGE_SIZE_LIMIT);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project/" + projectId + "/taskflowstatus/search", initHeaderToken(), body, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryProjectCustomFlow(String projectId, Map body) {
        body = UtilMap.put(body, "pageSize", TBConf.PAGE_SIZE_LIMIT);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project/" + projectId + "/taskflow/search", initHeaderToken(), body, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryTaskDependency(String taskId, Map body) {
        body = UtilMap.put(body, "pageSize", TBConf.PAGE_SIZE_LIMIT);
        body.put("taskId", taskId);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "v3/task/dependency", initHeaderToken(), body, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public Map removeTaskDependency(String dependencyId, String operatorId) {
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "/v3/task/dependency/" + dependencyId + "/delete", initHeaderToken(operatorId), null, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public List<Map> queryGanttBaselineList(String projectId, Map body) {
        body = UtilMap.put(body, "pageSize", TBConf.PAGE_SIZE_LIMIT);
        body.put("projectId", projectId);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/gantt/baseline", initHeaderToken(), body, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryGanttBaselineTask(String baselineId, Map param, QueryAll lambda) {
        param = UtilMap.put(param, "pageSize", 10);
        param.put("baselineId", baselineId);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/gantt/baseline-task", initHeaderToken(), param, TBR.class);
        List<Map> dataList = (List<Map>) tbr.getResult();
        if (ObjectUtil.isNotNull(lambda)) {
            boolean isAll = lambda.dataList(dataList);
            if (!isAll) {
                return dataList;
            }
            if (StringUtils.isNotBlank(tbr.getNextPageToken())) {
                param.put("pageToken", tbr.getNextPageToken());
                queryGanttBaselineTask(baselineId, param, lambda);
            }
        }
        return dataList;
    }

    @Override
    public List<Map> queryTagList(Map param) {
        param = UtilMap.put(param, "pageSize", TBConf.PAGE_SIZE_LIMIT);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project-tag/query", initHeaderToken(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public Map updateTag(String projectTagId, Map param) {
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/project-tag/" + projectTagId, initHeaderToken(), param, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public List<Map> queryProgramList(String programIds) {
        Map param = UtilMap.map("programIds", programIds);
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "/v3/program/query", initHeaderToken(), param, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryProgramList_all() {
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "/v3/program/query", initHeaderToken(), null, null, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> upsertProgramProject(String programId, List<String> projectIds, String operatorId) {
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "v3/program/" + programId + "/project/create", initHeaderToken(operatorId), UtilMap.map("projectIds", projectIds), TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public List<Map> queryProjectStatusField(String projectId) {
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "v3/project/" + projectId + "/status/customfield/list", initHeaderToken(), null, TBR.class);
        return (List<Map>) tbr.getResult();
    }

    @Override
    public Map updateProjectStatusField(String projectId, String operatorId, Map body) {
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "/v3/project/" + projectId + "/status/customfield/create", initHeaderToken(operatorId), body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public Map createProgramMember(String programId, String operatorId, List<String> users) {
        Map body = UtilMap.map("userIds", users);
        TBR tbr = (TBR) UtilHttp.doPost(tbConf.getApiHost() + "/v3/program/" + programId + "/member/create", initHeaderToken(operatorId), body, TBR.class);
        return (Map) tbr.getResult();
    }

    @Override
    public List<Map> queryProgramMember(String programId, String operatorId, Map body) {
        TBR tbr = (TBR) UtilHttp.doGet(tbConf.getApiHost() + "v3/program/" + programId + "/member", initHeaderToken(operatorId), body, TBR.class);
        return (List<Map>) tbr.getResult();
    }
}
