package com.malk.shantai.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.malk.server.common.McR;
import com.malk.server.dingtalk.DDR_New;
import com.malk.shantai.config.StEkbConfig;
import com.malk.shantai.entity.Shantai;
import com.malk.shantai.mapper.ShantaiMapper;
import com.malk.shantai.service.StDingProcService;
import com.malk.service.dingtalk.DDClient;
import com.malk.service.dingtalk.DDClient_Workflow;
import com.malk.utils.UtilHttp;
import com.malk.utils.UtilMap;
import com.malk.utils.UtilToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;

@Service
@Slf4j
public class StDingProcServiceImpl extends ServiceImpl<ShantaiMapper, Shantai> implements StDingProcService {

    @Autowired
    private DDClient_Workflow ddClient_workflow;

    @Autowired
    private DDClient ddClient;

    @Autowired
    private StEkbConfig stEkbConfig;

    @Autowired
    private ShantaiMapper shantaiMapper;

    @Value("${download.path}")
    private String filePath;


    @Value("${dingtalk.operatorUserId}")
    private String operatorUserId;

    @Value("${dingtalk.operatorUnionId}")
    private String operatorUnionId;

    @Value("${ekb.operatorStaffId}")
    private String operatorStaffId;

    //法人实体
    private static Map<String,String> CORPORATION = new HashMap<>();

    //任务状态
    private static Map<String,String> STATUS = new HashMap<>();

    static{
        CORPORATION.put("杉泰健康管理（上海）有限公司","ID01tX0xhMwgbR");
        CORPORATION.put("上海广慈太保互联网医院有限公司","LY4cw6EvcNZQ00");
        CORPORATION.put("苏州杉泰大药房有限公司","ID_3IzESoE1lxM");
        CORPORATION.put("成都杉泰互联网医院有限公司","zsIcw6E-tcIM00");
        CORPORATION.put("海南杉泰健康科技有限公司","kcwcw6F3esco00");
        CORPORATION.put("上海杉泰健康科技有限公司","SdEcw6EvcN9U00");

        STATUS.put("APPROVING","待审批");
        STATUS.put("PAYING","待支付");
        STATUS.put("PROCESSING","处理中（支付中）");
        STATUS.put("PROCESSED","已处理（审批完成、已支付）");
        STATUS.put("CANCELED","待办被取消");
        STATUS.put("SENDING","待寄送");
        STATUS.put("RECEIVING","待收单");
        STATUS.put("RECEIVING_EXCEP","收单异常");
        STATUS.put("REQUISITION_PAID","临时状态（申请单支付时消息发送）");

        STATUS.put("DRAFT","草稿");
        STATUS.put("PENDING","提交中");
        STATUS.put("REJECTED","已驳回");
        STATUS.put("PAID","已支付/审批完成");
        STATUS.put("ARCHIVED","归档");
    }

