package com.zg.project.wisdom.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.zg.common.exception.ServiceException;
import com.zg.common.utils.DateUtils;
import com.zg.common.utils.SecurityUtils;
import com.zg.common.utils.StringUtils;
import com.zg.project.information.domain.PcdeDetail;
import com.zg.project.information.mapper.PcdeDetailMapper;
import com.zg.project.wisdom.domain.GysJh;
import com.zg.project.wisdom.domain.RkInfo;
import com.zg.project.wisdom.domain.RkRecord;
import com.zg.project.wisdom.domain.dto.BorrowReturnDTO;
import com.zg.project.wisdom.domain.dto.BorrowReturnItemDTO;
import com.zg.project.wisdom.domain.dto.RkBillCreateDTO;
import com.zg.project.wisdom.mapper.GysJhMapper;
import com.zg.project.wisdom.mapper.RkInfoMapper;
import com.zg.project.wisdom.mapper.RkRecordMapper;
import com.zg.project.wisdom.service.IRkBillService;
import com.zg.project.wisdom.utils.BillNoUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zg.project.wisdom.mapper.RkBillMapper;
import com.zg.project.wisdom.domain.RkBill;
import org.springframework.transaction.annotation.Transactional;

import static com.zg.common.utils.SecurityUtils.getUserId;

/**
 * 库存单据Service业务层处理
 * 
 * @author zg
 * @date 2026-01-20
 */
@Service
public class RkBillServiceImpl implements IRkBillService
{
    @Autowired
    private RkBillMapper rkBillMapper;

    @Autowired
    private RkInfoMapper rkInfoMapper;

    @Autowired
    private RkRecordMapper rkRecordMapper;

    @Autowired
    private GysJhMapper gysJhMapper;

    @Autowired
    private PcdeDetailMapper pcdeDetailMapper;

    /**
     * 查询库存单据
     * 
     * @param id 库存单据主键
     * @return 库存单据
     */
    @Override
    public RkBill selectRkBillById(Long id)
    {
        return rkBillMapper.selectRkBillById(id);
    }

    /**
     * 查询库存单据列表
     * 
     * @param rkBill 库存单据
     * @return 库存单据
     */
    @Override
    public List<RkBill> selectRkBillList(RkBill rkBill)
    {
        return rkBillMapper.selectRkBillList(rkBill);
    }

