package com.xhpc.activity.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.csv.CsvData;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.xhpc.activity.domain.XhpcTradebillUploadRecordDomain;
import com.xhpc.common.core.exception.CustomException;
import com.xhpc.common.core.utils.SecurityUtils;
import com.xhpc.activity.domain.XhpcTradebillPaymentCheckRecordDomain;
import com.xhpc.activity.mapper.XhpcRechargeOrderMapper;
import com.xhpc.activity.mapper.XhpcRefundOrderMapper;
import com.xhpc.activity.mapper.XhpcTradebillPaymentCheckRecordMapper;
import com.xhpc.activity.service.PaymentBillService;
import com.xhpc.activity.utils.DownloadUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;


@Slf4j
@Service
public class PaymentBillServiceImpl implements PaymentBillService {

    @Resource
    XhpcRechargeOrderMapper rechargeOrderMapper;

    @Resource
    XhpcRefundOrderMapper refundOrderMapper;

    @Resource
    XhpcTradebillPaymentCheckRecordMapper paymentCheckRecordMapper;


    @Override
    public XhpcTradebillPaymentCheckRecordDomain getInfoByPk(int pk) {

        return paymentCheckRecordMapper.selectByPrimaryKey(pk);
    }

    @Override
    public boolean updateInfoByDomain(XhpcTradebillPaymentCheckRecordDomain domain) {

        domain.setUpdateBy(SecurityUtils.getUserId());
        return paymentCheckRecordMapper.updateStatusByPk(domain);
    }


    @Override
    public Integer insertPaymentCheckList(XhpcTradebillUploadRecordDomain recordDomain, List<XhpcTradebillPaymentCheckRecordDomain> paymentCheckList) {

        Long userId = SecurityUtils.getUserId();
        for (XhpcTradebillPaymentCheckRecordDomain domain : paymentCheckList) {
            domain.setUploadId(recordDomain.getId());
            domain.setTenantId(recordDomain.getTenantId());
            domain.setCreateBy(userId);
            domain.setUpdateBy(userId);
        }
        return paymentCheckRecordMapper.insertCheckList(paymentCheckList);
    }

    @Override
    public Map<String, Object> getStatusTotal(Map<String, Object> params) {

        List<Map<String, Object>> resultList = paymentCheckRecordMapper.selectTotalGroupbyStatus(params);
        int successCount = 0;
        int failCount = 0;
        int uncheckCount = 0;
        int checkCount = 0;
        for (Map<String, Object> res : resultList) {
            switch (res.get("status").toString()) {
                case "0":
                    uncheckCount = Integer.parseInt(res.get("total").toString());
                    break;
                case "1":
                    failCount = Integer.parseInt(res.get("total").toString());
                    break;
                case "2":
                    successCount = Integer.parseInt(res.get("total").toString());
                    break;
                default:
                    break;
            }
        }
        checkCount = successCount + failCount;
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("successCount", successCount);
        resultMap.put("failCount", failCount);
        resultMap.put("uncheckCount", uncheckCount);
        resultMap.put("checkCount", checkCount);
        return resultMap;
    }

    @Override
    public List<XhpcTradebillPaymentCheckRecordDomain> getPaymentCheckList(Map<String, Object> params) {

        return paymentCheckRecordMapper.selectListByParams(params);
    }


    /**
     * 支付宝订单对账
     *
     * @param fileUrl 数据文件存放地址
     * @return 对账完成的数据列表
     */
    public List<XhpcTradebillPaymentCheckRecordDomain> getAliPayDataList(String fileUrl, String startTime, String endTime, String tenantId) throws Exception {

        File dataFile = DownloadUtil.downloadFile(fileUrl);
        return getAliPayDataList(dataFile, startTime, endTime, tenantId);
    }


