package com.malk.hangfu.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.kingdee.bos.webapi.entity.IdentifyInfo;
import com.kingdee.bos.webapi.sdk.K3CloudApi;
import com.malk.hangfu.entity.Voucher;
import com.malk.hangfu.service.HangfuService;
import com.malk.server.common.McR;
import com.malk.server.dingtalk.DDConf;
import com.malk.server.dingtalk.DDR_New;
import com.malk.service.dingtalk.DDClient;
import com.malk.service.dingtalk.DDClient_Workflow;
import com.malk.utils.UtilHttp;
import com.malk.utils.UtilMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.malk.hangfu.config.KDWebApiConfig;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 功能：
 * 作者：hanxue
 * 日期：2025/11/17 10:38
 */
@Service
@Slf4j
@Component
public class HangfuServiceImpl implements HangfuService {
    @Autowired
    private DDConf ddConf;
    @Autowired
    private DDClient ddClient;
    @Autowired
    private DDClient_Workflow ddworkflow;
    @Autowired
    private KDWebApiConfig kdWebApiConf;

    @Value(value = "${dingtalk.unionId}")
    private String unionId;
    @Value(value = "${dingtalk.spaceId}")
    private String spaceId;
    @Value(value = "${dingtalk.saveFilePath}")
    private String saveFilePath;
    @Value(value ="${dingtalk.demoFilePath}")
    private String demoFilePath;
    @Override
    public McR syncFileToDing(Map param) throws IOException {
        String formInstId = param.get("formInstId").toString();
        String formSerNo = param.get("formSerNo").toString();
        LocalDate now = LocalDate.now();
        String yearMonth = now.getYear()+""+now.getMonthValue();
        Map topFolder = getDentryList(spaceId, "0", unionId, "").stream()
                .filter(map -> map.get("name").toString().equals(yearMonth) && map.get("type").toString().equals("FOLDER"))
                .findFirst()
                .orElse(null);
        String parentId ="";
//        1、获取到yyyyMM文件夹id信息
        if (Objects.nonNull(topFolder)){
            parentId = topFolder.get("id").toString();
        }else {
            //创建该数据标题文件夹
            Map yearMonthDentry = createFolder(spaceId,"0",yearMonth,unionId);
            parentId = yearMonthDentry.get("id").toString();
        }
//        2、创建yyyy-MM-formSerNo目录
        Map formInstIdentry = createFolder(spaceId,parentId,now.getYear()+"-"+now.getMonthValue()+"-"+formSerNo,unionId);
//        3、审批附件同步到钉盘
        return OAfileUptoDing(formInstId,formInstIdentry.get("uuid").toString());
    }

    @Override
    public McR syncKindeeToDing(Map<String, Object> param) throws Exception {
        String formSerNo = param.get("formSerNo").toString();
        LocalDate now = LocalDate.now();
        String yearMonth = now.getYear()+""+now.getMonthValue();
        String secondName =yearMonth+"-"+formSerNo;
//        Map topFolder = getDentryList(spaceId, "0", unionId, "").stream()
//                .filter(map -> map.get("name").toString().equals(yearMonth) && map.get("type").toString().equals("FOLDER"))
//                .findFirst()
//                .orElse(null);
//        String parentId =topFolder.get("id").toString();
//        Map formInstIdentry = getDentryList(spaceId, parentId, unionId, "").stream()
//                .filter(map -> map.get("name").toString().equals(secondName) && map.get("type").toString().equals("FOLDER"))
//                .findFirst()
//                .orElse(null);


        return kibdeeFileUptoDing(null,null,"FAccountBookID.FNumber='001'  and FYear='2025'");
    }


    //===========================================辅助函数================================================================
    //创建钉盘文件夹
    private Map createFolder(String spaceId, String parentId, String folderName, String unionId){
        String url = "https://api.dingtalk.com/v1.0/storage/spaces/"+spaceId+"/dentries/"+parentId+"/folders";
        Map query = new HashMap();
        query.put("unionId",unionId);
        Map body = new HashMap();
        body.put("name",folderName);
        DDR_New ddrNew = (DDR_New) UtilHttp.doPost(url, ddClient.initTokenHeader(), query, body, DDR_New.class);
        Map dentry = ddrNew.getDentry();
        return dentry;
    }