    @Override
    public void fkdSync(String procInstId) {
        Map map=ddClient_workflow.getProcessInstanceId(ddClient.getAccessToken(),procInstId);
        log.info("map:{}",map);
        List<Map> tasks = (List<Map>) map.get("tasks");
        boolean hasRunning = false;
        for (Map task : tasks) {
            if ("RUNNING".equals(task.get("status").toString())){
                hasRunning = true;
                //判断是否为审批流程第一个出纳节点是否完成（第二个出纳节点进行中）
                if (!"55d3_4702".equals(task.get("activityId").toString())){
                    log.info("当前节点为：activityId:{},activityName:{}，不进行同步",task.get("activityId"),task.get("activityName"));
                    return;
                }else {
                    log.info("当前节点为：activityId:{},activityName:{}，进行同步",task.get("activityId"),task.get("activityName"));
                    break;
                }
            }
        }
        //若没有正在运行的审批流程，则不进行同步
        if (!hasRunning){
            log.info("没有正在运行的审批流程，不进行同步");
            return;
        }
        Map rule=new HashMap();
        rule.put("DDSelectField_8UJMKRMLWYK0","是否推送到易快报");// 是否推送到易快报
//        rule.put("TextField-K11U6U5Q","付款事由");// 标题/付款事由
        rule.put("TextField_18XU5XCGC4RK0","付款摘要");// 标题/付款摘要
        rule.put("RecipientAccountField-K11U6U5P","收款信息");// 收款信息
        rule.put("MoneyField_FMP7OAX3TZ40","付款金额");// 付款金额
        rule.put("TextareaField_15PEUHBWFDDS0","备注");// 备注
        rule.put("DDMultiSelectField_17IA7RJQYGWW0","付款公司");// 付款公司
        Map data=getData(map,rule);
        if( Objects.isNull(data.get("是否推送到易快报")) || data.get("是否推送到易快报").toString().equals("否")){
            return;
        }
        data.put("户名", UtilMap.getMap(data,"收款信息").get("name"));// 户名
        data.put("银行",UtilMap.getMap(data,"收款信息").get("instName"));// 银行
        data.put("支行信息",UtilMap.getMap(data,"收款信息").get("instBranchName"));// 支行信息
        data.put("账号",UtilMap.getMap(data,"收款信息").get("cardNo"));// 账号
        data.put("省份",UtilMap.getMap(data,"收款信息").get("instProvince"));// 省份
        data.put("城市",UtilMap.getMap(data,"收款信息").get("instCity"));// 城市
        log.info("data:{}",JSONObject.toJSONString(data));

        //获取发起人userId
        String userId = map.get("originatorUserId").toString();

        String staffId = operatorStaffId;// 曹艳杰

        //查询易快报中发起人是否存在
        /*Map<String,Object> param3 = new HashMap<>();
        param3.put("accessToken",getToken());

        Map body3 = new HashMap<>();
        body3.put("type","USERID");
        body3.put("conditionIds",Arrays.asList(userId));
        Map user = (Map) JSONObject.parseObject(UtilHttp.doPost(stEkbConfig.getPreUrl() + "/api/openapi/v1/staffs/getStaffIds", null, param3, body3)).getJSONArray("items").get(0);
        if (Objects.isNull(user)){
            log.error("易快报中发起人不存在");
            staffId = "pQccdcNGsM3U00:16278700718435315";// 李溯
        }else {
            staffId = "pQccdcNGsM3U00:16278700718435315";// 李溯
            staffId = user.get("id").toString();
        }*/

        //查询易快报中收款账户是否存在
        Map result = (Map) JSONObject.parseObject(UtilHttp.doGet(stEkbConfig.getPreUrl() + "/api/openapi/v2/payeeInfos",
                null, UtilMap.map("accessToken, start, count, cardNos, ids, active, orderBy, orderByType", getToken(), 0, 100, data.get("账号"), null, true, "updateTime", "desc")))  ;
        System.out.println(result);
        String payeeId = "";
        List<Map> items = (List<Map>) result.get("items");
        //若不存在则新增收款账户
        if (Objects.isNull(items) || items.isEmpty()){
            Map<String, Object> body = new HashMap<>();
            Map visibility = new HashMap();
            visibility.put("fullVisible",true);

            body.put("sort","BANK");
            body.put("type","PUBLIC");
            body.put("owner","CORPORATION");
            body.put("name",data.get("户名"));
            body.put("cardNo",data.get("账号"));
            body.put("bank",data.get("银行"));
            body.put("branch",data.get("支行信息"));
            body.put("province",data.get("省份"));
            body.put("city",data.get("城市"));
            body.put("visibility",visibility);

            Map<String,Object> param = new HashMap<>();
            param.put("accessToken",getToken());
            payeeId = JSONObject.parseObject(UtilHttp.doPost(stEkbConfig.getPreUrl() + "/api/openapi/v2.1/payeeInfos", null, param, body)).getString("id");
            //再次获取新增后的收款账户
/*            account = JSONObject.parseObject(UtilHttp.doGet(stEkbConfig.getPreUrl() + "/api/openapi/v2/payeeInfos",
                    null, UtilMap.map("accessToken, start, count, names, cardNos, ids, active", getToken(), 0, 100, null, null, id, true)))  ;*/
        }else {
            payeeId = items.get(0).get("id").toString();
        }

        //根据模板ID获取模板信息
        String id="ID01yVMv53gDSL";// "对外付款单"模板id
        Map template = (Map) JSONObject.parseObject(HttpUtil.get(stEkbConfig.getPreUrl().concat("/api/openapi/v2/specifications/byIds/[").concat(id).concat("]"), UtilMap.map("accessToken, type, start, count", getToken(), "payment", 0, 10))).getJSONArray("items").get(0);
        String specificationId = template.get("id").toString();

        //新增单据
        Map<String, Object> param = new HashMap<>();
        param.put("accessToken",getToken());
        param.put("isCommit",true);
        param.put("isUpdate",true);

        Map<String, Object> body = new HashMap<>();
        Map<String, Object> form = new HashMap<>();
        //金额
        Map<String, Object> payMoney = new HashMap<>();
        payMoney.put("standard",data.get("付款金额"));
        payMoney.put("standardStrCode","CNY");
        payMoney.put("standardNumCode","156");
        payMoney.put("standardSymbol","¥");
        payMoney.put("standardUnit","元");
        payMoney.put("standardScale",2);

        form.put("title",data.get("付款摘要")); //单据标题
        form.put("submitterId",staffId); //提交人ID
        form.put("description",data.get("备注")); //描述
        form.put("specificationId",specificationId); //单据模板id
        form.put("payMoney",payMoney); //付款金额
        form.put("payeeId",payeeId);
        form.put("u_订单信息",procInstId);
        if (CORPORATION.keySet().contains(data.get("付款公司"))){
            form.put("法人实体",CORPORATION.get(data.get("付款公司"))); //法人实体
        }
        body.put("form",form);

        Map document = JSONObject.parseObject(UtilHttp.doPost(stEkbConfig.getPreUrl() + ("/api/openapi/v2.2/flow/data"), null, param, body));
        Map flow = (Map) document.get("flow");
        String flowId = flow.get("id").toString();

        Shantai shantai = new Shantai();
        shantai.setProcInstId(procInstId);
        shantai.setState("3");
        shantai.setUserId(userId);
        shantai.setFlowId(flowId);
        shantaiMapper.insert(shantai);

        log.info("新增单据成功:{}",document);
    }

