package com.malk.service.dingtalk.impl;

import cn.hutool.core.util.ObjectUtil;
import com.malk.server.common.FilePath;
import com.malk.server.dingtalk.DDConf;
import com.malk.server.dingtalk.DDConfigSign;
import com.malk.server.dingtalk.DDR_New;
import com.malk.service.dingtalk.*;
import com.malk.utils.UtilFile;
import com.malk.utils.UtilHttp;
import com.malk.utils.UtilList;
import com.malk.utils.UtilMap;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
@Slf4j
public class DDImplService implements DDService {

    @Autowired
    private DDClient_Workflow ddClient_workflow;

    @Autowired
    private DDClient_Attendance ddClient_attendance;

    @Autowired
    private DDClient_Contacts ddClient_contacts;

    @Autowired
    private DDClient_Storage ddClient_storage;

    @Autowired
    private FilePath filePath;

    @Autowired
    private DDConf ddConf;

    @Autowired
    private DDClient ddClient;

    /**
     * 新发起审批15s内不允许撤销, 异步执行 [审批同意/拒绝只能通过节点操作, 系统无法直接介入]   -- 异步需要中转一层进行触发, client为原子接口
     */
    @Async
    @Override
    @SneakyThrows
    public void terminateRunningApprove_async(String access_token, String processInstanceId, boolean isSystem, String subject, String description, String operatingUserId, String noteUserId) {
        ddClient_workflow.createTBTask(access_token, noteUserId, subject, description, new Date().getTime() + 16000, Arrays.asList(noteUserId), null, null, false, 20, null);
        Thread.sleep(16000);
        ddClient_workflow.terminateRunningApprove(access_token, processInstanceId, isSystem, description, operatingUserId);
    }

    /**
     * 钉钉查询假期余额返回是记录, 按照消失/天单位计算该假期类型下的余额
     */
    @Override
    public Map queryVacationQuota_balance(String access_token, String op_userid, String leave_code, String userids, int offset, int size) {
        List<Map> records = ddClient_attendance.queryVacationQuota_all(access_token, op_userid, leave_code, userids, offset, size);
        if (ObjectUtil.isNull(records)) {
            return null;
        }
        AtomicInteger balance = new AtomicInteger();
        AtomicReference<String> unit = new AtomicReference<>("");
        records.forEach(item -> {
            if (ObjectUtil.isNotNull(item.get("quota_num_per_hour"))) {
                balance.set(balance.get() + Integer.valueOf(String.valueOf(item.get("quota_num_per_hour"))) - Integer.valueOf(String.valueOf(item.get("used_num_per_hour"))));
                unit.set("小时");
            } else {
                balance.set(balance.get() + (Integer.valueOf(String.valueOf(item.get("quota_num_per_day"))) - Integer.valueOf(String.valueOf(item.get("used_num_per_day")))));
                unit.set("天");
            }
        });
        Map result = new HashMap();
        result.put("balance", balance.get() / 100f);
        result.put("unit", unit);
        return result;
    }

    /**
     * 上传审批附件
     *
     * @param urlFile      远程文件地址
     * @param fileNamePure 文件名称[后缀自动通过地址获取]
     */
    @Override
    public Map uploadFileFormUrlForOverflow(String accessToken, String userId, String urlFile, String fileNamePure) {
        // 下载到本地
        String fileName = fileNamePure + urlFile.substring(urlFile.lastIndexOf(".")).toLowerCase();
        File file = UtilFile.mkdirIfNot(fileName, filePath.getPath().getFile());
        UtilHttp.doDownload(urlFile, file);
        return uploadFileFormUrlForOverflow(accessToken, userId, file.getAbsolutePath());
    }

    /**
     * 上传审批附件
     *
     * @param filePath 已存在文件, 本地file绝对路径
     */
    @Override
    public Map uploadFileFormUrlForOverflow(String accessToken, String userId, String filePath) {
        // 获取用户unionId
        String unionId = String.valueOf(ddClient_contacts.getUserInfoById(accessToken, userId).get("unionid"));
        // 获取储存空间
        String spaceId = ddClient_storage.getSpacesInfos(accessToken, userId, null);
        // 添加空间用户权限
        List<Map> members = Arrays.asList(UtilMap.map("type, id, corpId", "USER", unionId, ddConf.getCorpId()));
        ddClient_storage.addSpacePermissions(accessToken, spaceId, "0", unionId, "EDITOR", members, null);
        // 获取空间上传信息
        DDR_New ddr_new = ddClient_storage.getUploadInfos(accessToken, spaceId, unionId, null);
        // 执行文件上传
        String resourceUrl = ((List<String>) ddr_new.getHeaderSignatureInfo().get("resourceUrls")).get(0);
        ddClient_storage.uploadFiles(resourceUrl, (Map<String, String>) ddr_new.getHeaderSignatureInfo().get("headers"), filePath);
        // 提交文件
        return ddClient_storage.commitFiles(accessToken, spaceId, unionId, ddr_new.getUploadKey(), String.valueOf(UtilList.getLast(filePath.split("/"))), "0", null);
    }