    /**
     * 支付宝订单对账
     *
     * @param dataFile 数据文件
     * @return 对账完成的数据列表
     */
    public List<XhpcTradebillPaymentCheckRecordDomain> getAliPayDataList(File dataFile, String startTime, String endTime, String tenantId) throws
            Exception {

        Long userId = SecurityUtils.getUserId();

        List<XhpcTradebillPaymentCheckRecordDomain> paymentCheckRecordDomainList = new ArrayList<>();
        String suffix = dataFile.getName().substring(dataFile.getName().lastIndexOf(".") + 1);
        System.out.println(dataFile.getAbsolutePath());
        try {
            switch (suffix.toUpperCase()) {
                case "CSV":
                    CsvReader csvReader = CsvUtil.getReader();
                    CsvData data = csvReader.read(dataFile, CharsetUtil.CHARSET_GBK);
                    List<CsvRow> rows = data.getRows();

                    List<String> serialNumberList = new ArrayList<>();
                    int orderType = 0;

                    for (CsvRow row : rows) {
                        // 列数是22，则是正文内容
                        if (row.getRawList().size() != 22) {
                            continue;
                        }
                        // 判断内容是否是中文，如果是中文就认为是标题，则跳过
                        if (row.get(0).getBytes(StandardCharsets.UTF_8).length != row.get(0).length()) {
                            continue;
                        }
                        XhpcTradebillPaymentCheckRecordDomain domain = new XhpcTradebillPaymentCheckRecordDomain();
                        domain.setCreateBy(userId);
                        domain.setUpdateBy(userId);
                        domain.setPaymentChannel(2);
                        domain.setPaymentUserId(row.get(12));
                        domain.setPaymentPayTime(DateUtil.parse(row.get(1)));

                        BigDecimal paymentAmount = BigDecimal.ZERO;
                        String serialNumber = "";

                        if (row.get(5).equals("在线支付")) {
                            // 充值订单
                            serialNumber = row.get(4).trim().replaceAll("`", "").replaceAll("'", "");
                            orderType = 1;
                            domain.setPaymentPayNumber(serialNumber);
                            domain.setOrderType(orderType);
                            System.out.println(row);
                            paymentAmount = row.get(6) != null && !"".equals(row.get(6).trim()) ? new BigDecimal(row.get(6)) : BigDecimal.ZERO;
                            domain.setPaymentAmount(paymentAmount);

                            serialNumberList.add(serialNumber);
                        } else if (row.get(5).equals("转账")) {
                            // 退款订单
                            serialNumber = row.get(16).trim().replaceAll("`", "").replaceAll("'", "").replaceAll("单笔转账--", "");
                            domain.setPaymentPayNumber(serialNumber);
                            orderType = 2;
                            domain.setOrderType(orderType);

                            paymentAmount = row.get(7) != null && !"".equals(row.get(7).trim()) ? new BigDecimal(row.get(7)) : BigDecimal.ZERO;
                            domain.setPaymentAmount(paymentAmount);

                            serialNumberList.add(serialNumber);
                        }
                        paymentCheckRecordDomainList.add(domain);
                    }
                    getPlatOrderData(paymentCheckRecordDomainList, orderType, serialNumberList, 2, startTime, endTime, tenantId);

                    break;
                case "ZIP":
                    ZipFile zip = new ZipFile(dataFile);
                    File file = null;
                    for (Enumeration<ZipEntry> enumeration = zip.getEntries(); enumeration.hasMoreElements(); ) {
                        ZipEntry entry = enumeration.nextElement();
                        InputStream in = zip.getInputStream(entry);
                        if (entry.isDirectory()) {
                            continue;
                        }
                        file = File.createTempFile("alipay_", entry.getName().substring(entry.getName().lastIndexOf(".")));
                        OutputStream outputStream = new FileOutputStream(file);
                        byte[] buff = new byte[1024];
                        int len;
                        while ((len = in.read(buff)) > 0) {
                            outputStream.write(buff, 0, len);
                        }
                        in.close();
                        outputStream.close();
                    }
                    if (file != null) {
                        paymentCheckRecordDomainList = getAliPayDataList(file, startTime, endTime, tenantId);
                    }

                    break;
                default:
                    throw new Exception("暂不支持的文件格式");
            }
        } catch (Exception e){
            e.printStackTrace();
            throw new CustomException("文件数据不合规，请检查");
        }

        paymentCheckRecordDomainList.sort(Comparator.comparing(XhpcTradebillPaymentCheckRecordDomain::getStatus));
        return paymentCheckRecordDomainList;
    }