    @Override
    public McR commentSync(Map map,String flowId){
        try{
            String procInstId = map.get("procInstId").toString();

            //查询表中是否存在同步评论记录
            LambdaQueryWrapper<Shantai> shantaiLambdaQueryWrapper = new LambdaQueryWrapper<>();
            shantaiLambdaQueryWrapper.eq(Shantai::getProcInstId,procInstId)
                    .eq(Shantai::getValidFlag,"1");
            Shantai shantai = shantaiMapper.selectOne(shantaiLambdaQueryWrapper);

            //获取提交人
            String userId = "";

            if (Objects.nonNull(shantai)){
                if (shantai.getState().equals("1")){
                    log.info("该单据Id:{}已同步过水单，本次不进行同步",flowId);
                    return McR.success();
                }
                userId = shantai.getUserId();
            }
            if (Objects.isNull(userId)){
                userId = map.get("userId").toString();
            }
            //上传钉盘目前设置为管理员
            String unionId = operatorUnionId;

        /*DDR_New ddrNew = (DDR_New) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/v2/user/get", null, ddClient.initTokenParams(), UtilMap.map("userid", userId), DDR_New.class);
        Map result = (Map) ddrNew.getResult();

        if (Objects.nonNull(result)){
            unionId = result.get("unionid").toString();
        }else {
            unionId = operatorUnionId;//曹艳杰
            userId = operatorUserId;//曹艳杰
        }*/

            //获取附件上传到钉盘
            List<String> paths = (List<String>) map.get("paths");
            List<Map> dentries = new ArrayList<>();
            if (Objects.nonNull(paths) && !paths.isEmpty()){
                for (String path : paths) {
                    File file = new File(path);
                    try{
                        //上传文件至钉盘
                        Map dentry = upload(file,unionId);
                        dentries.add(dentry);
                    }catch (IOException e){
                        e.printStackTrace();
                        log.info("上传文件至钉盘失败：{}",e.getMessage());
                        return McR.errorUnknown("上传文件至钉盘失败");
                    }
                }
            }

            String state = map.get("state").toString();

            if (Objects.nonNull(shantai) && shantai.getState().equals("2") && dentries.isEmpty()){
                log.info("该单据Id:{}本次无水单上传，不进行同步",flowId);
                return McR.success();
            }

            //新增评论
//        comment(dentries,state,userId,procInstId);
            //20241018 评论人统一改为曹艳杰
            comment(dentries,state,operatorUserId,procInstId);

            if (Objects.isNull(shantai)){
                //更新表中同步状态
                shantai = new Shantai();
                if (dentries.isEmpty()){
                    shantai.setState("2");
                }else {
                    shantai.setState("1");
                }
                shantai.setFlowId(flowId);
                shantai.setProcInstId(procInstId);
                shantai.setUserId(userId);
                shantaiMapper.insert(shantai);
            }else {
                //更新表中同步状态
                if (dentries.isEmpty()){
                    shantai.setState("2");
                    shantai.setFlowId(flowId);
                }else {
                    shantai.setState("1");
                    shantai.setFlowId(flowId);
                }
                //更新评论状态
                shantaiMapper.updateById(shantai);
            }
        }catch (Exception e){
            log.info("单据Id:{}同步失败，原因：{}",flowId,e.getMessage());
            return McR.errorUnknown("同步评论失败");
        }
        return McR.success();
    }