    //    获取文件夹
    private List<Map> getDentryList(String spaceId, String parentId, String unionId, String nextToken) {
        List<Map> dentryList = new ArrayList<>();
        Map query = new HashMap();
        query.put("parentId",parentId);
        query.put("unionId",unionId);
        query.put("maxResults",50);
        if (Strings.isNotBlank(nextToken)){
            query.put("nextToken",nextToken);
        }
//        获取文件或文件夹列表
        DDR_New ddrNew = (DDR_New) UtilHttp.doGet("https://api.dingtalk.com/v1.0/storage/spaces/"+spaceId+"/dentries", ddClient.initTokenHeader(), query, DDR_New.class);
        dentryList.addAll(ddrNew.getDentries());
        if (Objects.nonNull(ddrNew.getNextToken())){
            dentryList.addAll(getDentryList(spaceId, parentId, unionId, ddrNew.getNextToken()));
        }
        return dentryList;
    }

    //  上传审批附件到钉盘
    private McR OAfileUptoDing(String formInstId,String dentryUuid ) throws IOException {
        Map<String, Object> result = new HashMap<>();
        List<FileTask> processedFiles = new ArrayList<>();
        try {
            // 1. 获取表单详情
            Map<String, Object> tableDetails = ddworkflow.getProcessInstanceId(ddClient.getAccessToken(), formInstId);
            if (tableDetails == null || tableDetails.isEmpty()) {
                return McR.errorParam("获取表单详情失败");
            }
            List<Map<String, Object>> formComponentValues = (List<Map<String, Object>>) tableDetails.get("formComponentValues");
            if (formComponentValues == null || formComponentValues.isEmpty()) {
                return McR.errorParam("获取表单详情失败");
            }
            Set<String> targetNames = new HashSet<>(Arrays.asList("其他附件", "银行回单", "报销明细", "发票", "上传附件", "附件"));
            for (Map<String, Object> formComponentValue : formComponentValues) {
                String name = UtilMap.getString(formComponentValue, "name");
                if (name == null || !targetNames.contains(name)) {
                    continue;
                }
                try {
                    if ("银行回单".equals(name) || "其他附件".equals(name) || "附件".equals(name)) {
                        processFileAttachments(formComponentValue, formInstId, processedFiles);
                    } else if ("发票".equals(name)) {
                        processInvoiceAttachments(formComponentValue, processedFiles);
                    } else if ("报销明细".equals(name)) {
                        List<Map> paymentDeatils = (List<Map>) JSONArray.parse(formComponentValue.get("value").toString());
                        for (Map paymentDeatil : paymentDeatils) {
                            List<Map> rowValues = (List<Map>) JSONArray.parse(paymentDeatil.get("rowValue").toString());
                            for (Map rowValue : rowValues) {
                                String label = (String) rowValue.get("label");
                                switch (label) {
                                    case "发票":
                                        processInvoiceAttachments(rowValue, processedFiles);
                                        continue;
                                    case "附件":
                                        processFileAttachments(rowValue, formInstId, processedFiles);
                                        continue;
                                }
                            }
                        }
                    }

                } catch (Exception e) {
                    System.err.println("处理字段 [" + name + "] 时出错: " + e.getMessage());
                    e.printStackTrace();
                }

            }
            if (processedFiles.size() > 0) {
                for (FileTask processedFile : processedFiles) {
                    downloadFile(processedFile.downloadUrl, saveFilePath + processedFile.fileName);
                    uploadToDingTalk(saveFilePath + processedFile.fileName, dentryUuid, "OVERWRITE");

                }
            }
            return McR.success();
        } catch (Exception e) {
            return McR.errorParam("上传附件失败");
        }
    }

