package com.dxhy.core.service.dataupload;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dxhy.api.model.sign.TDxInvoiceImg;
import com.dxhy.api.model.sign.TDxScanInvoiceLog;
import com.dxhy.api.model.sign.openservice.OpenServiceOcr;
import com.dxhy.api.model.sign.openservice.SenderInfo;
import com.dxhy.common.constant.DbConstant;
import com.dxhy.common.datasource.config.DynamicContextHolder;
import com.dxhy.common.rabbitmq.RabbitMqSender;
import com.dxhy.common.service.impl.MpBaseServiceImpl;
import com.dxhy.common.util.FileUtil;
import com.dxhy.common.util.ScanIdUtils;
import com.dxhy.common.util.StringHelper;
import com.dxhy.common.utils.Aops;
import com.dxhy.common.utils.DateUtils;
import com.dxhy.core.dao.CoreTDxScanInvoiceLogDao;
import com.dxhy.core.service.config.SysGroupConfigService;
import com.dxhy.core.service.singletable.IDxInvoiceImgService;
import com.dxhy.jxpt.baseService.sign.service.openservice.IOpenServicePlatformService;
import com.dxhy.jxpt.baseService.util.FpzlChangeUtil;
import com.dxhy.oss.model.FileModel;
import com.dxhy.oss.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * ocr数据上传业务逻辑实现类
 * 
 * @author jiaohongyang
 */