    @Override
    public Map getFlowInfo(String flowId) {
        //根据单据id获取单据信息
        Map param = new HashMap<>();
        param.put("accessToken",getToken());
        param.put("flowId",flowId);

        Map flow = JSONObject.parseObject(UtilHttp.doGet("https://dd2.hosecloud.com/api/openapi/v1.1/flowDetails", null, param)).getJSONObject("value");
        Map form = (Map) flow.get("form");
        String procInstId = form.get("u_订单信息").toString();
        String submitterId = form.get("submitterId").toString();
        String userId = submitterId.substring(submitterId.indexOf(':') + 1);

        String state = flow.get("state").toString();
        log.info("流程状态：{}",state.toUpperCase());
        if (STATUS.keySet().contains(state.toUpperCase())){
            state = STATUS.get(state.toUpperCase());
        }

        //根据单据id获取单据附件信息
        Map param2 = new HashMap<>();
        param2.put("accessToken",getToken());

        Map body2 = new HashMap<>();
        String[] flowIds = {flowId};
        body2.put("flowIds",flowIds);

        JSONArray items = JSONObject.parseObject(UtilHttp.doPost("https://dd2.hosecloud.com/api/openapi/v1/flowDetails/attachment", null, param2, body2)).getJSONArray("items");
        Map item = (Map) items.get(0);
        List<Map> attachmentList = (List<Map>) item.get("attachmentList");

        List<String> paths = new ArrayList<>();
        if (!attachmentList.isEmpty()){
            for (Map attachment : attachmentList) {
                if (attachment.get("type").toString().equals("flow.receipt")){
                    List<Map> receiptUrls = (List<Map>) attachment.get("receiptUrls");
                    for (Map receiptUrl : receiptUrls) {
                        //附件下载到本地
                        String url = receiptUrl.get("url").toString();
                        String fileName = receiptUrl.get("key").toString();
                        String path = filePath + fileName;
                        downloadNet(url, path);
                        //添加到路径列表
                        paths.add(path);
                    }
                }
            }
        }

        Map result = new HashMap();
        result.put("state",state);
        result.put("procInstId",procInstId);
        result.put("paths",paths);
        result.put("userId",userId);

        return result;
    }

