package com.ruoyi.inventory.service.impl;

import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import com.ruoyi.common.config.WarehouseConfig;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.inventory.domain.*;
import com.ruoyi.inventory.domain.vo.InboundMaterialTotalVO;
import com.ruoyi.inventory.mapper.*;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;

import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.inventory.domain.vo.inboundVO.InboundTemplateVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.inventory.service.IInboundOrdersService;

/**
 * 入库单主Service业务层处理
 *
 * @author ruoyi
 * @date 2025-12-02
 */
@Service
public class InboundOrdersServiceImpl implements IInboundOrdersService
{
    @Autowired
    private InboundOrdersMapper inboundOrdersMapper;
    @Autowired
    private MaterialsMapper materialsMapper;
    @Autowired
    private OwnersMapper ownersMapper;
    @Autowired
    private WarehousesMapper warehousesMapper;
    @Autowired
    private StorageLocationsMapper storageLocationsMapper;


    private static final Logger log = LoggerFactory.getLogger(InboundOrdersServiceImpl.class);
    /**
     * 查询入库单主
     *
     * @param id 入库单主主键
     * @return 入库单主
     */
    @Override
    public InboundOrders selectInboundOrdersById(String id)
    {
        return inboundOrdersMapper.selectInboundOrdersById(id);
    }

    /**
     * 查询入库单主列表
     *
     * @param inboundOrders 入库单主
     * @return 入库单主
     */
    @Override
    public List<InboundOrders> selectInboundOrdersList(InboundOrders inboundOrders)
    {
        return inboundOrdersMapper.selectInboundOrdersList(inboundOrders);
    }

    /**
     * 新增入库单主
     *
     * @param inboundOrders 入库单主
     * @return 结果
     */
    @Transactional
    @Override
    public int insertInboundOrders(InboundOrders inboundOrders)
    {
        inboundOrders.setCreateTime(DateUtils.getNowDate());
        inboundOrders.setCreateUserCode(SystemUtils.getUserName());
        inboundOrders.setCreateBy(SystemUtils.getUserName());
        int rows = inboundOrdersMapper.insertInboundOrders(inboundOrders);
        insertInboundOrderItems(inboundOrders);
        return rows;
    }

    /**
     * 修改入库单主
     *
     * @param inboundOrders 入库单主
     * @return 结果
     */
    @Transactional
    @Override
    public int updateInboundOrders(InboundOrders inboundOrders)
    {
        inboundOrdersMapper.deleteInboundOrderItemsByOrderId(inboundOrders.getOrderId());
        inboundOrders.setUpdateTime(DateUtils.getNowDate());
        inboundOrders.setUpdateUserCode(SystemUtils.getUserName());
        inboundOrders.setUpdateBy(SystemUtils.getUserName());
        insertInboundOrderItems(inboundOrders);
        return inboundOrdersMapper.updateInboundOrders(inboundOrders);
    }

    /**
     * 批量删除入库单主
     *
     * @param ids 需要删除的入库单主主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteInboundOrdersByIds(String[] ids)
    {
        inboundOrdersMapper.deleteInboundOrderItemsByOrderIds(ids);
        return inboundOrdersMapper.deleteInboundOrdersByIds(ids);
    }

    /**
     * 删除入库单主信息
     *
     * @param id 入库单主主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteInboundOrdersById(String id)
    {
        inboundOrdersMapper.deleteInboundOrderItemsByOrderId(id);
        return inboundOrdersMapper.deleteInboundOrdersById(id);
    }

    /**
     * 新增入库单明细信息
     *
     * @param inboundOrders 入库单主对象
     */
    public void insertInboundOrderItems(InboundOrders inboundOrders)
    {
        List<InboundOrderItems> inboundOrderItemsList = inboundOrders.getInboundOrderItemsList();
        String orderId = inboundOrders.getOrderId();
        if (StringUtils.isNotNull(inboundOrderItemsList))
        {
            List<InboundOrderItems> list = new ArrayList<InboundOrderItems>();
            for (InboundOrderItems inboundOrderItems : inboundOrderItemsList)
            {
                inboundOrderItems.setCreateTime(DateUtils.getNowDate());
                inboundOrderItems.setCreateUserCode(SystemUtils.getUserName());
                inboundOrderItems.setCreateBy(SystemUtils.getUserName());

                inboundOrderItems.setId(UUID.randomUUID().toString());
                inboundOrderItems.setOrderId(orderId);
                inboundOrderItems.setInboundOrderId(inboundOrders.getId());

                list.add(inboundOrderItems);
            }
            if (list.size() > 0)
            {
                inboundOrdersMapper.batchInboundOrderItems(list);
            }
        }
    }