    /**
     * 新增库存单据
     * 
     * @param rkBill 库存单据
     * @return 结果
     */
    @Override
    public int insertRkBill(RkBill rkBill)
    {
        return rkBillMapper.insertRkBill(rkBill);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertRkBillAndDetail(RkBillCreateDTO dto) {

        if (dto == null || dto.getRkInfoList() == null || dto.getRkInfoList().isEmpty()) {
            throw new RuntimeException("入库明细不能为空");
        }

        String billNo = BillNoUtil.generateTodayBillNo("RK", null);
        Date now = DateUtils.getNowDate();
        String userId = String.valueOf(SecurityUtils.getUserId());

        List<RkInfo> rkInfoList = dto.getRkInfoList();

        /* ================== 0️⃣ 入库前：供应计划【批量】校验 ================== */
        checkGysJhQtyBeforeInStockBatch(rkInfoList);

        /* ================== 1️⃣ 主单 rk_bill ================== */
        RkBill bill = new RkBill();
        if (dto.getRkBill() != null) {
            BeanUtils.copyProperties(dto.getRkBill(), bill);
        }

        bill.setBillNo(billNo);
        bill.setBizType("0"); // 入库
        bill.setExecStatus(bill.getExecStatus());
        bill.setOperationTime(dto.getRkBill().getOperationTime());
        bill.setCreateTime(now);
        bill.setCreateBy(userId);
        bill.setIsDelete("0");

        if (bill.getOperator() == null) {
            bill.setOperator(Integer.valueOf(userId));
        }

        rkBillMapper.insertRkBill(bill);

        /* ================== 2️⃣ 明细 & 事件 ================== */
        for (RkInfo info : rkInfoList) {

            info.setExecStatus(bill.getExecStatus());

            // 库位编码
            if (StringUtils.isNotBlank(info.getPcode())) {
                PcdeDetail pcde = pcdeDetailMapper.selectByPcode(info.getPcode());
                if (pcde == null) {
                    throw new RuntimeException("库位不存在：" + info.getPcode());
                }
                info.setPcodeId(pcde.getEncodedId());
            }

            // rk_info
            info.setBillNo(billNo);
            info.setBizType(bill.getBizType());
            info.setOperationType(bill.getOperationType());
            info.setOperationTime(bill.getOperationTime());
            info.setOperator(bill.getOperator());
            info.setWlType(bill.getWlType());
            info.setCangku(bill.getCangku());

            info.setIsChuku("0");
            info.setHasMoved("0");
            info.setIsBorrowed("0");
            info.setIsDelete("0");

            info.setCreateTime(now);
            info.setCreateBy(userId);

            rkInfoMapper.insertRkInfo(info);

            // rk_record
            RkRecord record = buildInRkRecord(bill, info, now);
            record.setExecStatus(bill.getExecStatus());
            record.setPcodeId(info.getPcodeId());

            rkRecordMapper.insertRkRecord(record);
        }

        /* ================== 3️⃣ 入库后：供应计划【批量】更新 ================== */
        handleGysJhAfterInStockBatch(rkInfoList);

        return 1;
    }

    /**
     * 入库前校验（按供应计划ID聚合）
     * 规则：已入库数量 + 本次单据入库合计 ≤ 计划数量
     */
    private void checkGysJhQtyBeforeInStockBatch(List<RkInfo> rkInfoList) {

        Map<Long, BigDecimal> qtyMap = rkInfoList.stream()
                .filter(i -> i.getGysJhId() != null && i.getRealQty() != null)
                .collect(Collectors.groupingBy(
                        RkInfo::getGysJhId,
                        Collectors.mapping(
                                RkInfo::getRealQty,
                                Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)
                        )
                ));

        for (Map.Entry<Long, BigDecimal> entry : qtyMap.entrySet()) {

            Long gysJhId = entry.getKey();
            BigDecimal currentInQty = entry.getValue();

            GysJh gysJh = gysJhMapper.selectGysJhById(gysJhId);
            if (gysJh == null) {
                throw new RuntimeException("供应计划不存在，ID：" + gysJhId);
            }

            BigDecimal planQty = gysJh.getJhQty();
            if (planQty == null) {
                continue;
            }

            BigDecimal alreadyInQty = gysJh.getRealQty();
            if (alreadyInQty == null) {
                alreadyInQty = BigDecimal.ZERO;
            }

            BigDecimal afterQty = alreadyInQty.add(currentInQty);

            if (afterQty.compareTo(planQty) > 0) {
                throw new RuntimeException(
                        "入库数量超出供应计划数量，物料号：" + gysJh.getWlNo()
                                + "，计划数量：" + planQty
                                + "，已入库：" + alreadyInQty
                                + "，本次入库：" + currentInQty
                );
            }
        }
    }
    private RkRecord buildInRkRecord(RkBill bill, RkInfo info, Date now) {

        RkRecord record = new RkRecord();

        // ① 明细 → 事件快照（包含 remark）
        BeanUtils.copyProperties(info, record);
        record.setId(null);

        // ② 主单 → 事件上下文（不包含 remark）
        record.setBillNo(bill.getBillNo());
        record.setBizType(bill.getBizType());
        record.setOperationType(bill.getOperationType());
        record.setOperationTime(bill.getOperationTime());
        record.setOperator(bill.getOperator());
        record.setWlType(bill.getWlType());
        record.setCangku(bill.getCangku());

        // ③ 状态字段
        record.setIsChuku("0");
        record.setHasMoved("0");
        record.setIsBorrowed("0");
        record.setIsDelete("0");

        // ④ 审计字段
        record.setCreateTime(now);
        record.setCreateBy(bill.getCreateBy());

        // ⑤ 关联明细
        record.setRkInfoId(info.getId());
        record.setRdid(info.getId());

        return record;
    }
    /**
     * 入库完成后：按供应计划ID汇总本次入库数量，统一更新 real_qty 与 status
     */
    private void handleGysJhAfterInStockBatch(List<RkInfo> rkInfoList) {

        // 1️⃣ 按 gysJhId 分组并汇总 realQty
        Map<Long, BigDecimal> qtyMap = rkInfoList.stream()
                .filter(i -> i.getGysJhId() != null && i.getRealQty() != null)
                .collect(Collectors.groupingBy(
                        RkInfo::getGysJhId,
                        Collectors.mapping(
                                RkInfo::getRealQty,
                                Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)
                        )
                ));