@Service("dataUploadService")
@Slf4j
@SuppressWarnings("AlibabaUndefineMagicConstant")
public class SmDataUploadServiceImpl extends MpBaseServiceImpl<CoreTDxScanInvoiceLogDao, TDxScanInvoiceLog>
    implements ISmDataUploadService {

    @Resource
    private IDxInvoiceImgService imgService;

    @Resource
    private FileService fileService;

    @Resource
    private IOpenServicePlatformService openService;

    @Resource
    private RabbitMqSender rabbitLogsSender;
    @Resource
    private ScanIdUtils scanIdUtils;
    @Resource
    private SysGroupConfigService sysGroupConfigService;

    @Override
    public String validateData(String scanData) {
        String msg = null;
        JSONObject scanDataJson = JSONObject.parseObject(scanData);

        if (!scanDataJson.containsKey("pch") || StringHelper.isBlank(scanDataJson.getString("pch"))) {
            msg = "扫描批次号不能为空！";
        } else if (!scanDataJson.containsKey("Database")) {
            msg = "数据库名称标签不能为空！";
        } else {
            if (scanDataJson.containsKey("invoiceList")) {
                JSONArray invoiceList = scanDataJson.getJSONArray("invoiceList");
                if (invoiceList != null && invoiceList.size() > 0) {
                    for (int i = 0; i < invoiceList.size(); i++) {
                        JSONObject invoice = invoiceList.getJSONObject(i);
                        String scanid = invoice.getString("scanid");
                        if (StringHelper.isBlank(scanid)) {
                            msg = "第" + (i + 1) + "条数据scanid为空，不合法！";
                            break;
                        }
                    }
                    String dbName = scanDataJson.getString("Database");
                    DynamicContextHolder.push(dbName + DbConstant.BUSINESS_READ);
                    QueryWrapper<TDxScanInvoiceLog> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("pch", scanDataJson.getString("pch"));
                    int count = Math.toIntExact(super.count(queryWrapper));
                    if (count > 0) {
                        msg = "扫描批次号已存在，不能重复！";
                    }
                } else {
                    msg = "invoiceList数据列表不能为空！";
                }
            } else {
                msg = "invoiceList数据列表不能为空！";
            }
        }
        return msg;
    }

    @Override
    public void saveScanData(String scanData) {
        try {
            JSONObject scanDataJson = JSONObject.parseObject(scanData);

            String pch = scanDataJson.getString("pch");

            String dbName = scanDataJson.getString("Database");

            String scanType = scanDataJson.getString("ScanType");
            String[] split = pch.split("-");
            String loginName = split[0];
            String loginNsrsbh = split[1];
            // 根据 登录名获取 用户关联税号信息，获取 key 和 id
            Map<String, String> userApplyKey = sysGroupConfigService.getApplyKeyByNsrsbh(loginNsrsbh);
            log.info("获取鉴权信息：{}", userApplyKey);
            String aceId = userApplyKey.get("aceId");
            String aceKey = userApplyKey.get("aceKey");
            String taxno = userApplyKey.get("taxno");
            DynamicContextHolder.push(dbName + DbConstant.BUSINESS_WRITE);
            // 数据批量保存扫描图片表
            List<TDxInvoiceImg> invoiceImgList = new ArrayList<>();
            if ("1".equals(scanType) || "5".equals(scanType)) {
                JSONArray invoiceList = scanDataJson.getJSONArray("invoiceList");
                if (invoiceList.size() > 0) {
                    for (int i = 0; i < invoiceList.size(); i++) {
                        JSONObject invoice = invoiceList.getJSONObject(i);
                        String scanid = invoice.getString("scanid");
                        TDxInvoiceImg imgFile = this.getImgFile(scanid);
                        if (imgFile != null) {
                            String imagPath = imgFile.getImagePath();
                            if (imagPath != null && !"".equals(imagPath)) {
                                String localPath = fileService.getLocalPathDefault() + DateUtils.dateToStr(new Date())
                                    + "/" + new File(imagPath).getName();

                                FileModel fileModel = fileService.downFile(imagPath, localPath);
                                invoice.put("filePath", fileModel.getFilePath());
                            }
                        }
                    }

                }
                List<JSONObject> jsonList = toThreadPoolOcr(invoiceList, scanType, aceId, aceKey, taxno);

                JSONObject dataJson = new JSONObject();
                dataJson.put("pch", pch);
                dataJson.put("dbName", dbName);
                dataJson.put("scanType", scanType);
                dataJson.put("invoiceList", jsonList);
                scanData = dataJson.toJSONString();
                log.info("ocr识别信息：{}", scanData);
            }
            // 保存扫描记录表
            TDxScanInvoiceLog scanInvoiceLog = new TDxScanInvoiceLog();
            scanInvoiceLog.setPch(pch);
            scanInvoiceLog.setInvoiceText(scanData);
            scanInvoiceLog.setCreateDate(DateUtil.date());
            DynamicContextHolder.push(dbName + DbConstant.BUSINESS_WRITE);
            Aops.getSelf(this).seveData(scanInvoiceLog, invoiceImgList);
        } catch (Exception e) {
           log.info("业务处理异常：{}", e);
        }

    }

    private JSONObject toOcrInterface(JSONObject invoice, String scanType, String aceId, String aceKey, String taxno) {
        String scanid = invoice.getString("scanid");
        String filePath = invoice.getString("filePath");
        try {

            if (StringUtils.isNotBlank(filePath)) {
                String base64img = FileUtil.fileToBase64(new File(filePath));
                if ("1".equals(scanType)) {
                    List<OpenServiceOcr> invoiceOcrResult = openService.ocrInvoice(base64img, aceId, aceKey, taxno);
                    OpenServiceOcr openServiceOcr = invoiceOcrResult.get(0);
                    invoice.put("fpdm", openServiceOcr.getData().getFPDM());
                    invoice.put("fphm", openServiceOcr.getData().getFPHM());
                    invoice.put("dyFpdm", openServiceOcr.getData().getFPDM());
                    invoice.put("dyFphm", openServiceOcr.getData().getFPHM());
                    invoice.put("gfsh", openServiceOcr.getData().getGMFSBH());
                    invoice.put("gfmc", openServiceOcr.getData().getGMFMC());
                    invoice.put("kprq", openServiceOcr.getData().getKPRQ());
                    invoice.put("je", openServiceOcr.getData().getHJJE());
                    invoice.put("se", openServiceOcr.getData().getHJSE());
                    invoice.put("jshj", openServiceOcr.getData().getJSHJ());
                    invoice.put("jym", openServiceOcr.getData().getJYM());
                    invoice.put("xfsh", openServiceOcr.getData().getXHFSBH());
                    invoice.put("xfmc", openServiceOcr.getData().getXHFMC());
                    invoice.put("xdpbz", openServiceOcr.getData().getXDPBZ());
                    invoice.put("fplx", FpzlChangeUtil.getInvoiceTypeKfptToJxpt(openServiceOcr.getFPLXDM(),openServiceOcr.getData().getXDPBZ()));
                } else if ("5".equals(scanType)) {
                    List<OpenServiceOcr> invoiceOcrResult = openService.ocrInvoice(base64img, aceId, aceKey, taxno);
                    if (invoiceOcrResult != null) {
                        OpenServiceOcr openServiceOcr = invoiceOcrResult.get(0);
                        invoice.put("fpdm", openServiceOcr.getData().getFPDM());
                        invoice.put("fphm", openServiceOcr.getData().getFPHM());
                        invoice.put("dyFpdm", openServiceOcr.getData().getFPDM());
                        invoice.put("dyFphm", openServiceOcr.getData().getFPHM());
                        invoice.put("gfsh", openServiceOcr.getData().getGMFSBH());
                        invoice.put("gfmc", openServiceOcr.getData().getGMFMC());
                        invoice.put("kprq", openServiceOcr.getData().getKPRQ());
                        invoice.put("je", openServiceOcr.getData().getHJJE());
                        invoice.put("se", openServiceOcr.getData().getHJSE());
                        invoice.put("jshj", openServiceOcr.getData().getJSHJ());
                        invoice.put("jym", openServiceOcr.getData().getJYM());
                        invoice.put("xfsh", openServiceOcr.getData().getXHFSBH());
                        invoice.put("xfmc", openServiceOcr.getData().getXHFMC());
                        invoice.put("xdpbz", openServiceOcr.getData().getXDPBZ());
                        if (StringUtils.isNotBlank(openServiceOcr.getFPLXDM()) &&  "22".equals(openServiceOcr.getFPLXDM())) {
                            invoice.put("fplx", "32");
                        }
                        if (StringUtils.isNotBlank(openServiceOcr.getFPLXDM()) && ("21".equals(openServiceOcr.getFPLXDM()))) {
                            invoice.put("fplx", "31");
                        }
                        invoice.put("fplx", FpzlChangeUtil.getInvoiceTypeKfptToJxpt(openServiceOcr.getFPLXDM(),openServiceOcr.getData().getXDPBZ()));
                    } else {
                        invoice.put("fpdm", "F" + scanIdUtils.createInvoiceSerialNum());
                        invoice.put("fphm", "F" + scanIdUtils.createInvoiceSerialNum());
                        invoice.put("dyFpdm", invoice.getString("fpdm"));
                        invoice.put("dyFphm", invoice.getString("fpdm"));
                        invoice.put("gfsh", "");
                        invoice.put("gfmc", "");
                        invoice.put("kprq", DateUtil.formatDate(new Date()));
                        invoice.put("je", "0.00");
                        invoice.put("se", "0.00");
                        invoice.put("jshj", "0.00");
                        invoice.put("jym", "");
                        invoice.put("xfsh", "");
                        invoice.put("xfmc", "");
                    }
                }
            } else {
                invoice.put("fpdm", "F" + scanIdUtils.createInvoiceSerialNum());
                invoice.put("fphm", "F" + scanIdUtils.createInvoiceSerialNum());
                invoice.put("dyFpdm", invoice.getString("fpdm"));
                invoice.put("dyFphm", invoice.getString("fpdm"));
                invoice.put("gfsh", "");
                invoice.put("gfmc", "");
                invoice.put("kprq", DateUtil.formatDate(new Date()));
                invoice.put("je", "0.00");
                invoice.put("se", "0.00");
                invoice.put("jshj", "0.00");
                invoice.put("jym", "");
                invoice.put("xfsh", "");
                invoice.put("xfmc", "");
            }
        } catch (Exception e) {
            log.info("ocr识别出错，scanId:{}", scanid);
            invoice.put("fpdm", "F" + scanIdUtils.createInvoiceSerialNum());
            invoice.put("fphm", "F" + scanIdUtils.createInvoiceSerialNum());
            invoice.put("dyFpdm", invoice.getString("fpdm"));
            invoice.put("dyFphm", invoice.getString("fpdm"));
            invoice.put("gfsh", "");
            invoice.put("gfmc", "");
            invoice.put("kprq", DateUtil.formatDate(new Date()));
            invoice.put("je", "0.00");
            invoice.put("se", "0.00");
            invoice.put("jshj", "0.00");
            invoice.put("jym", "");
            invoice.put("xfsh", "");
            invoice.put("xfmc", "");
        } finally {
            if (StringUtils.isNotBlank(filePath)) {
                FileUtil.deleteFolder(filePath);
            }
        }

        return invoice;
    }

    public synchronized List<JSONObject> toThreadPoolOcr(JSONArray invoiceList, String scanType, String aceId,
        String aceKey, String taxno) throws Exception {
        final List<JSONObject> jsonList = new ArrayList<>();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(invoiceList.size()), r -> new Thread(r, "发票获取接口-" + r.hashCode()));
        for (int i = 0; i < invoiceList.size(); i++) {
            JSONObject invoice = invoiceList.getJSONObject(i);
            //加编号排序
            invoice.put("orderNum", i);
            TimeUnit.MILLISECONDS.sleep(100);
            threadPoolExecutor.execute(() -> {
                try {
                    JSONObject jsonObject = toOcrInterface(invoice, scanType, aceId, aceKey, taxno);
                    jsonList.add(jsonObject);
                } catch (Exception e) {
                   log.info("业务处理异常：{}", e);
                }
            });
        }

        threadPoolExecutor.shutdown();
        while (true) {
            // 线程执行完毕后返回查验结果
            if (threadPoolExecutor.isTerminated()) {

                List<JSONObject> resultJsonList = jsonList.stream()
                        .sorted(Comparator.comparing(jsonObject -> Integer.parseInt(jsonObject.getString("orderNum"))))
                        .collect(Collectors.toList());

                return resultJsonList;
            }
            Thread.sleep(1000);
        }

    }

    public TDxInvoiceImg getImgFile(String scanId) {
        return imgService.getImgFile(scanId);
    }

    /**
     * 实际执行保存的方法，兼容@Transactional和DynamicContextHolder.push
     *
     * @param scanInvoiceLog
     *            日志
     * @param invoiceImgList
     *            图片
     *
     */
    @Transactional(rollbackFor = Exception.class)
    public void seveData(TDxScanInvoiceLog scanInvoiceLog, List<TDxInvoiceImg> invoiceImgList) {
        try {
            super.save(scanInvoiceLog);
            if (invoiceImgList.size() > 0) {

                int batchSize = 500;
                imgService.saveBatch(invoiceImgList, batchSize);
            }

        } catch (Exception e) {
           log.info("业务处理异常：{}", e);
        }
    }

    @Override
    public String uploadImg(MultipartFile file, String database) {
        InputStream ins = null;
        OutputStream os = null;
        String gfTaxNo = null;
        String originalFilename = file.getOriginalFilename();
        String licalPathDefault = "";
        log.info("上传图片进来了 图片:{}", originalFilename);
        try {
            String[] split = originalFilename.split("_");
            gfTaxNo = split[0];
            String scanId = originalFilename.substring(0, originalFilename.length() - 4);

            ins = file.getInputStream();
            byte[] bytes = FileCopyUtils.copyToByteArray(ins);
            // 获取扫描时间
            StringBuilder fileName = new StringBuilder();
            fileName.append(DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_PATTERN)).append("_").append(scanId)
                .append(".jpg");
            // 创建目录
            FileUtil.directoryIsExists(fileService.getLocalPathDefault());
            // 输出的文件流保存到本地文件
            licalPathDefault = fileService.getLocalPathDefault() + fileName.toString();
            os = new FileOutputStream(licalPathDefault);
            // 开始读取
            os.write(bytes);
            // 图片上传ftp
            String[] toDay = DateUtil.today().split("-");
            String pathPrefix = fileService.getPathPrefix() + gfTaxNo + "/" + toDay[0] + File.separator + toDay[1] + "/"
                + toDay[2] + "/" + fileName.toString();
            FileModel fileModel = fileService.uploadFile(pathPrefix, licalPathDefault);
            TDxInvoiceImg savePo = new TDxInvoiceImg();

            DynamicContextHolder.push(database + DbConstant.BUSINESS_WRITE);
            savePo.setScanId(scanId);
            savePo.setImagePath(fileModel.getFilePath());
            savePo.setCreateDate(DateUtil.date());
            savePo.setValid("1");
            imgService.save(savePo);

            return "success";
        } catch (Exception e) {
            log.error("InvoiceSignController.uploadImg:err " + ins, e);
           log.info("业务处理异常：{}", e);
        } finally {
            try {
                if (null != os) {
                    os.close();
                }
                if (null != ins) {
                    ins.close();
                }
            } catch (IOException e) {
               log.info("业务处理异常：{}", e);
            }
        }
        return "fail";
    }

    @Override
    public String uploadCustomsImg(MultipartFile file, String database, String pch) {
        InputStream ins = null;
        OutputStream os = null;
        String gfTaxNo = null;
        String originalFilename = file.getOriginalFilename();
        String licalPathDefault = "";
        log.info("上传缴款书图片进来了 图片:{}", originalFilename);
        try {
            String[] split = originalFilename.split("_");

            gfTaxNo = split[0];
            String scanId = originalFilename.substring(0, originalFilename.length() - 4);

            ins = file.getInputStream();
            String encodeImg = Base64.encode(ins);
            byte[] bytes = FileCopyUtils.copyToByteArray(ins);
            // 获取扫描时间
            StringBuilder fileName = new StringBuilder();
            fileName.append(DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_PATTERN)).append("_").append(scanId)
                .append(".jpg");
            // 创建目录
            FileUtil.directoryIsExists(fileService.getLocalPathDefault());
            // 输出的文件流保存到本地文件
            licalPathDefault = fileService.getLocalPathDefault() + fileName.toString();
            os = new FileOutputStream(licalPathDefault);
            // 开始读取
            os.write(bytes);
            // 图片上传ftp
            String[] toDay = DateUtil.today().split("-");
            String pathPrefix = fileService.getPathPrefix() + gfTaxNo + "/" + toDay[0] + File.separator + toDay[1] + "/"
                + toDay[2] + "/" + fileName.toString();
            FileModel fileModel = fileService.uploadFile(pathPrefix, licalPathDefault);
            TDxInvoiceImg savePo = new TDxInvoiceImg();

            SenderInfo info = new SenderInfo();
            info.setEncodeImg(encodeImg);
            info.setPch(pch);
            info.setScanId(scanId);
            String sendInfo = JSONObject.toJSONString(info);
            rabbitLogsSender.toCustomsOcr(sendInfo);

            DynamicContextHolder.push(database + DbConstant.BUSINESS_WRITE);
            savePo.setScanId(scanId);
            savePo.setImagePath(fileModel.getFilePath());
            savePo.setCreateDate(DateUtil.date());
            savePo.setValid("1");
            imgService.save(savePo);

            return "success";
        } catch (Exception e) {
            log.error("InvoiceSignController.uploadImg:err " + ins, e);
           log.info("业务处理异常：{}", e);
        } finally {
            try {
                if (null != os) {
                    os.close();
                }
                if (null != ins) {
                    ins.close();
                }
            } catch (IOException e) {
               log.info("业务处理异常：{}", e);
            }
        }
        return "fail";
    }

    @Override
    public String getScanType(String company) {
        return sysGroupConfigService.getGroupInfo(company).getOcrType();
    }
}