    /**
     * 微信订单对账
     *
     * @param fileUrl 数据文件存放地址
     * @return 对账完成的数据列表
     */
    public List<XhpcTradebillPaymentCheckRecordDomain> getWechatDataList(String fileUrl, String startTime, String endTime, String tenantId) throws Exception {

        File dataFile = DownloadUtil.downloadFile(fileUrl);
        return getWechatDataList(dataFile, startTime, endTime, tenantId);
    }


    /**
     * 微信订单对账
     *
     * @param dataFile 数据文件
     * @return 对账完成的数据列表
     */
    public List<XhpcTradebillPaymentCheckRecordDomain> getWechatDataList(File dataFile, String startTime, String endTime, String tenantId) throws Exception {

        Long userId = SecurityUtils.getUserId();
        List<XhpcTradebillPaymentCheckRecordDomain> paymentCheckRecordDomainList = new ArrayList<>();
        String suffix = dataFile.getName().substring(dataFile.getName().lastIndexOf(".") + 1);

        try {
            if ("CSV".equalsIgnoreCase(suffix)) {// 微信充值订单格式
                CsvReader csvReader = CsvUtil.getReader();
                CsvData data = csvReader.read(dataFile);
                List<CsvRow> rows = data.getRows();

                List<String> serialNumberList = new ArrayList<>();
                int orderType = 0;
                for (CsvRow row : rows) {
                    // 列数是6，则是充值订单
                    // 列数是13，则是退款订单
                    if (row.getRawList().size() != 6 && row.getRawList().size() != 13) {
                        continue;
                    }
                    XhpcTradebillPaymentCheckRecordDomain domain = new XhpcTradebillPaymentCheckRecordDomain();
                    domain.setPaymentChannel(1);
                    domain.setCreateBy(userId);
                    domain.setUpdateBy(userId);

                    String serialNumber = "";
                    if (row.getRawList().size() == 6) {
                        if (row.get(0).getBytes(StandardCharsets.UTF_8).length != row.get(0).length()) {
                            continue;
                        }
                        orderType = 1;
                        serialNumber = StringFormat(row.get(2));
                        domain.setOrderType(orderType);
                        domain.setPaymentPayNumber(serialNumber);

                        BigDecimal paymentAmount = row.get(5) != null && !"".equals(row.get(5).trim()) ? new BigDecimal(StringFormat(row.get(5))) : BigDecimal.ZERO;

                        domain.setPaymentAmount(paymentAmount);
                        domain.setPaymentPayTime(DateUtil.parse(row.get(0)));

                        serialNumberList.add(serialNumber);
                    } else if (row.getRawList().size() == 13) {
                        // 微信退款订单
                        if (row.get(0).getBytes(StandardCharsets.UTF_8).length != row.get(0).length()) {
                            continue;
                        }
                        orderType = 2;
                        serialNumber = StringFormat(row.get(2));
                        domain.setOrderType(orderType);
                        domain.setPaymentPayNumber(serialNumber);

                        BigDecimal paymentAmount = row.get(10) != null && !"".equals(row.get(10).trim()) ?
                                new BigDecimal(StringFormat(row.get(10)))
                                : BigDecimal.ZERO;

                        domain.setPaymentAmount(paymentAmount);
                        domain.setPaymentUserId(StringFormat(row.get(4)));
                        domain.setPaymentPayTime(DateUtil.parse(row.get(6)));

                        serialNumberList.add(serialNumber);
                    }

                    paymentCheckRecordDomainList.add(computeTradebillData(domain));
                }
                getPlatOrderData(paymentCheckRecordDomainList, orderType, serialNumberList, 1, startTime, endTime, tenantId);

            } else {
                throw new Exception("暂不支持的文件格式");
            }
        } catch (Exception e){
            e.printStackTrace();
            throw new CustomException("文件数据不合规，请检查");
        }

        paymentCheckRecordDomainList.sort(Comparator.comparing(XhpcTradebillPaymentCheckRecordDomain::getStatus));
        return paymentCheckRecordDomainList;
    }

    private String StringFormat(String dataStr) {

        return dataStr.replaceAll("'", "").replaceAll("`", "");
    }


    /**
     * 计算财务支付数据和订单是否对照正常
     *
     * @param domain 需对账的数据
     * @return 对账完成的数据
     */
    public XhpcTradebillPaymentCheckRecordDomain computeTradebillData(XhpcTradebillPaymentCheckRecordDomain domain) {

        if (domain.getPaymentAmount() !=null && domain.getPlatformAmount()!=null
                &&  domain.getPaymentAmount().equals(domain.getPlatformAmount())) {
            domain.setStatus(2);
        } else {
            domain.setStatus(0);
        }
        return domain;
    }