    // 下载网络文件
    public void downloadNet(String netUrl, String path){
        int bytesum = 0;
        int byteread = 0;
        try{
            URL url = new URL(netUrl);
            URLConnection conn = url.openConnection();
            InputStream inStream = conn.getInputStream();
            FileOutputStream fs = new FileOutputStream(path);
            byte[] buffer = new byte[1204];
            while ((byteread = inStream.read(buffer)) != -1) {
                bytesum += byteread;
                fs.write(buffer, 0, byteread);
            }
        } catch (MalformedURLException e){
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void commentSyncAll() {
        //获取所有评论未同步水单的单据列表
        LambdaQueryWrapper<Shantai> shantaiLambdaQueryWrapper = new LambdaQueryWrapper<>();
        shantaiLambdaQueryWrapper.ne(Shantai::getState,"1")
                .eq(Shantai::getValidFlag,"1");
        List<Shantai> shantaiList = shantaiMapper.selectList(shantaiLambdaQueryWrapper);
        for (Shantai shantai : shantaiList) {
            String flowId = shantai.getFlowId();
            log.info("开始定时同步评论水单：{}",flowId);
            Map flowInfo = getFlowInfo(flowId);
            commentSync(flowInfo,flowId);
        }
    }

    private void comment(List<Map> dentries, String state, String userId, String procInstId) {
        Map body = new HashMap<>();
        Map fileMap = new HashMap<>();
        List<Map> attachments= new ArrayList<>();

        for (Map dentry : dentries) {
            Map attachment = new HashMap<>();

            attachment.put("spaceId",dentry.get("spaceId").toString());
            attachment.put("fileSize",dentry.get("size").toString());
            attachment.put("fileId",dentry.get("id").toString());
            attachment.put("fileName",dentry.get("name").toString());
            attachment.put("fileType",dentry.get("type").toString());

            attachments.add(attachment);
        }

        fileMap.put("photos",null);
        fileMap.put("attachments",attachments);

        body.put("processInstanceId",procInstId);
        body.put("text","付款状态：" + state);
        body.put("commentUserId",userId);
        body.put("file",fileMap);

        UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/comments",ddClient.initTokenHeader(),null,body);
    }

    private Map upload(File file,String unionId) throws IOException {
        //获取文件上传信息
//        String parentDentryUuid = "ZQYprEoWonmdnzdOFpklzk1a81waOeDk";// poc目录 api测试上传/附件
        String parentDentryUuid = "YQBnd5ExVEowejBQtZ1v6wO28yeZqMmz";// 杉泰目录 /技术文档/易快报银企直连
        Map<String,Object> param1 = new HashMap<>();
        param1.put("unionId",unionId);
        Map body1 = new HashMap<>();
        body1.put("protocol","HEADER_SIGNATURE");
        DDR_New ddrNew = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v2.0/storage/spaces/files/" + parentDentryUuid + "/uploadInfos/query", ddClient.initTokenHeader(), param1, body1, DDR_New.class);
        String uploadKey = ddrNew.getUploadKey();
        Map headerSignatureInfo = ddrNew.getHeaderSignatureInfo();
        Map<String,String> headers = (Map<String,String>) headerSignatureInfo.get("headers");
        String resourceUrl = ((List<String>) headerSignatureInfo.get("resourceUrls")).get(0);

        //使用OSS的header加签方式上传文件
        URL url = new URL(resourceUrl);
        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                connection.setRequestProperty(entry.getKey(), entry.getValue());
            }
        }
        connection.setDoOutput(true);
        connection.setRequestMethod("PUT");
        connection.setUseCaches(false);
        connection.setReadTimeout(10000);
        connection.setConnectTimeout(10000);
        connection.connect();
        OutputStream out = connection.getOutputStream();

        InputStream is = new FileInputStream(file);
        byte[] b =new byte[1024];
        int temp;
        while ((temp=is.read(b))!=-1){
            out.write(b,0,temp);
        }
        out.flush();
        out.close();
        int responseCode = connection.getResponseCode();
        connection.disconnect();
        if (responseCode == 200) {
            System.out.println("上传成功");
        } else {
            System.out.println("上传失败");
        }

        //提交文件
        Map<String,Object> param2 = new HashMap<>();
        param2.put("unionId",unionId);
        Map body2 = new HashMap<>();
        body2.put("uploadKey",uploadKey);
        body2.put("name",file.getName());
        Map option = new HashMap<>();
        option.put("conflictStrategy","OVERWRITE");//文件名称冲突策略：覆盖
        body2.put("option",option);

        DDR_New ddrNew2 = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v2.0/storage/spaces/files/" + parentDentryUuid + "/commit", ddClient.initTokenHeader(), param2, body2, DDR_New.class);

        return ddrNew2.getDentry();
    }


    public Map getData(Map instance,Map rule){
        Map resultMap=new HashMap();
        List<Map> list=(List<Map>) instance.get("formComponentValues");
        for (Map map:list) {
            if(rule.containsKey(String.valueOf(map.get("id")))){
                String componentType=String.valueOf(map.get("componentType"));
                if(componentType.equals("RecipientAccountField")){
                    resultMap.put(rule.get(String.valueOf(map.get("id"))),JSONObject.parseObject(String.valueOf(map.get("extValue"))));
                }else if (componentType.equals("DDMultiSelectField")){
                    resultMap.put(rule.get(String.valueOf(map.get("id"))),JSONObject.parseArray(String.valueOf(map.get("value"))).get(0).toString());
                }else{
                    resultMap.put(rule.get(String.valueOf(map.get("id"))),map.get("value"));
                }
            }
        }
        return resultMap;
    }

    private String getToken(){
        String accessToken = UtilToken.get("invalid-token-ykb");
        if (StringUtils.isNotBlank(accessToken)) return accessToken;


        Map result = JSONObject.parseObject(HttpUtil.post(stEkbConfig.getPreUrl().concat("/api/openapi/v1/auth/getAccessToken"),JSONObject.toJSONString(UtilMap.map("appKey, appSecurity",stEkbConfig.getAppKey(),stEkbConfig.getAppSecurity()))));
        String token = ((Map) result.get("value")).get("accessToken").toString();
        long expireTime = (long) ((Map) result.get("value")).get("expireTime");

        log.info("响应token, {}", token);
        // token失效自动重置: 重新调用会重置过期时间
        UtilToken.put("invalid-token-ykb", token, 2 * 3600 * 1000L);

        return token;
    }
}
