package com.malk.qiwang.Controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.malk.qiwang.Service.QiWangService;
import com.malk.qiwang.Service.TXYInvoice;
import com.malk.qiwang.entity.CompanyTitle;
import com.malk.qiwang.entity.InvoiceLibrary;
import com.malk.qiwang.model.McInvoiceDto;
import com.malk.server.aliwork.YDConf;
import com.malk.server.aliwork.YDParam;
import com.malk.server.common.FilePath;
import com.malk.server.common.McException;
import com.malk.server.common.McR;
import com.malk.server.dingtalk.DDR_New;
import com.malk.service.aliwork.YDClient;
import com.malk.service.dingtalk.DDClient;
import com.malk.service.dingtalk.DDClient_Contacts;
import com.malk.service.dingtalk.DDClient_Workflow;
import com.malk.utils.*;
import com.spire.pdf.PdfCompressionLevel;
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.exporting.PdfImageInfo;
import com.spire.pdf.graphics.PdfBitmap;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@Slf4j
@RequestMapping("/qw")
public class QiWangController {
    @Autowired
    private QiWangService qiWangService;
    @Autowired
    private DDClient ddClient;
    @Autowired
    private YDClient ydClient;
    @Autowired
    private TXYInvoice txyInvoice;
    @Value("${dingtalk.operator}")
    private String operator;
    @Value("${dingtalk.downloadPath}")
    private String downloadPath;
    @Autowired
    private DDClient_Workflow ddClientWorkflow ;
    @Autowired
    private DDClient_Contacts ddClient_contacts;
    private static final String bdurl = "D://qiwang//files//";
    private static final String url = "http://47.103.203.2:9092/qiwang/";
//    private static final String bdurl = "C:\\Users\\EDY\\Desktop\\项目\\琦王\\files\\";
//private static final String url = "http://410a09c5.r39.cpolar.top/qiwang/";
    @PostMapping("/test")

    public McR test(@RequestBody Map map) {
        log.info("map:{}", map);
        String processInstanceId = UtilMap.getString(map, "processInstanceId");

//         获取审批实例信息
        Map processInstance = ddClientWorkflow.getProcessInstanceId(ddClient.getAccessToken(), processInstanceId);
        System.out.println("processInstance====" + processInstance);
//
//        if (Objects.nonNull(processInstance)) {
//            List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
//            System.out.println("formComponentValues====" + formComponentValues);

            // 存储所有处理结果
//            List<Object> allResults = new ArrayList<>();
//
//            // 遍历所有表单组件
//            for (Map formComponentValue : formComponentValues) {
//                String id = String.valueOf(formComponentValue.get("id"));
//
//                // 查找 TableField_1A1CDMEN8DDS0 组件
//                if ("TableField_1A1CDMEN8DDS0".equals(id)) {
//                    String tableFieldValue = String.valueOf(formComponentValue.get("value"));
//                    System.out.println("原始value值: " + tableFieldValue);
//                }
//
//                try {
//                    if ("DDAttachment_OF8QX7XQWWW0".equals(id)) {
//                        String attachmentListStr = UtilMap.getString(formComponentValue, "value");
//                        List<Map> attachmentList = (List<Map>) JSONObject.parse(attachmentListStr);
//
//                        for (Map attachment : attachmentList) {
//
//                            String fileId = UtilMap.getString(attachment, "fileId");
//                            String fileType = UtilMap.getString(attachment, "fileType");
//                            String fileName = UtilMap.getString(attachment, "fileName");
//
//                            //下载钉盘文件
//                            String filePath = downloadPath + fileId + "." + fileType;
//                            downloadDdFile(processInstanceId, fileId, filePath);
//                            String hz = "qw/files/" + fileId + "." + fileType;
//                            Map fileMap = new HashMap();
//                            fileMap.put("url", url + hz);
//
//                            // 处理每个附件并收集结果
//                            McR<Object> result = processMixedInvoice(fileMap);
//                            allResults.add(result.getData());
////                            allResults.add(fileMap);
//                        }
//                    }
//                } catch (Exception e) {
//                    log.error("解析 TableField 数据失败", e);
//                    return McR.error("300", "解析数据失败");
//                }
//            }

            // 处理完所有附件后，汇总结果

//                return McR.success() ;
//
//        }

        return McR.success();
    }
    @PostMapping ("/test3")
    public McR test3(@RequestBody Map map) {
        log.info("map:{}", map);
        String processInstanceId = UtilMap.getString(map, "processInstanceId");
        // 获取审批实例信息
        Map processInstance = ddClientWorkflow.getProcessInstanceId(ddClient.getAccessToken(), processInstanceId);
        System.out.println("processInstance====" + processInstance);

        if (Objects.nonNull(processInstance)) {
            List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
            System.out.println("formComponentValues====" + formComponentValues);

            // 查找 TableField_1A1CDMEN8DDS0 组件
            for (Map formComponentValue : formComponentValues) {
                String id = String.valueOf(formComponentValue.get("id"));
                if ("TableField_1A1CDMEN8DDS0".equals(id)) {
                    String tableFieldValue = String.valueOf(formComponentValue.get("value"));
                    System.out.println("原始value值: " + tableFieldValue);

                    try {
                        // 解析子表数据
                        List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);

                        // 收集所有行的结果
                        List<Object> allResults = new ArrayList<>();

                        // 遍历子表的每一行
                        for (Map row : tableRows) {
                            List<Map> rowValues = (List<Map>) row.get("rowValue");

                            // 存储当前行的"是否有发票"状态和附件
                            String hasInvoice = null;
                            List<Map> attachmentList = null;

                            // 先收集这一行的字段值
                            for (Map rowItem : rowValues) {
                                String key = String.valueOf(rowItem.get("key"));
                                if ("DDSelectField_1ORUK0KIM5D6O".equals(key)) {
                                    hasInvoice = String.valueOf(rowItem.get("value"));
                                }
                                if ("DDAttachment_Z02OGR5QL8U8".equals(key)) {
                                    attachmentList = (List<Map>) rowItem.get("value");
                                }
                            }

                            // 只有当"是否有发票"为"是"且有附件时，才处理
                            if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
                                // 只处理这一行的第一个附件
                                Map attachment = attachmentList.get(0);

                                System.out.println("fileName: " + UtilMap.getString(attachment, "fileName"));
                                System.out.println("fileId: " + UtilMap.getString(attachment, "fileId"));
                                String fileId = UtilMap.getString(attachment, "fileId");
                                String fileType = UtilMap.getString(attachment, "fileType");
                                String fileName = UtilMap.getString(attachment, "fileName");

                                // 下载钉盘文件
                                String filePath = downloadPath + fileId + "." + fileType;
                                downloadDdFile(processInstanceId, fileId, filePath);
                                String hz = "qw/files/" + fileId + "." + fileType;
                                Map fileMap = new HashMap();
                                fileMap.put("url", url + hz);

                                // 处理发票识别
//                                McR<Object> result = processMixedInvoice(fileMap);
                                allResults.add(fileMap);
                            } else {
                                // 如果这一行不符合条件，可以添加null或跳过
                                // allResults.add(null); // 如果需要保持行数对应
                            }
                        }

                        // 所有行处理完成后统一返回
                        return McR.success(allResults);

                    } catch (Exception e) {
                        log.error("解析子表数据失败", e);
                        return McR.error("300", "解析数据失败");
                    }
                }
            }
        }

        return McR.success();
    }