    //    上传金蝶附件到钉盘
    private McR kibdeeFileUptoDing(String formInstId,String dentryUuid,String filterString ) throws Exception {
        Map<String, Object> billInfo = new HashMap<>();
        billInfo.put("FormId","GL_VOUCHER");
        billInfo.put("FieldKeys","FAccountBookID.FNumber,FDate,FVOUCHERGROUPID.Fname,FYEAR,FVOUCHERGROUPNO,FEXPLANATION,FACCOUNTID.Fname,FDC,FDebit,FCredit,FDEBITTOTAL,FCREDITTOTAL");
        billInfo.put("FilterString",filterString);
        String billInfoJson = new Gson().toJson(billInfo);
        K3CloudApi client = new K3CloudApi(initIden());
        List<List<Object>> billList = client.executeBillQuery(billInfoJson);
        System.out.println(billList);
        if (billList == null || billList.isEmpty()) {
            return McR.errorParam("获取表单详情失败");
        }else{
            Map<Object, List<List<Object>>> grouped = billList.stream()
                    .filter(row -> row != null && row.size() > 4)
                    .collect(Collectors.groupingBy(row -> row.get(4)));
            System.out.println(grouped);
            for (Map.Entry<Object, List<List<Object>>> entry : grouped.entrySet()) {
                List<Voucher> voucherList = ListUtils.newArrayList();
                List<List<Object>> group = entry.getValue();
                for (List<Object> row : group) {
                    Voucher voucherentity = new Voucher();
                    voucherentity.setAccountBook(row.get(0).toString());
                    voucherentity.setDate((Date) row.get(1));
                    voucherentity.setVoucherWord(row.get(2).toString());
                    voucherentity.setYear(row.get(3).toString());
                    voucherentity.setVoucherNo(row.get(4).toString());
                    voucherentity.setExplanation(row.get(5).toString());
                    voucherentity.setAccountId(row.get(6).toString());
                    voucherentity.setDebit(Double.parseDouble(row.get(7).toString()));
                    voucherentity.setCredit(Double.parseDouble(row.get(8).toString()));
                    voucherList.add(voucherentity);
                }
                writeExcel(voucherList);

                // 转为 Object[][] 或直接传 List
//                callExternalFunction(entry.getKey(), group);
            }
        }


        return McR.success();

    }