    @Override
    public void export(HttpServletResponse response, Map<String, Object> params) throws Exception {

        List<Map<String, Object>> list = paymentCheckRecordMapper.selectByParams(params);

        ExcelWriter writer = ExcelUtil.getWriter(true);
        writer.addHeaderAlias("order_type", "订单类型（1-充值，2-退款）");
        writer.addHeaderAlias("paymengt_channel", "支付渠道（1-微信，2-支付宝）");

        writer.addHeaderAlias("platform_order_number", "平台-订单编号");
        writer.addHeaderAlias("platform_user_id", "平台-用户帐号");
        writer.addHeaderAlias("platform_amount", "平台-支付金额");
        writer.addHeaderAlias("platform_pay_time", "平台-支付时间");

        writer.addHeaderAlias("payment_pay_number", "支付方-订单编号");
        writer.addHeaderAlias("payment_user_id", "支付方-用户帐号");
        writer.addHeaderAlias("payment_amount", "支付方-支付金额");
        writer.addHeaderAlias("payment_pay_time", "支付方-支付时间");

        writer.addHeaderAlias("status", "对账状态（0-待对账，1-对账错误，2-对账正确）");
        writer.addHeaderAlias("remark", "备注");

        writer.setOnlyAlias(true);
        writer.write(list, true);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        ServletOutputStream out = response.getOutputStream();

        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);
    }



    private List<XhpcTradebillPaymentCheckRecordDomain> getPlatOrderData(List<XhpcTradebillPaymentCheckRecordDomain> domainList,
                                                                         int orderType,
                                                                         List<String> serialNumberList,
                                                                         Integer type,
                                                                         String startTime,
                                                                         String endTime,
                                                                        String tenantId){

        List<Map<String, Object>> orderMapList = null;
        if (orderType == 1) {
            orderMapList = rechargeOrderMapper.findListByTimeBetween(startTime, endTime, type, tenantId);
        } else if (orderType == 2) {
            orderMapList = refundOrderMapper.findListByTimeBetween(startTime, endTime,type, tenantId);
        }

        Map<String, Map<String, Object>> orderList = new HashMap<>();
        for (Map<String, Object> orderMap : orderMapList) {
            orderList.put(orderMap.get("orderNumber").toString().trim(), orderMap);
        }

        for (XhpcTradebillPaymentCheckRecordDomain domain : domainList) {
            Map<String, Object> orderMap = orderList.get(domain.getPaymentPayNumber());
            if (orderMap != null) {
                domain.setPlatformOrderNumber(orderMap.get("orderNumber").toString().trim());
                domain.setPlatformUserId(orderMap.get("userId").toString());
                domain.setPlatformPayNumber(orderMap.get("id").toString());
                domain.setPlatformAmount(new BigDecimal(orderMap.get("amount").toString()));
                domain.setPlatformPayTime(DateUtil.parse(orderMap.get("updateTime").toString(), "yyyy-MM-dd'T'HH:mm"));
            }
            orderList.remove(domain.getPaymentPayNumber());
            computeTradebillData(domain);
        }

        if(orderList.size() > 0){
            for(Map<String, Object> orderMap: orderList.values()){
                XhpcTradebillPaymentCheckRecordDomain domain = new XhpcTradebillPaymentCheckRecordDomain();
                domain.setPlatformOrderNumber(orderMap.get("orderNumber").toString().trim());
                domain.setPlatformUserId(orderMap.get("userId").toString());
                domain.setPlatformPayNumber(orderMap.get("id").toString());
                domain.setPlatformAmount(new BigDecimal(orderMap.get("amount").toString()));
                domain.setPlatformPayTime(DateUtil.parse(orderMap.get("updateTime").toString(), "yyyy-MM-dd'T'HH:mm"));
                computeTradebillData(domain);
                domainList.add(domain);
            }
        }
        domainList.sort(Comparator.comparing(XhpcTradebillPaymentCheckRecordDomain::getStatus));
        return domainList;
    }

}