@PostMapping("/test4")
public McR test4(@RequestBody Map map) {
    log.info("map:{}", map);
    String processInstanceId = UtilMap.getString(map, "processInstanceId");
    // 获取审批实例信息
    Map processInstance = ddClientWorkflow.getProcessInstanceId(ddClient.getAccessToken(), processInstanceId);
//    System.out.println("processInstance====" + processInstance);
    String  userId = (String) processInstance.get("originatorUserId");
    System.out.println(userId);

    if (Objects.nonNull(processInstance)) {
        List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
        System.out.println("formComponentValues====" + formComponentValues);

        // 存储主表的 zt 和 je 值
        String zt = "是";
        String mainJe = null;

        // 先遍历收集主表字段值
        for (Map formComponentValue : formComponentValues) {
            String id = String.valueOf(formComponentValue.get("id"));
            if ("DDSelectField_4SITXLYUEO80".equals(id)) {
                zt = String.valueOf(formComponentValue.get("value"));
            }
            if ("MoneyField_3QZLY8BD3780".equals(id)) {
                mainJe = String.valueOf(formComponentValue.get("value"));
            }
        }

        // 判断主表状态
        if ("否".equals(zt)) {
            // 当为"否"时，直接返回 je 字段值
            Map<String, Object> result = new HashMap<>();
            result.put("je", mainJe);
            result.put("status", "否");
            Map body = new HashMap<>();
        body.put("processInstanceId",processInstanceId);
        body.put("text", result);
        body.put("commentUserId",userId);
//        body.put("file",fileMap);
//
        String re  =UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/comments",ddClient.initTokenHeader(),null,body);
            return McR.success(result);
        }

        // 当为"是"时，继续处理子表附件
        if ("是".equals(zt)) {
            // 查找 TableField_1A1CDMEN8DDS0 组件
            for (Map formComponentValue : formComponentValues) {
                String id = String.valueOf(formComponentValue.get("id"));

                if ("TableField_1A1CDMEN8DDS0".equals(id)) {
                    String tableFieldValue = String.valueOf(formComponentValue.get("value"));
                    System.out.println("原始value值: " + tableFieldValue);

                    try {
                        // 解析子表数据
                        List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);

                        // 收集所有发票识别结果
                        List<Object> allResults = new ArrayList<>();

                        // 遍历子表的每一行
                        for (Map row : tableRows) {
                            List<Map> rowValues = (List<Map>) row.get("rowValue");

                            // 先检查这一行的"是否有发票"字段值
                            String hasInvoice = null;
                            List<Map> attachmentList = null;

                            for (Map rowItem : rowValues) {
                                String key = String.valueOf(rowItem.get("key"));

                                // 获取"是否有发票"字段的值
                                if ("DDSelectField_1ORUK0KIM5D6O".equals(key)) {
                                    hasInvoice = String.valueOf(rowItem.get("value"));
                                }

                                // 获取附件字段
                                if ("DDAttachment_Z02OGR5QL8U8".equals(key)) {
                                    attachmentList = (List<Map>) rowItem.get("value");
                                }
                            }

                            // 只有当"是否有发票"为"是"时，才处理附件
                            if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
                                // 遍历这一行的所有附件
                                for (Map attachment : attachmentList) {
                                    System.out.println("fileName: " + UtilMap.getString(attachment, "fileName"));
                                    System.out.println("fileId: " + UtilMap.getString(attachment, "fileId"));
                                    String fileId = UtilMap.getString(attachment, "fileId");
                                    String fileType = UtilMap.getString(attachment, "fileType");
                                    String fileName = UtilMap.getString(attachment, "fileName");

                                    // 下载钉盘文件
                                    String filePath = downloadPath + fileId + "." + fileType;
                                    downloadDdFile(processInstanceId, fileId, filePath);
                                    String hz = "qw/files/" + fileId + "." + fileType;
                                    Map fileMap = new HashMap();
                                    fileMap.put("url", url + hz);

                                    // 处理发票识别并收集结果
                                    McR<Object> result = processMixedInvoice(fileMap);
                                    allResults.add(result.getData());
                                }
                            }
                        }
                        Map body = new HashMap<>();
                        body.put("processInstanceId",processInstanceId);
                        body.put("text", JSON.toJSONString(allResults));
                        body.put("commentUserId",userId);
                        String re  =UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/comments",ddClient.initTokenHeader(),null,body);
                        // 所有附件处理完成后统一返回
                        return McR.success(allResults);

                    } catch (Exception e) {
                        log.error("解析子表数据失败", e);
                        return McR.error("300", "解析数据失败");
                    }
                }
            }
        }
    }

    return McR.success();
}
    @PostMapping("/test2")
    public McR test2(@RequestBody Map map) {
        log.info("map:{}", map);
//        String procInstId = String.valueOf(map.get("procInstId"));
        String processInstanceId = UtilMap.getString(map, "processInstanceId");
        // 获取审批实例信息
        Map processInstance = ddClientWorkflow.getProcessInstanceId(ddClient.getAccessToken(), processInstanceId);
        System.out.println("processInstance====" + processInstance);
//        Map body = new HashMap<>();
//        body.put("processInstanceId",processInstanceId);
//        body.put("text", "发票信息如下：\n\n1. 电子发票(普通发票) | 全电普通发票 | 发票号码：26312000001701010876 | 开票日期：2026-03-20 | 金额：103.28 | 不含税：94.75 | 税额：8.53 | 购买方：罗庆宇（个人） | 销售方：上海聪雷贸易有限公司 | 税号：91310107MA1G0XPA4K\n\n2. 电子发票(普通发票) | 全电普通发票 | 发票号码：26442000003207012931 | 开票日期：2026-03-25 | 金额：239.00 | 不含税：211.50 | 税额：27.50 | 购买方：罗庆宇 | 销售方：广州延阳电子商务有限公司 | 税号：91440101MA5AWRGH4L\n\n3. 电子发票(普通发票) | 全电普通发票 | 发票号码：26332000002236540291 | 开票日期：2026-03-20 | 金额：518.00 | 不含税：458.41 | 税额：59.59 | 购买方：罗庆宇（个人） | 销售方：杭州亿奥网络科技有限公司 | 税号：91330108MA28MK7K08\n\n4. 电子发票(普通发票) | 全电普通发票 | 发票号码：26442000003052085371 | 开票日期：2026-03-21 | 金额：183.00 | 不含税：161.95 | 税额：21.05 | 购买方：罗庆宇 | 销售方：广州梓夕贸易有限公司 | 税号：91440101MA59M7J471");
//        body.put("commentUserId","16441447913471369");
////        body.put("file",fileMap);
//
//        String re  =UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/comments",ddClient.initTokenHeader(),null,body);
//        System.out.println("re"+ re);
//        if (Objects.nonNull(processInstance)) {
//            Map formData = new HashMap<>();
//            String title = String.valueOf(processInstance.get("title"));
//            String type = "";
//            List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
//        }
        return McR.success();
    }
    @GetMapping("/files/{fileId}")
    public ResponseEntity<Resource> getFileResource(
            @PathVariable String fileId,
            @RequestParam(defaultValue = "download") String option) throws IOException {

        // 根据fileId获取实际文件路径，这里简化处理，实际可能需要从数据库查询
        Path filePath = Paths.get(bdurl).resolve(fileId).normalize();

        // 检查文件是否存在
        if (!Files.exists(filePath)) {
            return ResponseEntity.notFound().build();
        }

        // 创建Resource对象
        Resource resource =  new UrlResource(filePath.toUri());

        // 根据选项设置响应头
        HttpHeaders headers = new HttpHeaders();
        if ("preview".equalsIgnoreCase(option)) {
            // 预览模式 - 尝试确定内容类型
            String contentType = Files.probeContentType(filePath);
            // 强制修正 PDF 的 Content-Type
            if (filePath.toString().toLowerCase().endsWith(".pdf")) {
                contentType = "application/pdf";
            } else if (contentType == null) {
                contentType = "application/octet-stream";
            }
            headers.setContentType(MediaType.parseMediaType(contentType));
            headers.add("Content-Disposition", "inline; filename=\"" + resource.getFilename() + "\"");
            headers.add("X-Content-Type-Options", "nosniff"); // 防止浏览器忽略 Content-Type
        } else {
            // 下载模式 - 默认处理
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
        }

        return ResponseEntity.ok()
                .headers(headers)
                .contentLength(resource.contentLength())
                .body(resource);
    }
    @PostMapping("/test1")
    public McR test1(@RequestBody Map<String, String> data) throws TencentCloudSDKException {
        log.info("test: {}", data);
        McException.assertParamException_Null(data, "url");
//        String image = ydClient.convertTemporaryUrl(data.get("url"));
        String image = data.get("url");
        log.info("混票识别, 免登地址, {}", image);
        // 非PDF, 且内存大于3M, 压缩后上传
        if (UtilMap.getFloat(data, "size") > 3.0f && !UtilMap.getBoolean(data, "isPdf")) {
            image = imageUrlConvertBase64(image);
        }
        if (UtilMap.getFloat(data, "size") > 6.0f && UtilMap.getBoolean(data, "isPdf")) {
            image = pdfUrlConvertBase64(image);
        }

        List<Map> invoices = (List<Map>) txyInvoice.doRecognizeGeneralInvoice(image).get("MixedInvoiceItems");
        System.out.println("invoices===="+invoices);
        List<String> nos=new ArrayList<>();
        List<McInvoiceDto> result = invoices.stream().map(item -> {
            Map prop = UtilMap.getMap(UtilMap.getMap(item, "SingleInvoiceInfos"), UtilMap.getString(item, "SubType"));

            List<Map<String, Object>> vatInvoiceItemInfos = UtilMap.getList(prop, "VatInvoiceItemInfos");
            if (!vatInvoiceItemInfos.isEmpty()) {
                String taxRate = UtilMap.getString(vatInvoiceItemInfos.get(0), "TaxRate");
                System.out.println("TaxRate: " + taxRate); // 输出: 9%
            }
            System.out.println("prop====="+prop);
            String no=UtilMap.getString(prop, "Number");
            if(nos.contains(no)){
                return null;
            }
            nos.add(no);
            String kind = UtilMap.getString(item, "TypeDescription");
            String invoiceName = UtilMap.getString(item, "SubTypeDescription");
            if (kind.equals("全电发票")) {
                if(invoiceName.contains("铁路电子客票")){
                    kind="火车票";
                } else if(invoiceName.contains("机票行程单")){
                    kind="机票行程单";
                } else if (invoiceName.contains("专用发票")) {
                    kind="全电专用发票";
                }else {
                    kind="全电普通发票";
                }
            }
            if (kind.equals("增值税发票")) {
                if(invoiceName.contains("区块链电子发票")){
                    kind="区块链电子发票";
                } else if(invoiceName.contains("增值税专用发票")){
                    kind="增值税专用发票";
                } else if(invoiceName.contains("增值税普通发票")){
                    kind="增值税普通发票";
                } else if (invoiceName.contains("增值税电子专用发票")) {
                    kind="增值税电子专用发票";
                } else if (invoiceName.contains("增值税电子普通发票")) {
                    kind="增值税电子普通发票";
                }else {
                    kind="增值税普通发票";
                }
            }
            // ppExt: 通用字段定义
            McInvoiceDto invoiceDto = McInvoiceDto.builder()
                    .name(UtilMap.getString(item, "SubTypeDescription"))
                    .kindName(kind)
                    .kind(UtilMap.getInt(item, "Type"))
                    .code(UtilMap.getString(prop, "Code"))
                    .serial(UtilMap.getString(prop, "Number"))
                    .date(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "Date")))
                    .checkCode(UtilMap.getString(prop, "CheckCode"))
                    // ppExt: 多明细行时, 优先取值合计 [全电票返回了subTotal字段, 但值为空] // 2025.5.12 去除小计金额，并去除发票编号重复数据
                    .amount(UtilNumber.setBigDecimal(UtilMap.getString_first(prop,  "Total", "Fare"))) // "SubTotal",
                    .tax(UtilNumber.setBigDecimal(UtilMap.getString_first(prop,  "Tax"))) // "SubTax",
                    .excludingTax(UtilNumber.setBigDecimal(UtilMap.getString(prop, "PretaxAmount")))
                    .buyerName(StringUtils.isBlank(guyuanNameRepalce(UtilMap.getString(prop, "Buyer")))?"上海能辉科技股份有限公司":guyuanNameRepalce(UtilMap.getString(prop, "Buyer")))
                    // ppExt: 中央非税未返回税号官方说明: 非税发票理论是没有税号的，图片中属于信用代码
                    .buyerTaxId(StringUtils.isBlank(UtilMap.getString(prop, "BuyerTaxID"))?"": UtilMap.getString(prop, "BuyerTaxID"))
                    .sellerName(guyuanNameRepalce(UtilMap.getString_first(prop, "Seller", "Issuer")))                            // 行程单: 填开单位
                    .sellerTaxId(UtilMap.getString_first(prop, "SellerTaxID", "AgentCode"))                       // 行程单: 销售单位代号
                    .passengerName(UtilMap.getString_first(prop, "Name", "UserName"))                             // 火车票, 行程单
                    // 交通出行
                    .seatType(UtilMap.getString(prop, "Seat"))
                    .departureTime(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "DateGetOn")) + " " + UtilMap.getString(prop, "TimeGetOn"))
                    .departurePort(UtilMap.getString_first(prop, "StationGetOn", "Entrance", "Place"))            // 火车票: 出发车站, 过路过桥费: 入口, 出租车: 发票所在地
                    .arrivePort(UtilMap.getString_first(prop, "StationGetOff", "Exit"))                           // 行程单, 火车票: 到达车站, 过路过桥费: 出口
                    .trainNo(UtilMap.getString_first(prop, "TrainNumber", "LicensePlate"))                        // 火车票: 车次, 出租车: 车牌号
                    .insuranceCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "Insurance"))))               // 行程单: 保险费
                    .fuelCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "FuelSurcharge"))))                // 行程单: 燃油附加费
                    .constructionCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "AirDevelopmentFund"))))   // 行程单: 民航发展基金
                    .build();
            // ppExt: 机票行程单, 行程与座位信息在明细内
            if ("机票行程单".equals(kind)) {
                invoiceDto.setSellerName(UtilMap.getString_first(prop, "Issuer"));
                invoiceDto.setSellerTaxId(UtilMap.getString_first(prop, "Seller"));
                Map flight = (Map) UtilMap.getList(prop, "FlightItems").get(0);
                invoiceDto.setDepartureTime(UtilString.replaceDateZH_cn(UtilMap.getString(item, "DateGetOn")) + " " + UtilMap.getString(prop, "TimeGetOn"));
                invoiceDto.setDeparturePort(UtilMap.getString(flight, "StationGetOn"));
                invoiceDto.setArrivePort(UtilMap.getString(flight, "StationGetOff"));
                invoiceDto.setSeatType(UtilMap.getString(flight, "Seat"));
            }
            if ("出租车发票".equals(item.get("TypeDescription"))) {
                // 上下车时间
                invoiceDto.setDepartureTime(UtilMap.getString(prop, "TimeGetOn") + " ~ " + UtilMap.getString(prop, "TimeGetOff"));
            }
            return invoiceDto;
        }).filter(item -> item!=null).collect(Collectors.toList());
        return McR.success(McInvoiceDto.formatResponse(result));

    }
    @PostMapping("invoice-iv2")
    McR invoice_iv2(@RequestBody Map<String, String> data) throws TencentCloudSDKException {

        McException.assertParamException_Null(data, "url");
        String image = ydClient.convertTemporaryUrl(data.get("url"));
        log.info("混票识别, 免登地址, {}", image);
        // 非PDF, 且内存大于3M, 压缩后上传
        if (UtilMap.getFloat(data, "size") > 3.0f && !UtilMap.getBoolean(data, "isPdf")) {
            image = imageUrlConvertBase64(image);
        }
        if (UtilMap.getFloat(data, "size") > 6.0f && UtilMap.getBoolean(data, "isPdf")) {
            image = pdfUrlConvertBase64(image);
        }
        List<Map> invoices = (List<Map>) txyInvoice.doRecognizeGeneralInvoice(image).get("MixedInvoiceItems");
        List<String> nos=new ArrayList<>();
        List<McInvoiceDto> result = invoices.stream().map(item -> {
            Map prop = UtilMap.getMap(UtilMap.getMap(item, "SingleInvoiceInfos"), UtilMap.getString(item, "SubType"));
            String no=UtilMap.getString(prop, "Number");
            if(nos.contains(no)){
                return null;
            }
            nos.add(no);
            String kind = UtilMap.getString(item, "TypeDescription");
            String invoiceName = UtilMap.getString(item, "SubTypeDescription");
            if (kind.equals("全电发票")) {
                if(invoiceName.contains("铁路电子客票")){
                    kind="火车票";
                } else if(invoiceName.contains("机票行程单")){
                    kind="机票行程单";
                } else if (invoiceName.contains("专用发票")) {
                    kind="全电专用发票";
                }else {
                    kind="全电普通发票";
                }
            }
            if (kind.equals("增值税发票")) {
                if(invoiceName.contains("区块链电子发票")){
                    kind="区块链电子发票";
                } else if(invoiceName.contains("增值税专用发票")){
                    kind="增值税专用发票";
                } else if(invoiceName.contains("增值税普通发票")){
                    kind="增值税普通发票";
                } else if (invoiceName.contains("增值税电子专用发票")) {
                    kind="增值税电子专用发票";
                } else if (invoiceName.contains("增值税电子普通发票")) {
                    kind="增值税电子普通发票";
                }else {
                    kind="增值税普通发票";
                }
            }
            // ppExt: 通用字段定义
            McInvoiceDto invoiceDto = McInvoiceDto.builder()
                    .name(UtilMap.getString(item, "SubTypeDescription"))
                    .kindName(kind)
                    .kind(UtilMap.getInt(item, "Type"))
                    .code(UtilMap.getString(prop, "Code"))
                    .serial(UtilMap.getString(prop, "Number"))
                    .date(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "Date")))
                    .checkCode(UtilMap.getString(prop, "CheckCode"))
                    // ppExt: 多明细行时, 优先取值合计 [全电票返回了subTotal字段, 但值为空] // 2025.5.12 去除小计金额，并去除发票编号重复数据
                    .amount(UtilNumber.setBigDecimal(UtilMap.getString_first(prop,  "Total", "Fare"))) // "SubTotal",
                    .tax(UtilNumber.setBigDecimal(UtilMap.getString_first(prop,  "Tax"))) // "SubTax",
                    .excludingTax(UtilNumber.setBigDecimal(UtilMap.getString(prop, "PretaxAmount")))
                    .buyerName(StringUtils.isBlank(guyuanNameRepalce(UtilMap.getString(prop, "Buyer")))?"上海能辉科技股份有限公司":guyuanNameRepalce(UtilMap.getString(prop, "Buyer")))
                    // ppExt: 中央非税未返回税号官方说明: 非税发票理论是没有税号的，图片中属于信用代码
                    .buyerTaxId(StringUtils.isBlank(UtilMap.getString(prop, "BuyerTaxID"))?"": UtilMap.getString(prop, "BuyerTaxID"))
                    .sellerName(guyuanNameRepalce(UtilMap.getString_first(prop, "Seller", "Issuer")))                            // 行程单: 填开单位
                    .sellerTaxId(UtilMap.getString_first(prop, "SellerTaxID", "AgentCode"))                       // 行程单: 销售单位代号
                    .passengerName(UtilMap.getString_first(prop, "Name", "UserName"))                             // 火车票, 行程单
                    // 交通出行
                    .seatType(UtilMap.getString(prop, "Seat"))
                    .departureTime(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "DateGetOn")) + " " + UtilMap.getString(prop, "TimeGetOn"))
                    .departurePort(UtilMap.getString_first(prop, "StationGetOn", "Entrance", "Place"))            // 火车票: 出发车站, 过路过桥费: 入口, 出租车: 发票所在地
                    .arrivePort(UtilMap.getString_first(prop, "StationGetOff", "Exit"))                           // 行程单, 火车票: 到达车站, 过路过桥费: 出口
                    .trainNo(UtilMap.getString_first(prop, "TrainNumber", "LicensePlate"))                        // 火车票: 车次, 出租车: 车牌号
                    .insuranceCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "Insurance"))))               // 行程单: 保险费
                    .fuelCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "FuelSurcharge"))))                // 行程单: 燃油附加费
                    .constructionCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "AirDevelopmentFund"))))   // 行程单: 民航发展基金
                    .build();
            // ppExt: 机票行程单, 行程与座位信息在明细内
            if ("机票行程单".equals(kind)) {
                invoiceDto.setSellerName(UtilMap.getString_first(prop, "Issuer"));
                invoiceDto.setSellerTaxId(UtilMap.getString_first(prop, "Seller"));
                Map flight = (Map) UtilMap.getList(prop, "FlightItems").get(0);
                invoiceDto.setDepartureTime(UtilString.replaceDateZH_cn(UtilMap.getString(item, "DateGetOn")) + " " + UtilMap.getString(prop, "TimeGetOn"));
                invoiceDto.setDeparturePort(UtilMap.getString(flight, "StationGetOn"));
                invoiceDto.setArrivePort(UtilMap.getString(flight, "StationGetOff"));
                invoiceDto.setSeatType(UtilMap.getString(flight, "Seat"));
            }
            if ("出租车发票".equals(item.get("TypeDescription"))) {
                // 上下车时间
                invoiceDto.setDepartureTime(UtilMap.getString(prop, "TimeGetOn") + " ~ " + UtilMap.getString(prop, "TimeGetOff"));
            }
            return invoiceDto;
        }).filter(item -> item!=null).collect(Collectors.toList());
        return McR.success(McInvoiceDto.formatResponse(result));
    }

