package com.czg.service.product.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.czg.product.dto.SaleOrderDTO;
import com.czg.product.entity.ConsInfo;
import com.czg.product.param.ConsInOutStockBodyParam;
import com.czg.product.param.ConsInOutStockHeadParam;
import com.czg.sa.StpKit;
import com.czg.service.product.mapper.ConsInfoMapper;
import com.czg.service.product.mapper.MkOcrMapper;
import com.czg.utils.AliOcrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.MkOcr;
import com.czg.product.entity.MkOcrService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * ocr识别结果 服务层实现。
 *
 * @author zs
 * @since 2025-11-26
 */
@Slf4j
@Service
public class MkOcrServiceImpl extends ServiceImpl<MkOcrMapper, MkOcr> implements MkOcrService {

    @Resource
    private ConsInfoMapper consInfoMapper;

    String consOcrDetail = "你是一名票据OCR结构化专家，请从我提供的票据图片中智能提取信息并只输出JSON，不得添加解释、不补充不存在内容、不得返回空字符串、字段缺失填null、字段不可省略、数字一律用字符串，" +
            "使用以下固定JSON结构：{\"documentType\":\"\",\"orderNumber\":\"\",\"date\":\"\",\"customerName\":\"\",\"operator\":\"\"," +
            "\"items\":[{\"conName\":\"\",\"spec\":\"\",\"unitName\":\"\",\"inOutNumber\":\"\",\"purchasePrice\":\"\",\"subTotal\":\"\"}]," +
            "\"totalAmount\":\"\",\"remark\":\"\"}。字段映射规则：documentType对应单据类型/销售单/采购单/出货单；orderNumber对应单号/编号/No；" +
            "date对应日期/开单日期；customerName对应客户名称/收货单位/供应商；operator对应业务员/经办人/制单人/操作员；items.conName对应品名/名称；" +
            "items.spec对应规格/型号；items.unitName对应单位；items.inOutNumber对应数量；items.purchasePrice对应单价；items.subTotal对应金额/小计；" +
            "totalAmount对应总金额/合计金额；remark对应备注。严禁生成图片中不存在的字段内容，看不清或未出现的字段必须为null，不允许推测或补全，不得生成多余字段；" +
            "items只能根据识别到的行生成，不得虚构。必须能识别旋转、倾斜、模糊、撕裂、光照差异、列顺序混乱、无表格线等情况并尽量恢复信息。" +
            "最终输出必须是纯JSON，不得包含任何非JSON字符。";

    @Override
    public ConsInOutStockHeadParam ocrDetail(Long id) {
        MkOcr mkOcr = getOne(new QueryWrapper().eq(MkOcr::getShopId, StpKit.USER.getShopId()).eq(MkOcr::getId, id));
        if (StrUtil.isNotBlank(mkOcr.getResp())) {
            return JSONObject.parseObject(mkOcr.getResp(), ConsInOutStockHeadParam.class);
        }
        return null;
    }

    @Override
    public Integer ocr(String originalFilename, InputStream inputStream, String type) {
        Long shopId = StpKit.USER.getShopId();
        byte[] readAllBytes = null;
        try {
            readAllBytes = inputStream.readAllBytes();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String md5 = DigestUtil.md5Hex(readAllBytes);
        MkOcr ocr = getOne(new QueryWrapper().eq(MkOcr::getShopId, shopId).eq(MkOcr::getMd5, md5));
        if (ocr != null) {
            return ocr.getId();
        }
        MkOcr mkOcr = new MkOcr();
        mkOcr.setShopId(shopId);
        mkOcr.setMd5(md5);
        save(mkOcr);
        byte[] finalReadAllBytes1 = readAllBytes;
        ThreadUtil.execAsync(() -> {
            try {
                if ("cons".equals(type)) {
                    mkOcr.setResp(ocrCons(finalReadAllBytes1, originalFilename, shopId));
                } else {

                }
                mkOcr.setStatus("SUCCESS");
            } catch (Exception e) {
                mkOcr.setErr(e.getMessage());
                mkOcr.setStatus("FAILED");
                log.error("ocr失败:", e);
            } finally {
                updateById(mkOcr);
            }

        });

        return mkOcr.getId();
    }


    /**
     * 识别 入库单
     */
    private String ocrCons(byte[] finalReadAllBytes1, String originalFilename, Long shopId) {
        String infoStr = AliOcrUtil.appCall(finalReadAllBytes1, originalFilename, consOcrDetail);
        SaleOrderDTO saleOrderDTO = JSONObject.parseObject(infoStr, SaleOrderDTO.class);

        ArrayList<ConsInOutStockBodyParam> bodyList = new ArrayList<>();
        Set<String> nameList = saleOrderDTO.getItems().stream().map(SaleOrderDTO.Item::getConName).collect(Collectors.toSet());
        Map<String, ConsInfo> consInfoMap = new HashMap<>();
        if (!nameList.isEmpty()) {
            consInfoMap = consInfoMapper.selectListByQuery(new QueryWrapper().in(ConsInfo::getConName, nameList).eq(ConsInfo::getShopId, shopId))
                    .stream().collect(Collectors.toMap(ConsInfo::getConName, consInfo -> consInfo));
        }
        ArrayList<SaleOrderDTO.Item> unInCons = new ArrayList<>();
        for (SaleOrderDTO.Item item : saleOrderDTO.getItems()) {
            ConsInOutStockBodyParam bodyParam = new ConsInOutStockBodyParam()
                    .setConName(item.getConName())
                    .setPurchasePrice(new BigDecimal(item.getPurchasePrice()))
                    .setUnitName(item.getUnitName())
                    .setSubTotal(new BigDecimal(item.getSubTotal()))
                    .setInOutNumber(new BigDecimal(item.getInOutNumber()));
            ConsInfo consInfo = consInfoMap.get(item.getConName());
            if (consInfo != null) {
                unInCons.add(item);
                bodyParam.setConId(consInfo.getId().toString());
            }
            bodyList.add(bodyParam);
        }

        ConsInOutStockHeadParam headParam = new ConsInOutStockHeadParam();
        headParam.setBatchNo(saleOrderDTO.getOrderNumber())
                .setInOutDate(LocalDate.parse(saleOrderDTO.getDate()))
                .setAmountPayable(new BigDecimal(saleOrderDTO.getTotalAmount()))
                .setBodyList(bodyList)
                .setUnInCons(unInCons)
                .setOcrSaleOrder(saleOrderDTO);
        return JSON.toJSONString(headParam);
    }


}