    /**
     * 导入入库单明细信息
     *
     * @param  inboundOrdersList,isUpdateSupport,operName 入库单数据信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public <T> String importInboundOrders(List<T> inboundOrdersList, Integer isUpdateSupport, String operName, Integer orderType) {
        if (StringUtils.isNull(inboundOrdersList) || inboundOrdersList.size() == 0) {
            throw new ServiceException("导入数据不能为空！");
        }
        // 2. 初始化统计变量
        int totalMainSuccess = 0; // 成功的主表数量
        int totalMainFailure = 0; // 失败的主表数量
        int totalItemSuccess = 0; // 成功的明细数量
        int totalItemFailure = 0; // 失败的明细数量
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        Date now = DateUtils.getNowDate();
        Long userId = SecurityUtils.getUserId();
        String operId = Optional.ofNullable(userId).map(String::valueOf).orElse("");

        // 预加载物料SAP-ID映射
        Map<String, Map<String, String>> sapAndIdMap = Optional.ofNullable(materialsMapper.selectMaterialIdAndSapMap())
                .orElse(Collections.emptyMap());
        // 预加载仓库名称-ID映射
        Map<String, String> warehouseNameIdMap = loadWarehouseNameIdMap();
        // 预加载库位名称-ID映射
        Map<String, String> storageLocationNameIdMap = loadStorageLocationNameIdMap();

        // 3. 按入库单号分组（核心：通过反射处理泛型T的orderId字段）
        Map<String, List<T>> orderGroupMap = Optional.ofNullable(inboundOrdersList)
                .orElse(Collections.emptyList()) // 空列表兜底，避免NPE
                .stream()
                .filter(Objects::nonNull) // 过滤null的VO对象
                .map(vo -> {
                    // 预处理：对入库单号和批次号去空格，统一格式（反射调用getter/setter）
                    try {
                        // 反射获取getOrderId方法
                        Method getOrderIdMethod = vo.getClass().getMethod("getOrderId");
                        String orderId = Optional.ofNullable(getOrderIdMethod.invoke(vo))
                                .map(Object::toString)
                                .map(String::trim) // 去除首尾空格
                                .orElse("");
                        // 反射获取getBatchId方法
                        Method getBatchId = vo.getClass().getMethod("getBatchId");
                        String batchId = Optional.ofNullable(getBatchId.invoke(vo))
                                .map(Object::toString)
                                .map(String::trim)
                                .orElse("");

                        // 反射调用setOrderId方法回写处理后的单号
                        Method setOrderIdMethod = vo.getClass().getMethod("setOrderId", String.class);
                        setOrderIdMethod.invoke(vo, orderId);
                        // 反射调用setBatchId方法回写处理后的批次号
                        Method setBatchIdMethod = vo.getClass().getMethod("setBatchId",String.class);
                        setBatchIdMethod.invoke(vo,batchId);
                    } catch (Exception e) {
                        throw new ServiceException("实体类缺少orderId的getter/setter方法" + e);
                    }
                    return vo;
                })
                .filter(vo -> {
                    // 反射过滤无/空白入库单号的无效行
                    try {
                        Method getOrderIdMethod = vo.getClass().getMethod("getOrderId");
                        String orderId = Optional.ofNullable(getOrderIdMethod.invoke(vo))
                                .map(Object::toString)
                                .orElse("");
                        return StringUtils.isNotBlank(orderId);
                    } catch (Exception e) {
                        throw new ServiceException("获取orderId失败" + e);
                    }
                })
                .collect(Collectors.groupingBy(
                        vo -> {
                            // 反射获取分组Key：处理后的入库单号
                            try {
                                Method getOrderIdMethod = vo.getClass().getMethod("getOrderId");
                                Method getBatchIdMethod = vo.getClass().getMethod("getBatchId");
                                return Optional.ofNullable(getOrderIdMethod.invoke(vo))
                                        .map(Object::toString)
                                        .orElse("")
                                        + "--" +
                                        Optional.ofNullable(getBatchIdMethod.invoke(vo))
                                        .map(Object::toString)
                                        .orElse("");
                            } catch (Exception e) {
                                throw new ServiceException("分组获取orderId失败" + e);
                            }
                        },
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                list -> Collections.unmodifiableList(list) // 生成不可变列表，防止后续篡改
                        )
                ));
        // 兜底：若分组结果为空，返回空的不可变Map（避免后续判空）
        orderGroupMap = Optional.ofNullable(orderGroupMap).orElse(Collections.emptyMap());

        // 4. 遍历每个入库单分组处理
        for (Map.Entry<String, List<T>> entry : orderGroupMap.entrySet()) {
            String orderKey = entry.getKey();
            String orderId = orderKey.split("--")[0];
            String batchId = orderKey.split("--")[1];
            List<T> voList = entry.getValue();
            InboundOrders mainDO = null;
            List<InboundOrderItems> itemDOList = new ArrayList<>();

            try {
                // 4.1 处理主表（每个入库单号只处理一次主表）
                T firstVO = voList.get(0); // 取第一条VO的主表信息
                // 检查入库单是否已存在
                InboundOrders query = new InboundOrders();
                query.setOrderId(orderId);
                query.setBatchId(batchId);
                List<InboundOrders> existMain = inboundOrdersMapper.selectInboundOrdersList(query);
                System.out.println(existMain);
                if (existMain != null && !existMain.isEmpty()) {
                    if (isUpdateSupport == 0) {
                        // 不支持更新，跳过该入库单
                        totalMainFailure++;
                        failureMsg.append(String.format("该批次的入库单号【%s】已存在，且不支持更新，跳过导入；\n", orderId));
                        totalItemFailure += voList.size(); // 该单的明细全部失败
                        continue;
                    }
                    // 支持更新，复用已有主表ID
                    mainDO = existMain.get(0);
                    // 反射复制VO中的主表字段到已有主表（只更新可修改的字段）
                    BeanUtils.copyProperties(firstVO, mainDO, "id", "createBy", "createTime"); // 排除不可更新字段
                    mainDO.setUpdateBy(operId);
                    mainDO.setUpdateTime(now);
                    mainDO.setUpdateUserCode(operId);
                    mainDO.setOrderTypeId(Optional.ofNullable(orderType).map(String::valueOf).orElse(""));
                    // 更新主表
                    inboundOrdersMapper.updateInboundOrders(mainDO);
                    totalMainSuccess++;
                    successMsg.append(String.format("入库单号【%s】已更新；\n", orderId));
                } else {
                    // 新增主表
                    mainDO = new InboundOrders();
                    // 反射复制主表字段（只复制主表相关字段，避免物料字段污染）
                    BeanUtils.copyProperties(firstVO, mainDO,
                            "sapNo", "materialName", "plannedQuantity", "actualQuantity",
                            "plannedPackages", "materialUnit", "materialRemark"); // 排除子表字段
                    // 填充主表必填字段
                    mainDO.setId(UUID.randomUUID().toString());
                    mainDO.setInboundDate(now);
                    mainDO.setOrderId(orderId);
                    mainDO.setCreateBy(operId);
                    mainDO.setCreateTime(now);
                    mainDO.setCreateUserCode(operId);
                    mainDO.setOrderTypeId(Optional.ofNullable(orderType).map(String::valueOf).orElse(""));
                    // 设置默认值
                    if (mainDO.getSortNo() == null) {
                        mainDO.setSortNo(0L);
                    }
                    if (mainDO.getOrderStatus() == null) {
                        mainDO.setOrderStatus(1L); // 默认草稿状态
                    }

                    // ========== 货主查询（反射获取ownerId） ==========
                    String ownerName = "";
                    try {
                        Method getOwnerIdMethod = firstVO.getClass().getMethod("getOwnerId");
                        ownerName = Optional.ofNullable(getOwnerIdMethod.invoke(firstVO))
                                .map(Object::toString)
                                .orElse("");
                    } catch (Exception e) {
                        throw new ServiceException("获取货主名称失败" + e);
                    }
                    Owners owners = new Owners();
                    owners.setOwnerName(ownerName);
                    List<Owners> olist = ownersMapper.selectOwnersList(owners);
                    if (CollectionUtils.isEmpty(olist)) {
                        // 抛业务异常，携带具体单号/条件，方便排查
                        throw new ServiceException("入库单【" + orderId + "】关联的货主不存在，请检查模板数据！");
                    }
                    mainDO.setOwnerId(olist.get(0).getId());

                    // 插入主表
                    inboundOrdersMapper.insertInboundOrders(mainDO);
                    totalMainSuccess++;
                    successMsg.append(String.format("入库单号【%s】已新增；\n", orderId));
                }

                // 4.2 处理子表明细（每条VO对应一条明细）
                for (T vo : voList) {
                    try {
                        InboundOrderItems itemDO = new InboundOrderItems();
                        // 反射复制子表字段（物料相关）
                        BeanUtils.copyProperties(vo, itemDO,
                                "orderId", "systemNo", "orderTypeId", "batchId"); // 排除主表字段
                        // 填充明细必填字段
                        itemDO.setId(UUID.randomUUID().toString());
                        itemDO.setCreateBy(operId);
                        itemDO.setCreateTime(now);
                        itemDO.setCreateUserCode(operId);
                        itemDO.setOrderId(orderId); // 关联入库单号

                        // 反射获取batchId并设置
                        try {
                            Method getBatchIdMethod = vo.getClass().getMethod("getBatchId");
                            batchId = Optional.ofNullable(getBatchIdMethod.invoke(vo))
                                    .map(Object::toString)
                                    .orElse("");
                        } catch (Exception e) {
                            throw new ServiceException("获取批号失败" + e);
                        }
                        itemDO.setBatchId(batchId);

                        itemDO.setInboundOrderId(mainDO.getId()); // 关联主表ID（核心！）
                        itemDO.setSortNo(0L);

                        // 反射获取remark2并设置
                        String remark2 = "";
                        try {
                            Method getRemark2Method = vo.getClass().getMethod("getRemark2");
                            remark2 = Optional.ofNullable(getRemark2Method.invoke(vo))
                                    .map(Object::toString)
                                    .orElse("");
                        } catch (Exception e) {
                            throw new ServiceException("获取明细备注失败" + e);
                        }
                        itemDO.setRemark(remark2);

                        // ========== 物料SAPNO校验（反射获取sapNo） ==========
                        String sapNo = "";
                        try {
                            Method getSapNoMethod = vo.getClass().getMethod("getSapNo");
                            sapNo = Optional.ofNullable(getSapNoMethod.invoke(vo))
                                    .map(Object::toString)
                                    .map(String::trim)
                                    .orElse("");
                        } catch (Exception e) {
                            throw new ServiceException("获取物料SAP号失败" + e);
                        }
                        if (StringUtils.isBlank(sapNo)) {
                            throw new ServiceException("物料SAP号为空");
                        }
                        Map<String, String> sapAndId = sapAndIdMap.get(sapNo);
                        if (CollectionUtils.isEmpty(sapAndId) || StringUtils.isBlank(sapAndId.get("id"))) {
                            throw new ServiceException("物料SAP号【" + sapNo + "】不存在");
                        }
                        itemDO.setMaterialId(sapAndId.get("id"));

                        // ========== 仓库/库位查询（反射获取） ==========
                        // 1. 仓库名称转ID
                        String warehouseName = "";
                        try {
                            Method getWarehouseIdMethod = vo.getClass().getMethod("getWarehouseId");
                            warehouseName = Optional.ofNullable(getWarehouseIdMethod.invoke(vo))
                                    .map(Object::toString)
                                    .map(String::trim)
                                    .orElse("");
                        } catch (Exception e) {
                            throw new ServiceException("获取仓库名称失败" + e);
                        }
                        String warehouseId = warehouseNameIdMap.get(warehouseName);
                        if (StringUtils.isBlank(warehouseId)) {
                            log.info("仓库【" + warehouseName + "】不存在,可能暂无仓库或为成品入库,已使用默认仓库");
                            itemDO.setWarehouseId(WarehouseConfig.DEFAULT_WAREHOUSE_ID);
                        }else{
                            itemDO.setWarehouseId(warehouseId);
                        }
                        // 2. 库位名称转ID
                        String locationName = "";
                        try {
                            Method getLocationIdMethod = vo.getClass().getMethod("getLocationId");
                            locationName = Optional.ofNullable(getLocationIdMethod.invoke(vo))
                                    .map(Object::toString)
                                    .map(String::trim)
                                    .orElse("");
                        } catch (Exception e) {
                            throw new ServiceException("获取库位名称失败" + e);
                        }
                        String locationId = storageLocationNameIdMap.get(locationName);

                        if (StringUtils.isBlank(locationId)) {
                            log.info("库位【" + locationName + "】不存在,可能暂无库位或为成品入库");
                        }else{
                            itemDO.setLocationId(locationId);
                        }
                        itemDOList.add(itemDO);
                    } catch (Exception e) {
                        // 单个明细失败：仅统计，不影响整单
                        totalItemFailure++;
                        // 反射获取sapNo用于错误提示
                        String sapNo = "";
                        try {
                            Method getSapNoMethod = vo.getClass().getMethod("getSapNo");
                            sapNo = Optional.ofNullable(getSapNoMethod.invoke(vo))
                                    .map(Object::toString)
                                    .orElse("未知");
                        } catch (Exception ex) {
                            sapNo = "未知";
                        }
                        failureMsg.append(String.format("入库单号【%s】的物料明细【%s】处理失败：%s；\n",
                                orderId, sapNo, e.getMessage()));
                        log.error("导入明细失败-入库单【{}】-SAP【{}】", orderId, sapNo, e);
                    }
                }

                // 4.3 批量插入明细
                if (!CollectionUtils.isEmpty(itemDOList)) {
                    int itemSuccess = inboundOrdersMapper.batchInboundOrderItems(itemDOList);
                    if (itemSuccess != itemDOList.size()) {
                        // 批量插入部分失败，主动回滚事务
                        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                        throw new ServiceException("明细批量插入失败，成功" + itemSuccess + "条，总" + itemDOList.size() + "条");
                    }
                    totalItemSuccess += itemSuccess;
                    successMsg.append(String.format("入库单号【%s】成功导入%d条物料明细；\n", orderId, itemSuccess));
                }

            } catch (Exception e) {
                // 单个入库单处理失败，统计错误
                totalMainFailure++;
                totalItemFailure += voList.size();
                failureMsg.append(String.format("入库单号【%s】处理失败：%s；\n", orderId, e.getMessage()));
                // 打印异常栈，方便调试
                log.error("导入入库单【{}】失败", orderId, e);
                // 整单失败回滚
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
        }

        // 5. 结果汇总
        if (totalMainFailure > 0 || totalItemFailure > 0) {
            // 有失败数据，抛出异常提示
            String finalMsg = String.format(
                    "导入结果：成功新增/更新%d个入库单，失败%d个；成功导入%d条明细，失败%d条。失败详情：%s",
                    totalMainSuccess, totalMainFailure, totalItemSuccess, totalItemFailure, failureMsg
            );
            throw new ServiceException(finalMsg);
        } else {
            // 全部成功
            return String.format(
                    "导入成功！共处理%d个入库单，成功导入%d条物料明细。",
                    totalMainSuccess, totalItemSuccess
            );
        }
    }

    // ========== 辅助方法：预加载仓库名称-ID映射 ==========
    private Map<String, String> loadWarehouseNameIdMap() {
        Warehouses query = new Warehouses();
        query.setIsUsed(1L); // 只查可用仓库
        List<Warehouses> warehouseList = warehousesMapper.selectWarehousesList(query);
        if (CollectionUtils.isEmpty(warehouseList)) {
            return Collections.emptyMap();
        }
        Map<String, String> nameIdMap = new HashMap<>();
        for (Warehouses warehouse : warehouseList) {
            String name = Optional.ofNullable(warehouse.getWarehousesName()).map(String::trim).orElse("");
            if (StringUtils.isNotBlank(name)) {
                nameIdMap.put(name, warehouse.getId());
            }
        }
        return Collections.unmodifiableMap(nameIdMap); // 不可变Map，防止篡改
    }

    // ========== 辅助方法：预加载库位名称-ID映射 ==========
    private Map<String, String> loadStorageLocationNameIdMap() {
        StorageLocations query = new StorageLocations();
        query.setIsUsed(1L); // 只查可用库位
        List<StorageLocations> locationList = storageLocationsMapper.selectStorageLocationsList(query);
        if (CollectionUtils.isEmpty(locationList)) {
            return Collections.emptyMap();
        }
        Map<String, String> nameIdMap = new HashMap<>();
        for (StorageLocations location : locationList) {
            String name = Optional.ofNullable(location.getLocationName()).map(String::trim).orElse("");
            if (StringUtils.isNotBlank(name)) {
                nameIdMap.put(name, location.getId());
            }
        }
        return Collections.unmodifiableMap(nameIdMap);
    }
    /**
     * 统计本月入库数量
     *
     * @return 结果
     */
    @Override
    public int countInboundOrders() {
        String monthParam = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
        return inboundOrdersMapper.countInboundOrders(monthParam);
    }
    /**
     * 按数量统计本月入库物料Top前10
     * @return 结果
     */
    @Override
    public List<InboundMaterialTotalVO> countInboundMaterialQuantity() {
        String monthParam = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
        return inboundOrdersMapper.countInboundMaterialQuantity(monthParam);
    }
    /**
     * 按金额统计本月入库物料Top前10
     * @return 结果
     */
    @Override
    public List<InboundMaterialTotalVO> countInboundMaterialMoney() {
        String monthParam = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
        return inboundOrdersMapper.countInboundMaterialMoney(monthParam);
    }
}