    /**
     * 上传钉盘文件
     *
     * @param urlFile 远程文件地址
     */
    @Override
    public Map uploadFileFormUrlForDingDrive(String accessToken, String unionId, String spaceId, String parentId, String urlFile, String fileNamePure) {
        // 下载到本地 [兼容中文]
        // String fileName = URLDecoder.decode(String.valueOf(UtilList.getLast(urlFile.split("/"))));
        File file = UtilFile.mkdirIfNot(fileNamePure, filePath.getPath().getFile());
        UtilHttp.doDownload(urlFile, file);
        // 获取空间上传信息
        DDR_New ddr_new = ddClient_storage.getUploadInfos(accessToken, spaceId, unionId, null);
        // 执行文件上传
        String resourceUrl = ((List<String>) ddr_new.getHeaderSignatureInfo().get("resourceUrls")).get(0);
        ddClient_storage.uploadFiles(resourceUrl, (Map<String, String>) ddr_new.getHeaderSignatureInfo().get("headers"), file.getAbsolutePath());
        // 提交文件
        Map dentry = ddClient_storage.commitFiles(accessToken, spaceId, unionId, ddr_new.getUploadKey(), fileNamePure, parentId, null);
        // 删除本地临时文件
        UtilFile.deleteFile(file.getAbsolutePath());
        return dentry;
    }

    /**
     * 判断员工是否在指定部门
     */
    @Override
    public boolean matchDepartment(String access_token, String userId, List<Long> deptIds) {
        List<Number> deptIdList = (List<Number>) ddClient_contacts.getUserInfoById(access_token, userId).get("dept_id_list");
        boolean isMatch = false;
        // 兼容多部门场景
        for (Number deptId : deptIdList) {
            // ppExt: 不要直接使用 Number 作为类型, 不同基本类型比较值时会有偏差 [可以作为父类接受数据, 避免直接强制类型转换错误, 再进行基本类型处理]
            isMatch = _matchDepartment(access_token, deptId.longValue(), deptIds);
            if (isMatch) {
                break;
            }
        }
        return isMatch;
    }

    /// 递归: 判断员工是否在指定部门
    boolean _matchDepartment(String access_token, long dept_id, List<Long> deptIds) {
        boolean isMatch = false;
        // 判断入参 [同样作为递归出口]
        if (dept_id == DDConf.TOP_DEPARTMENT) {
            return false;
        }
        if (deptIds.contains(dept_id)) {
            isMatch = true;
        }
        // 递归出口 [查询上级部门匹配]
        if (!isMatch) {
            Map deptInfo = ddClient_contacts.getDepartmentInfo(access_token, dept_id);
            long parentId = UtilMap.getLong(deptInfo, "parent_id");
            return _matchDepartment(access_token, parentId, deptIds);
        }
        return isMatch;
    }

    /**
     * 获取员工所属部门全路径
     */
    @Override
    public List<Map> getUserDepartmentHierarchy(String access_token, String userId) {
        // PRD: 一个人存在多个部门默认取第一个
        List<Number> deptIdList = (List<Number>) ((List<Map>) ddClient_contacts.listParentByUser(access_token, userId).get("parent_list")).get(0).get("parent_dept_id_list");
        List<Map> deptInfo = new ArrayList();
        // Number 仅仅作为数据类型声明, 避免比较类型不一致导致判定问题
        for (Number deptId : deptIdList) {
            if (deptId.longValue() == DDConf.TOP_DEPARTMENT) {
                continue;
            }
            deptInfo.add(UtilMap.map("id, name", deptId, ddClient_contacts.getDepartmentInfo(access_token, deptId.longValue()).get("name")));
        }
        Collections.reverse(deptInfo);
        return deptInfo;
    }

    /**
     * 获取员工所属部门路径层级 [名称拼接]
     */
    @Override
    public String getUserDepartmentHierarchyJoin(String access_token, String userId, String delimiter) {
        return String.join(delimiter, getUserDepartmentHierarchy(access_token, userId).stream().map(dept -> dept.get("name").toString()).collect(Collectors.toList()));
    }

    /**
     * jsApi 注册
     */
    @Override
    public Map registerJsApi(String url, String nonceStr) {
        String jsTicket = ddClient.getJsApiTicket(ddClient.getAccessToken());
        long timeStamp = new Date().getTime();
        String signature = DDConfigSign.sign(jsTicket, nonceStr, timeStamp, url);
        return UtilMap.map("nonceStr, agentId, timeStamp, corpId, signature", nonceStr, ddConf.getAgentId(), timeStamp, ddConf.getCorpId(), signature);
    }

    /**
     * jsApi 注册 - 动态
     */
    @Override
    public Map registerJsApi(String accessToken, String url, String nonceStr) {
        String jsTicket = ddClient.getJsApiTicket(accessToken);
        long timeStamp = new Date().getTime();
        String signature = DDConfigSign.sign(jsTicket, nonceStr, timeStamp, url);
        return UtilMap.map("nonceStr, agentId, timeStamp, corpId, signature", nonceStr, ddConf.getAgentId(), timeStamp, ddConf.getCorpId(), signature);
    }

    /**
     * 免登code获取用户信息
     */
    @Override
    public Map getUserInfoByCode(String code) {
        Map rsp = ddClient.getUserInfoByCode(ddClient.getAccessToken(), code);
        return ddClient_contacts.getUserInfoById(ddClient.getAccessToken(), rsp.get("userid").toString());
    }
}