    private void writeExcel(List<Voucher> voucherList)  {
        String templateFileName = demoFilePath + "demo" + File.separator + "凭证模板.xls";
        String fileName = demoFilePath + "templateWrite" + System.currentTimeMillis() + ".xlsx";
        EasyExcel.write(fileName, Voucher.class).withTemplate(templateFileName).sheet().doWrite(voucherList);
    }
    private List<Voucher> getVoucherData() {
        List<Voucher> voucherListlist = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            Voucher data = new Voucher();

            voucherListlist.add(data);
        }
        return voucherListlist;
    }

    private IdentifyInfo initIden(){
        //注意 1：此处不再使用参数形式传入用户名及密码等敏感信息，改为在登录配置文件中设置。
        //注意 2：必须先配置第三方系统登录授权信息后，再进行业务操作，详情参考各语言版本SDK介绍中的登录配置文件说明。
        //读取配置，初始化SDK
        IdentifyInfo iden = new IdentifyInfo();
        iden.setUserName(kdWebApiConf.getXKDApiUserName());
        iden.setAppId(kdWebApiConf.getXKDApiAppID());
        iden.setdCID(kdWebApiConf.getXKDApiAcctID());
        iden.setAppSecret(kdWebApiConf.getXKDApiAppSec());
        iden.setServerUrl(kdWebApiConf.getXKDApiServerUrl());

        return iden;
    }
    static class FileTask {
        String fileName;        // 文件名
        String downloadUrl;     // 下载链接（后续填充）

        public FileTask(String fileName, String downloadUrl) {
            this.fileName = fileName;
            this.downloadUrl = downloadUrl;
        }
    }
    //处理附件组件
    private void processFileAttachments(Map<String, Object> formComponentValue, String formInstId, List<FileTask> processedFiles) throws IOException{
        Object valueObj = formComponentValue.get("value");
        if (valueObj == null) return;
        try {
            List<Map<String, Object>> files = (List<Map<String, Object>>) JSONArray.parse(UtilMap.getString(formComponentValue,"value"));
            if (files == null) return;
            for (Map<String, Object> file : files) {
                String fileId = UtilMap.getString(file, "fileId");
                String fileName = UtilMap.getString(file, "fileName");
                if (fileId == null || fileName == null) continue;
                Map body = new HashMap();
                body.put("processInstanceId",formInstId);
                body.put("fileId",fileId);
                DDR_New ddr = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com//v1.0/workflow/processInstances/spaces/files/urls/download", ddConf.initTokenHeader(ddClient.getAccessToken()), null, body, DDR_New.class);
                Map downloadUri = (Map) ddr.getResult();
                String downloadUrl = UtilMap.getString(downloadUri, "downloadUri");
                if (downloadUrl == null) continue;
                processedFiles.add(new FileTask(fileName, downloadUrl));
            }
        } catch (Exception e) {
            throw new RuntimeException("解析附件列表失败", e);
        }
    }

    //    处理发票组件
    private void processInvoiceAttachments(Map<String, Object> formComponentValue, List<FileTask> processedFiles) throws IOException {
        Map extendValue =new HashMap();
        if(formComponentValue.containsKey("extValue")){
//            主表发票信息
            extendValue =(Map)  JSONObject.parse(formComponentValue.get("extValue").toString());
        }else if (formComponentValue.containsKey("extendValue")){
//            明细表发票信息extendValue
            extendValue =(Map)  JSONObject.parse(formComponentValue.get("extendValue").toString());
        }
        if(extendValue.containsKey("invoiceList")){
            List< Map> invoiceList = (List<Map>) extendValue.get("invoiceList");
            for(Map invoice : invoiceList){
                String downloadUrl = UtilMap.getString(invoice,"electronicUrl");
                String fileName = UtilMap.getString(invoice,"fileName");
                processedFiles.add(new FileTask(fileName, downloadUrl));
            }
        }
    }


    //上传文件至钉盘
    private Map uploadToDingTalk(String filePath,String dentryUuid,String conflictStrategy) {
        //获取当前时间戳
        long beginTimestamp = System.currentTimeMillis();
        System.out.println("开始上传附件：" + DateUtil.format(new Date(beginTimestamp), "yyyy-MM-dd HH:mm:ss"));

        //文件
        File file = new File(filePath);

        //获取文件上传信息
        Map param = new HashMap();
        param.put("unionId",unionId);
        Map body = new HashMap();
        body.put("protocol","HEADER_SIGNATURE");
        body.put("multipart",false);

        DDR_New ddr = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v1.0/storage/spaces/" + spaceId + "/files/uploadInfos/query", ddConf.initTokenHeader(ddClient.getAccessToken()), param, body, DDR_New.class);
        String uploadKey = ddr.getUploadKey();
        Map headerSignatureInfo = ddr.getHeaderSignatureInfo();
        Map<String,String> headers = (Map<String,String>) headerSignatureInfo.get("headers");
        List<String> resourceUrls = (List<String>) headerSignatureInfo.get("resourceUrls");
        String resourceUrl = resourceUrls.get(0);

        Map dentry = new HashMap();
        //使用OSS的header加签方式上传文件
        try {
            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 body2 = new HashMap();
            Map option = new HashMap();
            option.put("conflictStrategy",conflictStrategy);
            body2.put("uploadKey",uploadKey);
            body2.put("name",file.getName());
            body2.put("option",option);
            DDR_New ddrNew = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v2.0/storage/spaces/files/"+dentryUuid+"/commit", ddClient.initTokenHeader(), param, body2, DDR_New.class);
            dentry = ddrNew.getDentry();

        }catch (IOException e){
            log.info("上传文件异常:{}",e);
        }

        long endTimestamp = System.currentTimeMillis();
        System.out.println("上传文件结束：" + DateUtil.format(new Date(endTimestamp), "yyyy-MM-dd HH:mm:ss"));
        System.out.println("上传文件耗时：" + (endTimestamp - beginTimestamp)/1000.0 + "s");
        return dentry;
    }
    // 文件下载本地
    private void downloadFile(String fileURL, String savePath) throws IOException {
        URL url = new URL(fileURL);
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
        int responseCode = httpConn.getResponseCode();

        // 检查HTTP响应代码是否为200
        if (responseCode == HttpURLConnection.HTTP_OK) {
            InputStream inputStream = httpConn.getInputStream();
            FileOutputStream outputStream = new FileOutputStream(savePath);

            byte[] buffer = new byte[4096];
            int bytesRead = -1;

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.close();
            inputStream.close();
        } else {
            System.out.println("无法下载文件。HTTP响应代码: " + responseCode);
        }
        httpConn.disconnect();
    }
}
