package com.malk.aipocloud.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.malk.aipocloud.repository.dao.ABEventDao;
import com.malk.aipocloud.repository.dao.ABUserDao;
import com.malk.aipocloud.repository.entity.AbEventPo;
import com.malk.aipocloud.repository.entity.AbUserPo;
import com.malk.aipocloud.service.ABClient;
import com.malk.server.dingtalk.DDConf;
import com.malk.server.dingtalk.DDFormComponentDto;
import com.malk.service.dingtalk.DDClient;
import com.malk.service.dingtalk.DDClient_Contacts;
import com.malk.service.dingtalk.DDClient_Notice;
import com.malk.service.dingtalk.DDClient_Workflow;
import com.malk.utils.*;
import lombok.SneakyThrows;
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.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ABImplClient implements ABClient {

    @Autowired
    private DDClient ddClient;

    @Autowired
    private DDClient_Contacts ddClient_contacts;

    @Autowired
    private ABUserDao abUserDao;

    /**
     * 通讯录同步
     */
    @Override
    @Synchronized
    public void syncContact() {

        List<Long> deptList = ddClient_contacts.getDepartmentId_all(ddClient.getAccessToken(), true, DDConf.TOP_DEPARTMENT);
        for (long deptId : deptList) {
            List<String> userIds = ddClient_contacts.listDepartmentUserId(ddClient.getAccessToken(), deptId);
            if (userIds.size() == 0) {
                continue;
            }
            String deptName = String.valueOf(ddClient_contacts.getDepartmentInfo(ddClient.getAccessToken(), deptId).get("name"));
            for (String userId : userIds) {
                Map userInfo = ddClient_contacts.getUserInfoById(ddClient.getAccessToken(), userId);
                log.info("同步人员, {}", userInfo);
                if (StringUtils.isBlank(UtilMap.getString(userInfo, "email"))) {
                    continue;
                }
                AbUserPo userPo = AbUserPo.builder()
                        .userId(userId)
                        .name(String.valueOf(userInfo.get("name")))
                        .dept(deptName)
                        .email(String.valueOf(userInfo.get("email")))
                        .build();
                userPo.upsert(abUserDao.findByUserId(userId)); // 匹配更新
                abUserDao.save(userPo);
            }
        }
    }

    @Autowired
    private DDClient_Workflow ddClient_workflow;

    @Autowired
    private ABEventDao abEventDao;

    @Autowired
    private DDConf ddConf;

    // DLP
    private static final String CODE = "PROC-FA464E73-56A4-43DF-A9DD-CCE11114C129";
    //private static final String CODE = "PROC-B501FF97-E6FC-41D5-8704-D988D4878F45"; // poc
    private static final String USER = "dingTalk";
    private static final int DLP_DAYS = 1;
    private static final int DLP_NUM = 10;

    private String _getUrl(String path) {
        return "https://10.14.2.5:8443" + path; // 内网
//        return "https://ucss.aipocloud.com:8443" + path; // 公网
    }

    // 认证逻辑: token作为后续接口密码
    private String _getToken() {

        Map header = UtilMap.map("content-type, user-agent", "application/json, QKAct-External-Client");
        Map body = UtilMap.map("client-id", USER);
        String rsp = UtilHttp.doRequest(UtilHttp.METHOD.POST, _getUrl("/qkact/v0/checkin"),
                header, null, body, null, "dingtalk_dlp", "SkyP@ssw0rd1!");
        Map data = (Map) JSON.parse(rsp);
        return UtilMap.getString(data, "access-token");
    }

    /**
     * 同步审批单 [prd 审批去重逻辑：用户 + 通道 + md5 + 有效期（审批通过且在生效时间内）] - 查询5min, 避免拒绝\撤销重复推送问题
     */
    @Override
    @Synchronized
    public void syncProcess(LocalDateTime sTime, LocalDateTime eTime) {

        String parttern = "yyyy-MM-dd'T'HH:mm:ss";
        String pwd = _getToken();
        Map query = (Map) UtilFile.readJsonObjectFromResource("static/json/query.json");  // 事件查询条件: 查询5min, 避免拒绝\撤销重复推送问题
        query = (Map) JSON.parse(JSON.toJSONString(query).replace("xxxx-xx-xxSxx:xx:xx", UtilDateTime.formatLocal(sTime, parttern)).replace("xxxx-xx-xxExx:xx:xx", UtilDateTime.formatLocal(eTime, parttern)));

        Map header = UtilMap.map("content-type, user-agent", "application/json, QKAct-External-Client");
        String rsp = UtilHttp.doRequest(UtilHttp.METHOD.POST, _getUrl("/sps/v1/proxy/dlp-endpoint/_search"), header, null, query, null, USER, pwd);
        Map data = (Map) JSON.parse(rsp);
        List<Map> list = (List<Map>) UtilMap.getList(UtilMap.getMap(data, "hits"), "hits");
        for (Map record : list) {

            record = UtilMap.getMap(record, "_source");
            List<Map> attachments = (List<Map>) record.get("attachments");
            if (UtilList.isEmpty(attachments)) {
                continue;
            }
            // prd 审批去重逻辑：用户 + 通道 + md5 + 有效期（审批通过且在生效时间内）
            Map<String, String> source = UtilMap.getMap(record, "source");
            int channel = UtilMap.getInt(record, "channelType");
            Date detect = UtilDateTime.parse(String.valueOf(record.get("detectDateTime")), "yyyy-MM-dd'T'HH:mm:ss.SSS");
            Date expire = new Date(detect.getTime() + DLP_DAYS * 24 * 60 * 60 * 1000L);
            List<AbEventPo> eventPoList = abEventDao.queryList(UtilDateTime.convertToDateFromLocalDateTime(sTime), UtilDateTime.convertToDateFromLocalDateTime(eTime), channel, source.get("logonName"), expire);
            if (eventPoList.stream().filter(item -> attachments.get(0).get("fileMd5").equals(item.getAttachmentsDisplay().get(0).get("fileMd5"))).findAny().isPresent()) {
                log.info("记录已存在, {}", record);
                continue;
            }
            // 邮箱错误通知管理员
            String userId = "";
            AbUserPo userPo = abUserDao.findByEmail(source.get("mail"));
            if (ObjectUtil.isNull(userPo)) {
                Map message = UtilMap.map("msgtype", "text");
                message.put("text", UtilMap.map("content", "「" + source.get("mail") + "」: 未匹配到客户，需要维护花名册邮箱信息。"));
                ddClient_notice.sendNotification(ddClient.getAccessToken(), Arrays.asList(ddConf.getOperator()), null, false, message);
                continue;
            }
            userId = userPo.getUserId();
            //userId = ddConf.getOperator(); // test
            List<Map<String, String>> policies = UtilMap.getList(record, "matchedPolicies");
            List<String> tPolicies = policies.stream().map(item -> item.get("name")).collect(Collectors.toList());
            List<Map<String, String>> rules = UtilMap.getList(record, "extendIncidentRules");
            List<String> tRules = rules.stream().map(item -> item.get("name")).collect(Collectors.toList());
            List<Map<String, String>> destinations = UtilMap.getList(record, "destinations");
            List<String> tDestinations = destinations.stream().map(item -> item.get("displayName")).collect(Collectors.toList());

            AbEventPo eventPo = AbEventPo.builder()
                    .serialId(UtilMap.getInt(record, "serialId"))
                    .userId(userId)
                    .userName(userPo.getName())
                    .logonName(source.get("logonName"))
                    .department(source.get("department"))
                    .fqdn(source.get("fqdn"))
                    .ipAddress(source.get("ipAddress"))
                    .email(source.get("mail"))
                    .detectDateTime(detect)
                    .expire(expire)
                    .channelType(channel)
                    .channelTypeDisplay(AbEventPo.convertChannelTypeDisplay(channel))
                    .policiesName(String.join(", ", tPolicies))
                    .classificationLevelNames(String.join(", ", UtilMap.getList(record, "classificationLevelNames")))
                    .rulesName(String.join(", ", tRules))
                    .tagNames(String.join(", ", UtilMap.getList(record, "tagNames")))
                    .displayName(String.join(", ", tDestinations))
                    .attachments(JSON.toJSONString(attachments))
                    .attachmentsDisplay(attachments)
                    .build();
            // 组件数据格式化: prd 发送通道钉钉取值发送的目标名称
            Map ruleForm = UtilMap.map("logonName, department, fqdn, detectDateTime, displayName, tagNames",
                    "用户姓名, 用户所在部门, 计算机名称, DLP事件产生时间, 发送通道, 文件标签名称");
            Map ruleDetail = UtilMap.map("attachmentsDisplay", UtilMap.map("filename, fileSize, fileMd5", "附件名称, 附件大小, 附件MD5"));
            ruleForm.put("attachmentsDisplay", "附件信息");

            // 推送钉钉审批
            Map formData = (Map<String, ?>) JSON.parse(JSON.toJSONString(eventPo));
            detect = new Date(detect.getTime() + 8 * 60 * 60 * 1000L); // prd utc转gmt显示
            formData.putAll(UtilMap.map("detectDateTime, logonName", UtilDateTime.formatDateTime(detect), JSON.toJSONString(Arrays.asList(userId)))); // 默认值
            List<Map> formValues = DDFormComponentDto.formatComponentValues(formData, ruleForm, ruleDetail);
            Map extInfo = UtilMap.map("dept_id", DDConf.TOP_DEPARTMENT);
            String processInstanceId = ddClient_workflow.doProcessInstances(ddClient.getAccessToken(), userId, CODE, formValues, extInfo);
            eventPo.setInstanceId(processInstanceId);
            eventPo.setState("process");
            abEventDao.save(eventPo);
            log.info("推送钉钉审批, {}", eventPo);
        }
    }

    @Autowired
    private DDClient_Notice ddClient_notice;

    /**
     * 审批回调
     */
    @SneakyThrows
    @Override
    public void callbackApprove(String processCode, String processInstanceId, String state, String staffId) {
        // 回调结果
        if (CODE.equals(processCode)) {

            if (!"agree".equals(state)) {
                abEventDao.updateState(processInstanceId, state, null);
                return;
            }
            // prd: 审批去重逻辑：用户 + 通道 + md5 +有效期（审批通过且在生效时间内）
            AbEventPo abEventPo = abEventDao.findByInstanceId(processInstanceId);
            List<Map> attachments = abEventPo.getAttachmentsDisplay();
            if (attachments.isEmpty()) {
                return;
            }
            // 匹配审批人信息
            Map userInfo = ddClient_contacts.getUserInfoById(ddClient.getAccessToken(), staffId);
            Map data = UtilMap.map("approver, submitter_fqdn, approved_time, forensic", userInfo.get("name"), abEventPo.getFqdn(), new Date().getTime() / 1000L, 1);
            data.putAll(UtilMap.map("submitter_name, submitter_ip, submitter_email", abEventPo.getLogonName(), abEventPo.getIpAddress().split(",")[0], abEventPo.getEmail()));
            Map attachment = attachments.get(0);
            data.putAll(UtilMap.map("file_size, file_md5, file_name", attachment.get("fileSize"), attachment.get("fileMd5"), attachment.get("filename")));
            // prd channel 号 是100-106 的对应成22 通道进行回传
            int channel = abEventPo.getChannelType();
            if (channel >= 100 && channel <= 106) {
                channel = 22;
            }
            data.put("channel", Arrays.asList(channel));
            // 匹配单据必填项
            //List<Map<String, String>> formComponentValues = UtilMap.getList(instance, "formComponentValues");
            //String expired = formComponentValues.stream().filter(item -> "过期时间".equals(item.get("name"))).findAny().get().get("value");
            //data.put("expired_time", UtilDateTime.parse(expired, "yyyy-MM-dd HH:mm").getTime());
            //data.put("max_num", formComponentValues.stream().filter(item -> "可发送次数".equals(item.get("name"))).findAny().get().get("value"));
            data.put("expired_time", abEventPo.getExpire().getTime() / 1000L);
            data.put("max_num", DLP_NUM);

            Map body = UtilMap.map("data", Arrays.asList(data));
            Map header = UtilMap.map("content-type, user-agent", "application/json, QKAct-External-Client");
            String rsp = UtilHttp.doRequest(UtilHttp.METHOD.POST, _getUrl("/qkact/v0/dlp/incident/approval"),
                    header, null, body, null, USER, _getToken());
            abEventDao.updateState(processInstanceId, state, rsp);
            // 延迟5分钟通知
            Thread.sleep(5 * 60 * 1000);
            Map message = UtilMap.map("msgtype", "text");
            String tips = "「" + abEventPo.getUserName() + "」,您的文件：「" + attachment.get("filename") + "」通过「" + abEventPo.getDisplayName() + "」外发的审批已通过，截止「" + UtilDateTime.formatDateTime(abEventPo.getExpire()) + "」前有效，请重新发起文件外发。";
            message.put("text", UtilMap.map("content", tips));
            ddClient_notice.sendNotification(ddClient.getAccessToken(), Arrays.asList(abEventPo.getUserId()), null, false, message);
        }
    }
}