        // 2️⃣ 逐个供应计划更新
        for (Map.Entry<Long, BigDecimal> entry : qtyMap.entrySet()) {

            Long gysJhId = entry.getKey();
            BigDecimal totalInQty = entry.getValue();

            // 累加入库数量
            gysJhMapper.increaseRealQtyById(totalInQty, gysJhId);

            // 查询最新状态
            GysJh gysJh = gysJhMapper.selectGysJhById(gysJhId);
            if (gysJh == null) {
                throw new RuntimeException("供应计划不存在，ID：" + gysJhId);
            }

            BigDecimal planQty = gysJh.getJhQty();
            BigDecimal realQty = gysJh.getRealQty();
            if (realQty == null) {
                realQty = BigDecimal.ZERO;
            }

            String status;
            if (realQty.compareTo(BigDecimal.ZERO) == 0) {
                status = "0"; // 未到货
            } else if (planQty != null && realQty.compareTo(planQty) >= 0) {
                status = "1"; // 已入库
            } else {
                status = "2"; // 部分入库
            }

            gysJhMapper.updateStatusById(gysJhId, status);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int appendRkBillDetail(RkBillCreateDTO dto) {

        if (dto == null || dto.getRkBill() == null
                || StringUtils.isBlank(dto.getRkBill().getBillNo())) {
            throw new ServiceException("单据号不能为空");
        }

        if (dto.getRkInfoList() == null || dto.getRkInfoList().isEmpty()) {
            throw new ServiceException("入库明细不能为空");
        }

        String billNo = dto.getRkBill().getBillNo();
        Date now = DateUtils.getNowDate();

        /* ================== 1️⃣ 查询主单 ================== */
        RkBill bill = rkBillMapper.selectByBillNo(billNo);
        if (bill == null) {
            throw new ServiceException("单据不存在：" + billNo);
        }

        /* ================== 2️⃣ 执行状态规则 ================== */
        String execStatus = dto.getRkBill().getExecStatus();
        if (StringUtils.isBlank(execStatus)) {
            execStatus = bill.getExecStatus();
        }

        // 🚩 标记：本次是否包含预入库
        boolean hasPreIn = false;

        List<RkInfo> rkInfoList = dto.getRkInfoList();

        /* ================== 3️⃣ 追加前：供应计划【批量】校验 ================== */
        checkGysJhQtyBeforeInStockBatch(rkInfoList);

        /* ================== 4️⃣ 插入明细 & 事件 ================== */
        for (RkInfo info : rkInfoList) {

            /* ===== 4.1 库位校验 ===== */
            if (StringUtils.isNotBlank(info.getPcode())) {
                PcdeDetail pcde = pcdeDetailMapper.selectByPcode(info.getPcode());
                if (pcde == null) {
                    throw new ServiceException("库位不存在：" + info.getPcode());
                }
                info.setPcodeId(pcde.getEncodedId());
            }

            /* ===== 4.2 继承主单字段 ===== */
            info.setBillNo(bill.getBillNo());
            info.setOperationType(bill.getOperationType());
            info.setBizType(bill.getBizType());
            info.setWlType(bill.getWlType());
            info.setCangku(bill.getCangku());

            info.setOperationTime(now);
            info.setOperator(bill.getOperator());
            info.setExecStatus(execStatus);

            if ("0".equals(execStatus)) {
                hasPreIn = true;
            }

            info.setIsChuku("0");
            info.setHasMoved("0");
            info.setIsBorrowed("0");

            /* ===== 4.3 备注兜底 ===== */
            String finalRemark = StringUtils.isNotBlank(info.getRemark())
                    ? info.getRemark()
                    : bill.getRemark();
            info.setRemark(finalRemark);

            /* ===== 4.4 审计字段 ===== */
            info.setCreateTime(now);
            info.setCreateBy(bill.getCreateBy());
            info.setIsDelete("0");

            /* ===== 4.5 插入 rk_info ===== */
            rkInfoMapper.insertRkInfo(info);

            /* ===== 4.6 插入 rk_record ===== */
            RkRecord record = buildInRkRecord(bill, info, now);
            record.setExecStatus(execStatus);
            record.setRkInfoId(info.getId());
            record.setPcodeId(info.getPcodeId());
            record.setRemark(finalRemark);

            rkRecordMapper.insertRkRecord(record);
        }

        /* ================== 5️⃣ 追加后：供应计划【批量】更新 ================== */
        handleGysJhAfterInStockBatch(rkInfoList);

        /* ================== 6️⃣ 同步回退主单状态 ================== */
        if (hasPreIn && !"0".equals(bill.getExecStatus())) {
            rkBillMapper.updateExecStatusByBillNo(billNo, "0");
        }

        return 1;
    }

    /**
     * 修改库存单据
     * 
     * @param rkBill 库存单据
     * @return 结果
     */
    @Override
    public int updateRkBill(RkBill rkBill)
    {
        return rkBillMapper.updateRkBill(rkBill);
    }

    /**
     * 批量删除库存单据
     * 
     * @param ids 需要删除的库存单据主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteRkBillByIds(Long[] ids) {

        if (ids == null || ids.length == 0) {
            return 0;
        }

        int rows = 0;

        for (Long id : ids) {

            // 1. 查询单据
            RkBill bill = rkBillMapper.selectRkBillById(id);
            if (bill == null) {
                continue;
            }

            String billNo = bill.getBillNo();
            if (billNo == null || billNo.isEmpty()) {
                continue;
            }

            // 2. 校验 rk_record 是否存在
            int recordCount = rkRecordMapper.countByBillNo(billNo);
            if (recordCount > 0) {
                throw new ServiceException(
                        "单据号【" + billNo + "】仍存在出入库记录，不允许删除"
                );
            }

            // 3. 校验 rk_info 是否存在
            int infoCount = rkInfoMapper.countByBillNo(billNo);
            if (infoCount > 0) {
                throw new ServiceException(
                        "单据号【" + billNo + "】仍存在库存明细，不允许删除"
                );
            }

            // 4. 真正删除 rk_bill
            rows += rkBillMapper.deleteRkBillById(id);
        }

        return rows;
    }
    /**
     * 删除库存单据信息
     * 
     * @param id 库存单据主键
     * @return 结果
     */
    @Override
    public int deleteRkBillById(Long id)
    {
        return rkBillMapper.deleteRkBillById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertOutBillAndDetail(RkBillCreateDTO dto) {

        if (dto == null || dto.getRkInfoList() == null || dto.getRkInfoList().isEmpty()) {
            throw new RuntimeException("出库明细不能为空");
        }

        String billNo = BillNoUtil.generateTodayBillNo("CK", null);
        Date now = DateUtils.getNowDate();
        String userId = String.valueOf(SecurityUtils.getUserId());

        // ================== 1. 主单 rk_bill ==================
        RkBill bill = new RkBill();
        if (dto.getRkBill() != null) {
            BeanUtils.copyProperties(dto.getRkBill(), bill);
        }

        if (StringUtils.isBlank(bill.getBizType())) {
            throw new RuntimeException("bizType 不能为空");
        }

        bill.setBillNo(billNo);
        bill.setOperationTime(now);
        bill.setCreateTime(now);
        bill.setCreateBy(userId);
        bill.setIsDelete("0");

        if (bill.getOperator() == null) {
            bill.setOperator(Integer.valueOf(userId));
        }

        // exec_status：0=预出库，1=已出库
        if (StringUtils.isBlank(bill.getExecStatus())) {
            bill.setExecStatus("1");
        }

        boolean isPreOut = "0".equals(bill.getExecStatus());

        // ===== 借料出库字段 =====
        if ("2".equals(bill.getBizType())) {
            bill.setBorrowTime(bill.getBorrowTime() != null ? bill.getBorrowTime() : now);
            bill.setXmNoCk(bill.getXmNoCk());
            bill.setXmMsCk(bill.getXmMsCk());
        }

        rkBillMapper.insertRkBill(bill);

        // ================== 2. 出库处理 ==================
        for (RkInfo outInfo : dto.getRkInfoList()) {

            if (outInfo.getId() == null) {
                throw new RuntimeException("出库必须指定库存明细ID");
            }
            if (outInfo.getRealQty() == null) {
                throw new RuntimeException("出库数量不能为空");
            }

            RkInfo dbInfo = rkInfoMapper.selectRkInfoById(outInfo.getId());
            if (dbInfo == null) {
                throw new RuntimeException("库存不存在，ID：" + outInfo.getId());
            }

            BigDecimal remainQty = dbInfo.getRealQty();
            BigDecimal outQty = outInfo.getRealQty();

            if (!isPreOut && remainQty.compareTo(outQty) < 0) {
                throw new RuntimeException("出库数量不能大于库存数量，库存ID：" + dbInfo.getId());
            }

            // ===== ① 非预出库才扣库存 =====
            if (!isPreOut) {
                BigDecimal newQty = remainQty.subtract(outQty);
                rkInfoMapper.updateRealQtyById(dbInfo.getId(), newQty);

                rkInfoMapper.updateIsChukuById(
                        dbInfo.getId(),
                        newQty.compareTo(BigDecimal.ZERO) == 0 ? "1" : "0"
                );
            }

            // ===== ② 出库记录（重点：传 outInfo）=====
            RkRecord record = buildOutRkRecord(bill, dbInfo, outInfo, outQty, now);
            record.setExecStatus(isPreOut ? "0" : "1");

            rkRecordMapper.insertRkRecord(record);
        }

        return 1;
    }

    private RkRecord buildOutRkRecord(
            RkBill bill,
            RkInfo dbInfo,
            RkInfo outInfo,
            BigDecimal outQty,
            Date now) {

        RkRecord record = new RkRecord();

        // ① 库存快照
        BeanUtils.copyProperties(dbInfo, record);
        record.setId(null);

        // ② 本次出库数量
        record.setRealQty(outQty);

        // ③ 主单上下文
        record.setBillNo(bill.getBillNo());
        record.setBizType(bill.getBizType());
        record.setOperationType(bill.getOperationType());
        record.setOperationTime(bill.getOperationTime());
        record.setOperator(bill.getOperator());
        record.setCangku(bill.getCangku());
        record.setTeamCode(bill.getTeamCode());

        // ===== ★ 关键：备注来自【出库明细 outInfo】=====
        record.setRemark(outInfo.getRemark());

        // ④ 借料专属字段
        if ("2".equals(bill.getBizType())) {
            record.setIsBorrowed("1");
            record.setBorrowTime(bill.getBorrowTime());
            record.setReturnTime(bill.getReturnTime());
            record.setXmNoCk(bill.getXmNoCk());
            record.setXmMsCk(bill.getXmMsCk());
        } else {
            record.setIsBorrowed("0");
        }

        // ⑤ 状态字段
        record.setIsChuku("1");
        record.setHasMoved("0");
        record.setIsDelete("0");
        record.setExecStatus(bill.getExecStatus());

        // ⑥ 审计字段
        record.setCreateTime(now);
        record.setCreateBy(bill.getCreateBy());

        // ⑦ 关联库存
        record.setRkInfoId(dbInfo.getId());
        record.setRdid(dbInfo.getId());

        return record;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int returnBorrow(BorrowReturnDTO dto) {

        if (dto == null || dto.getItems() == null || dto.getItems().isEmpty()) {
            throw new RuntimeException("还料明细不能为空");
        }

        Date now = DateUtils.getNowDate();
        String userId = String.valueOf(SecurityUtils.getUserId());

        for (BorrowReturnItemDTO item : dto.getItems()) {

            /* ================== 1. 参数校验 ================== */
            if (item.getRecordId() == null) {
                throw new RuntimeException("借料记录ID(recordId)不能为空");
            }
            if (item.getReturnTime() == null) {
                throw new RuntimeException("归还时间不能为空");
            }

            /* ================== 2. 查询借料 record（主驱动） ================== */
            RkRecord record = rkRecordMapper.selectRkRecordById(item.getRecordId());
            if (record == null) {
                throw new RuntimeException("借料记录不存在，recordId=" + item.getRecordId());
            }

            // 必须是借料出库记录
            if (!"2".equals(record.getBizType())) {
                throw new RuntimeException("该记录不是借料出库记录，禁止还料");
            }

            // 防止重复还料
            if ("2".equals(record.getIsBorrowed())) {
                throw new RuntimeException("该借料记录已归还，禁止重复操作");
            }

            Long rkInfoId = record.getRkInfoId();
            if (rkInfoId == null) {
                throw new RuntimeException("借料记录未关联库存(rkInfoId)");
            }

            /* ================== 3. 更新 rk_info（库存闭环） ================== */
            rkInfoMapper.updateBorrowReturn(
                    rkInfoId,
                    "2",                     // is_borrowed = 已归还
                    item.getReturnTime()
            );

            /* ================== 4. 更新 rk_record（状态闭环，按主键） ================== */
            rkRecordMapper.updateBorrowReturnRecordById(
                    record.getId(),
                    "2",                     // is_borrowed = 已归还
                    item.getReturnTime(),
                    userId,
                    now
            );
        }

        return dto.getItems().size();
    }

}