//校验查重
    @PostMapping("invoice-va")
    McR invoice_va(@RequestBody Map data) {
        McException.assertParamException_Null(data, "param");
        List<McInvoiceDto> invoices = JSON.parseArray(JSON.toJSONString(data.get("param")), McInvoiceDto.class);

        log.info("发票查重, 验真, {}", invoices);
        invoices.forEach(UtilMc.consumerWithIndex((item, index) -> {

            McInvoiceDto dto = (McInvoiceDto) item;
            String invoiceNo = dto.getSerial(); // 唯一标识, 发票号码

            String serial = "第【" + (index + 1) + "】张发票";
            validateBuyer(dto.getBuyerName(), serial + "有疑问");
            McException.assertAccessException(StringUtils.isBlank(invoiceNo), serial + ", 识别结果为空, 请检查!");
            YDParam ydParam = YDParam.builder()
                    .formUuid("FORM-W2A66Z910O9B3LP9C6IYUDPRVWY62DO0YHIILY")
                    .searchFieldJson(JSON.toJSONString(UtilMap.map("radioField_liihyrtb, textField_liihyrt8", "否", invoiceNo)))
                    .build();
            List<String> idList = (List<String>) ydClient.queryData(ydParam, YDConf.FORM_QUERY.retrieve_search_form_id).getData();
            if (idList.size() > 0) {
                McException.exceptionAccess(serial + "已存在, 请勿重复提交!");
            }
            // prd 仅仅识别增值税普通发票
//            if (dto.getName().contains("普通发票")) {
//                String serialTips = serial + "有疑问";
//                try {
//                    // ppExt: 识别与验真后抬头对比 [全电票, 新版本识别接口, 返回名称为: 电子发票(普通发票) 不包含全电标识, 发类型为: 全电发票. 注意取值]
//                    Map rsp = txyInvoice.doVatInvoiceVerifyNew(dto.getKindName(), dto.getCode(), invoiceNo, dto.getDate(), String.valueOf(dto.getAmount()), dto.getCheckCode(), String.valueOf(dto.getExcludingTax()), serialTips);
//                    Map invoice = (Map) rsp.get("Invoice");
//                    McException.assertAccessException(!dto.getBuyerName().equals(guyuanNameRepalce(invoice.get("BuyerName").toString())), serialTips + ", 购买方名称不匹配!");
//                    McException.assertAccessException(!dto.getBuyerTaxId().equals(invoice.get("BuyerTaxCode")), serialTips + ", 购买方税号不匹配!");
//                    McException.assertAccessException(!dto.getSellerName().equals(guyuanNameRepalce(invoice.get("SellerName").toString())), serialTips + ", 销售方名称不匹配!");
//                    McException.assertAccessException(!dto.getSellerTaxId().equals(invoice.get("SellerTaxCode")), serialTips + ", 销售方税号不匹配!");
//                } catch (TencentCloudSDKException e) {
//                    log.error(e.getMessage(), e);
//                    // prd: 上传发票为假发票时，提示：该发票有疑问，请联系财务人员
//                    String message = e.getMessage();
//                    // ppExt: 已经是新版本接口, 过滤提示 [官方答复: 提示不会检测您是否使用的是新版，所有的用户都会提示, 忽略即可]
//                    if (message.contains("温馨提示")) {
//                        message = message.split("温馨提示")[0];
//                    }
//                    if (message.contains("发票不存在")) {
//                        message = "有疑问，请联系财务人员";
//                    }
//                    McException.exceptionAccess(serial + message);
//                }
//            }
        }));
        return McR.success();
    }
    /// url压缩转base64
    @SneakyThrows
    private String imageUrlConvertBase64(String imageUrl) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        // scale(比例), outputQuality(质量)
        Thumbnails.fromURLs(Arrays.asList(new URL(imageUrl))).scale(0.5f).outputQuality(0.25f).toOutputStream(out);
        InputStream inputStream = new ByteArrayInputStream(out.toByteArray());
        //转换为base64
        byte[] bytes = IOUtils.toByteArray(inputStream);
        return Base64.getEncoder().encodeToString(bytes);
    }
    private static String guyuanNameRepalce(String name) {
        name=name.replaceAll(" ","");
        if (name.contains("谷元")) {
            return UtilString.replaceBracketIsWhole(name);
        } else {
            return UtilString.replaceBracketIsSemiangle(name);
        }
    }
    @Autowired
    private FilePath filePath;

    /// PDF压缩转base64
    @SneakyThrows
    private String pdfUrlConvertBase64(String pdfUrl) {
        String fileName = "tmp_" + new Date().getTime() + ".pdf";
        // 下载文件
        File file = UtilFile.mkdirIfNot(fileName, filePath.getPath().getTmp());
        UtilHttp.doDownload(pdfUrl, file);

        // PDF压缩
        PdfDocument doc = new PdfDocument(); // 创建PdfDocument类的对象
        doc.loadFromFile(file.getAbsolutePath()); // 加载PDF文档
        doc.getFileInfo().setIncrementalUpdate(false); // 禁用增量更新
        doc.setCompressionLevel(PdfCompressionLevel.Normal); // 将压缩级别设置为最佳

        // 遍历文档页面
        for (int i = 0; i < doc.getPages().getCount(); i++) {
            PdfPageBase page = doc.getPages().get(i);  // 获取指定页面
            PdfImageInfo[] images = page.getImagesInfo(); // 获取每个页面的图像信息集合
            // 遍历集合中的所有项目
            if (images != null && images.length > 0)
                for (int j = 0; j < images.length; j++) {
                    PdfImageInfo image = images[j];  // 获取指定图片
                    PdfBitmap bp = new PdfBitmap(image.getImage());
                    bp.setQuality(30); // 设置压缩质量
                    page.replaceImage(j, bp); // 将原始图像替换为压缩图像
                }
        }
        // 将结果文档保存至另一个PDF文档中: 覆盖
        doc.saveToFile(file.getAbsolutePath());
        doc.close();
        // PDF转base64, 无需透出本地文件地址
        String base64 = UtilFile.fileToBase64(file.getAbsolutePath());
        // 删除临时PDF文件
        UtilFile.deleteFile(file.getAbsolutePath());
        return base64;
    }
    private void validateBuyer(String BuyerName, String tips) {
        List<String> corpNames = Arrays.asList(
                "谷元(上海)文化科技有限责任公司",
                "上海爱鱼文化传媒有限公司",
                "上海渔米可禧文化传媒有限公司",
                "上海巧豆文化传媒有限公司",
                "钰鸿文化创意（上海）有限公司",
                "上海攸元文化科技有限公司",
                "上海盈田演出经纪有限公司",
                "上海小蝠文化科技有限公司",
                "沐田居海（厦门）文化传媒有限公司",
                "北京元环文化科技有限责任公司",
                "厦门攸元文化科技有限公司",
                "厦门亨有文化科技有限公司",
                "厦门银雀思汀文化传媒有限公司",
                "上海渝泽信息科技有限公司",
                "厦门神谷飞流影视传媒有限公司",
                "厦门谷钛数字科技有限公司",
                "上海观情科技有限公司",
                "渔米可禧文化传媒（香港）有限公司",
                "杭州渔米可禧文化传媒有限公司");
        McException.assertAccessException(!corpNames.contains(BuyerName), tips + ", 购买方名称不合法!");
    }

    //下载钉钉OA审批单附件
    private void downloadDdFile(String processInstanceId,String fileId,String downloadPath){
        try {
            Map body = new HashMap();
            body.put("processInstanceId",processInstanceId);
            body.put("fileId",fileId);

            DDR_New ddrNew = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/spaces/files/urls/download", ddClient.initTokenHeader(), null, body, DDR_New.class);

            //2、执行下载
            Map result = (Map) ddrNew.getResult();

            String downloadUri = UtilMap.getString(result, "downloadUri");

            downloadFile(downloadUri,downloadPath);

        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    //文件下载到本地
    private void downloadFile(String downloadUri,String downloadPath){
        try {
            URL url = new URL(downloadUri);

            HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
            int responseCode = httpConn.getResponseCode();

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

                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();
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
    /**
     * 私有化方法：混票识别处理
     * @param data 包含url、size、isPdf的Map参数
     * @return 识别结果
     */
    private McR<Object> processMixedInvoice(Map<String, Object> data) throws TencentCloudSDKException {
        McException.assertParamException_Null(data, "url");
        String image = String.valueOf(data.get("url"));
        log.info("混票识别, 免登地址, {}", image);

        // 非PDF, 且内存大于3M, 压缩后上传
        if (UtilMap.getFloat(data, "size") > 3.0f && !UtilMap.getBoolean(data, "isPdf")) {
            image = imageUrlConvertBase64(image);
        }
        if (UtilMap.getFloat(data, "size") > 6.0f && UtilMap.getBoolean(data, "isPdf")) {
            image = pdfUrlConvertBase64(image);
        }

        List<Map> invoices = (List<Map>) txyInvoice.doRecognizeGeneralInvoice(image).get("MixedInvoiceItems");
        System.out.println("invoices====="+invoices);
        List<String> nos = new ArrayList<>();

        List<McInvoiceDto> result = invoices.stream().map(item -> {
            Map prop = UtilMap.getMap(UtilMap.getMap(item, "SingleInvoiceInfos"), UtilMap.getString(item, "SubType"));
            String no = UtilMap.getString(prop, "Number");
            if (nos.contains(no)) {
                return null;
            }
            nos.add(no);
            String kind = UtilMap.getString(item, "TypeDescription");
            String invoiceName = UtilMap.getString(item, "SubTypeDescription");

            if (kind.equals("全电发票")) {
                if (invoiceName.contains("铁路电子客票")) {
                    kind = "火车票";
                } else if (invoiceName.contains("机票行程单")) {
                    kind = "机票行程单";
                } else if (invoiceName.contains("专用发票")) {
                    kind = "全电专用发票";
                } else {
                    kind = "全电普通发票";
                }
            }

            if (kind.equals("增值税发票")) {
                if (invoiceName.contains("区块链电子发票")) {
                    kind = "区块链电子发票";
                } else if (invoiceName.contains("增值税专用发票")) {
                    kind = "增值税专用发票";
                } else if (invoiceName.contains("增值税普通发票")) {
                    kind = "增值税普通发票";
                } else if (invoiceName.contains("增值税电子专用发票")) {
                    kind = "增值税电子专用发票";
                } else if (invoiceName.contains("增值税电子普通发票")) {
                    kind = "增值税电子普通发票";
                } else {
                    kind = "增值税普通发票";
                }
            }

            McInvoiceDto invoiceDto = McInvoiceDto.builder()
                    .name(UtilMap.getString(item, "SubTypeDescription"))
                    .kindName(kind)
                    .kind(UtilMap.getInt(item, "Type"))
                    .code(UtilMap.getString(prop, "Code"))
                    .serial(UtilMap.getString(prop, "Number"))
                    .date(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "Date")))
                    .checkCode(UtilMap.getString(prop, "CheckCode"))
                    .amount(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "Total", "Fare")))
                    .tax(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "Tax")))
                    .excludingTax(UtilNumber.setBigDecimal(UtilMap.getString(prop, "PretaxAmount")))
                    .buyerName(StringUtils.isBlank(guyuanNameRepalce(UtilMap.getString(prop, "Buyer")))
                            ? "上海能辉科技股份有限公司" : guyuanNameRepalce(UtilMap.getString(prop, "Buyer")))
                    .buyerTaxId(StringUtils.isBlank(UtilMap.getString(prop, "BuyerTaxID"))
                            ? "" : UtilMap.getString(prop, "BuyerTaxID"))
                    .sellerName(guyuanNameRepalce(UtilMap.getString_first(prop, "Seller", "Issuer")))
                    .sellerTaxId(UtilMap.getString_first(prop, "SellerTaxID", "AgentCode"))
                    .passengerName(UtilMap.getString_first(prop, "Name", "UserName"))
                    .seatType(UtilMap.getString(prop, "Seat"))
                    .departureTime(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "DateGetOn"))
                            + " " + UtilMap.getString(prop, "TimeGetOn"))
                    .departurePort(UtilMap.getString_first(prop, "StationGetOn", "Entrance", "Place"))
                    .arrivePort(UtilMap.getString_first(prop, "StationGetOff", "Exit"))
                    .trainNo(UtilMap.getString_first(prop, "TrainNumber", "LicensePlate"))
                    .insuranceCosts(UtilNumber.setBigDecimal(UtilMap.getString(prop, "Insurance")))
                    .fuelCosts(UtilNumber.setBigDecimal(UtilMap.getString(prop, "FuelSurcharge")))
                    .constructionCosts(UtilNumber.setBigDecimal(UtilMap.getString(prop, "AirDevelopmentFund")))
                    .build();

            if ("机票行程单".equals(kind)) {
                invoiceDto.setSellerName(UtilMap.getString_first(prop, "Issuer"));
                invoiceDto.setSellerTaxId(UtilMap.getString_first(prop, "Seller"));
                Map flight = (Map) UtilMap.getList(prop, "FlightItems").get(0);
                invoiceDto.setDepartureTime(UtilString.replaceDateZH_cn(UtilMap.getString(item, "DateGetOn"))
                        + " " + UtilMap.getString(prop, "TimeGetOn"));
                invoiceDto.setDeparturePort(UtilMap.getString(flight, "StationGetOn"));
                invoiceDto.setArrivePort(UtilMap.getString(flight, "StationGetOff"));
                invoiceDto.setSeatType(UtilMap.getString(flight, "Seat"));
            }

            if ("出租车发票".equals(item.get("TypeDescription"))) {
                invoiceDto.setDepartureTime(UtilMap.getString(prop, "TimeGetOn")
                        + " ~ " + UtilMap.getString(prop, "TimeGetOff"));
            }

            return invoiceDto;
        }).filter(Objects::nonNull).collect(Collectors.toList());

        return McR.success(McInvoiceDto.formatResponse(result));
    }




}
