package com.ruoyi.asset.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.ruoyi.asset.constant.AssetStatus;
import com.ruoyi.asset.constant.InboundOrderStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetLocation;
import com.ruoyi.asset.domain.AmsInboundOrder;
import com.ruoyi.asset.domain.AmsInboundOrderItem;
import com.ruoyi.asset.domain.AmsWarehouse;
import com.ruoyi.asset.domain.AssetTransitionContext;
import com.ruoyi.asset.mapper.AmsAssetMapper;
import com.ruoyi.asset.mapper.AmsInboundOrderMapper;
import com.ruoyi.asset.service.IAmsAssetLocationService;
import com.ruoyi.asset.service.IAmsInboundOrderService;
import com.ruoyi.asset.service.IAmsWarehouseService;
import com.ruoyi.asset.service.IAssetStatusTransitionService;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysCodeRuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 入库管理Service业务层处理
 *
 * @author Yangk
 */
@Service
public class AmsInboundOrderServiceImpl implements IAmsInboundOrderService
{
    private static final String INBOUND_ORDER_RULE = "INBOUND_ORDER";

    private static final String ENABLED_YES = "Y";

    private static final String DEL_FLAG_NORMAL = "0";

    @Autowired
    private AmsInboundOrderMapper amsInboundOrderMapper;

    @Autowired
    private AmsAssetMapper amsAssetMapper;

    @Autowired
    private ISysCodeRuleService sysCodeRuleService;

    @Autowired
    private IAmsWarehouseService amsWarehouseService;

    @Autowired
    private IAmsAssetLocationService amsAssetLocationService;

    @Autowired
    private IAssetStatusTransitionService assetStatusTransitionService;

    @Override
    public AmsInboundOrder selectAmsInboundOrderByOrderId(Long orderId)
    {
        return amsInboundOrderMapper.selectAmsInboundOrderByOrderId(orderId);
    }

    @Override
    public List<AmsInboundOrder> selectAmsInboundOrderList(AmsInboundOrder amsInboundOrder)
    {
        return amsInboundOrderMapper.selectAmsInboundOrderList(amsInboundOrder);
    }

    /**
     * 查询可入库资产候选列表，状态和占用条件由服务层固定传入，不开放给前端控制。
     */
    @Override
    public List<AmsAsset> selectAvailableInboundAssetList(AmsAsset amsAsset, Long currentOrderId)
    {
        return amsInboundOrderMapper.selectAvailableInboundAssetList(amsAsset, currentOrderId,
                AssetStatus.PENDING_INBOUND, InboundOrderStatus.DRAFT);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertAmsInboundOrder(AmsInboundOrder amsInboundOrder)
    {
        validateOrderRequest(amsInboundOrder);
        amsInboundOrder.setInboundNo(sysCodeRuleService.nextCode(INBOUND_ORDER_RULE));
        amsInboundOrder.setOrderStatus(InboundOrderStatus.DRAFT);
        amsInboundOrder.setInboundUserId(null);
        amsInboundOrder.setInboundUserName(null);
        amsInboundOrder.setInboundTime(null);
        amsInboundOrder.setDelFlag(DEL_FLAG_NORMAL);
        amsInboundOrder.setCreateTime(DateUtils.getNowDate());
        fillOrderSnapshots(amsInboundOrder, null);

        int rows = amsInboundOrderMapper.insertAmsInboundOrder(amsInboundOrder);
        if (rows != 1 || StringUtils.isNull(amsInboundOrder.getOrderId()))
        {
            throw new ServiceException("入库单保存失败");
        }
        insertAmsInboundOrderItem(amsInboundOrder);
        return rows;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateAmsInboundOrder(AmsInboundOrder amsInboundOrder)
    {
        if (StringUtils.isNull(amsInboundOrder) || StringUtils.isNull(amsInboundOrder.getOrderId()))
        {
            throw new ServiceException("入库单ID不能为空");
        }
        AmsInboundOrder current = requireDraftOrderForUpdate(amsInboundOrder.getOrderId());
        validateOrderRequest(amsInboundOrder);

        // 单据号、状态和确认信息只能由服务层维护，防止前端篡改生命周期状态。
        amsInboundOrder.setInboundNo(current.getInboundNo());
        amsInboundOrder.setOrderStatus(current.getOrderStatus());
        amsInboundOrder.setInboundUserId(current.getInboundUserId());
        amsInboundOrder.setInboundUserName(current.getInboundUserName());
        amsInboundOrder.setInboundTime(current.getInboundTime());
        amsInboundOrder.setUpdateTime(DateUtils.getNowDate());
        fillOrderSnapshots(amsInboundOrder, amsInboundOrder.getOrderId());

        amsInboundOrderMapper.deleteAmsInboundOrderItemByOrderId(amsInboundOrder.getOrderId());
        insertAmsInboundOrderItem(amsInboundOrder);
        if (amsInboundOrderMapper.updateAmsInboundOrder(amsInboundOrder) != 1)
        {
            throw new ServiceException("入库单状态已变化，保存失败");
        }
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int confirmInbound(Long orderId, Long operateUserId, String operateUserName, String operateLoginName)
    {
        AmsInboundOrder order = requireDraftOrderForUpdate(orderId);
        List<AmsInboundOrderItem> itemList = order.getAmsInboundOrderItemList();
        if (itemList == null || itemList.isEmpty())
        {
            throw new ServiceException("入库单明细不能为空");
        }
        // 所有批量流转统一按资产ID升序执行，保证与草稿保存使用相同锁序。
        List<AmsInboundOrderItem> sortedItems = new ArrayList<>(itemList);
        sortedItems.sort(Comparator.comparing(AmsInboundOrderItem::getAssetId));
        for (AmsInboundOrderItem item : sortedItems)
        {
            AssetTransitionContext context = new AssetTransitionContext();
            context.setSourceOrderId(order.getOrderId());
            context.setSourceOrderNo(order.getInboundNo());
            context.setSourceItemId(item.getItemId());
            context.setOperateUserId(operateUserId);
            context.setOperateUserName(operateUserName);
            context.setOperateLoginName(operateLoginName);
            context.setRemark(order.getRemark());
            assetStatusTransitionService.confirmInbound(item.getAssetId(), order.getWarehouseId(),
                    item.getLocationId(), context);
        }

        order.setOrderStatus(InboundOrderStatus.INBOUND_DONE);
        order.setInboundUserId(operateUserId);
        order.setInboundUserName(operateUserName);
        Date now = DateUtils.getNowDate();
        order.setInboundTime(now);
        order.setUpdateBy(operateLoginName);
        order.setUpdateTime(now);
        if (amsInboundOrderMapper.confirmAmsInboundOrder(order) != 1)
        {
            throw new ServiceException("入库单状态已变化，确认入库失败");
        }
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteAmsInboundOrderByOrderIds(String orderIds)
    {
        Long[] sortedIds = Arrays.stream(Convert.toStrArray(orderIds))
                .map(Long::valueOf).sorted().toArray(Long[]::new);
        int rows = 0;
        for (Long orderId : sortedIds)
        {
            AmsInboundOrder order = amsInboundOrderMapper.selectAmsInboundOrderByOrderIdForUpdate(orderId);
            if (StringUtils.isNull(order))
            {
                continue;
            }
            requireDraft(order);
            amsInboundOrderMapper.deleteAmsInboundOrderItemByOrderId(orderId);
            rows += amsInboundOrderMapper.deleteAmsInboundOrderByOrderId(orderId);
        }
        return rows;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteAmsInboundOrderByOrderId(Long orderId)
    {
        AmsInboundOrder order = amsInboundOrderMapper.selectAmsInboundOrderByOrderIdForUpdate(orderId);
        if (StringUtils.isNull(order))
        {
            return 0;
        }
        requireDraft(order);
        amsInboundOrderMapper.deleteAmsInboundOrderItemByOrderId(orderId);
        return amsInboundOrderMapper.deleteAmsInboundOrderByOrderId(orderId);
    }

    private AmsInboundOrder requireDraftOrderForUpdate(Long orderId)
    {
        if (StringUtils.isNull(orderId))
        {
            throw new ServiceException("入库单ID不能为空");
        }
        AmsInboundOrder order = amsInboundOrderMapper.selectAmsInboundOrderByOrderIdForUpdate(orderId);
        if (StringUtils.isNull(order))
        {
            throw new ServiceException("入库单不存在或已删除");
        }
        requireDraft(order);
        return order;
    }

    private void requireDraft(AmsInboundOrder order)
    {
        if (!StringUtils.equals(InboundOrderStatus.DRAFT, order.getOrderStatus()))
        {
            throw new ServiceException("仅草稿入库单允许修改、删除或确认");
        }
    }

    private void validateOrderRequest(AmsInboundOrder order)
    {
        if (StringUtils.isNull(order))
        {
            throw new ServiceException("入库单不能为空");
        }
        if (StringUtils.isNull(order.getWarehouseId()))
        {
            throw new ServiceException("入库仓库不能为空");
        }
        if (order.getAmsInboundOrderItemList() == null || order.getAmsInboundOrderItemList().isEmpty())
        {
            throw new ServiceException("入库单明细不能为空");
        }
        validateLength(order.getRemark(), 500, "备注");
    }

    /**
     * 仓库、资产和位置快照全部从主数据回填，页面只提交ID，避免伪造快照。
     * <p>并发控制要点：明细按 assetId 升序排序后逐行加行锁（FOR UPDATE），
     * 与 confirmInbound 使用相同锁序，消除死锁风险。</p>
     *
     * @param order 当前入库单
     * @param currentOrderId 当前单据ID，新增时为 null，编辑时传入以排除自身的草稿占用记录
     */
    private void fillOrderSnapshots(AmsInboundOrder order, Long currentOrderId)
    {
        AmsWarehouse warehouse = amsWarehouseService.selectAmsWarehouseByWarehouseId(order.getWarehouseId());
        if (StringUtils.isNull(warehouse) || !StringUtils.equals(ENABLED_YES, warehouse.getEnabled()))
        {
            throw new ServiceException("入库仓库不存在或已停用");
        }
        order.setWarehouseCode(warehouse.getWarehouseCode());
        order.setWarehouseName(warehouse.getWarehouseName());

        Set<Long> assetIds = new HashSet<>();
        List<AmsInboundOrderItem> sortedItems = new ArrayList<>(order.getAmsInboundOrderItemList());
        sortedItems.sort(Comparator.comparing(AmsInboundOrderItem::getAssetId,
                Comparator.nullsFirst(Long::compareTo)));
        order.setAmsInboundOrderItemList(sortedItems);
        for (AmsInboundOrderItem item : sortedItems)
        {
            if (StringUtils.isNull(item.getAssetId()))
            {
                throw new ServiceException("入库资产不能为空");
            }
            if (!assetIds.add(item.getAssetId()))
            {
                throw new ServiceException("同一入库单不能重复选择资产");
            }
            // 保存草稿时锁定资产，避免两个并发草稿同时通过占用校验。
            AmsAsset asset = amsAssetMapper.selectAmsAssetByAssetIdForUpdate(item.getAssetId());
            if (StringUtils.isNull(asset))
            {
                throw new ServiceException("入库资产不存在或已删除");
            }
            if (!StringUtils.equals(AssetStatus.PENDING_INBOUND, asset.getAssetStatus()))
            {
                throw new ServiceException(StringUtils.format("资产【{}】当前状态不允许入库", asset.getAssetCode()));
            }
            if (amsInboundOrderMapper.countOtherDraftInboundOrderByAssetId(asset.getAssetId(), currentOrderId,
                    InboundOrderStatus.DRAFT) > 0)
            {
                throw new ServiceException(StringUtils.format("资产【{}】已被其他草稿入库单占用", asset.getAssetCode()));
            }
            if (StringUtils.isNull(item.getLocationId()))
            {
                throw new ServiceException(StringUtils.format("资产【{}】的入库位置不能为空", asset.getAssetCode()));
            }
            AmsAssetLocation location = amsAssetLocationService.selectAmsAssetLocationByLocationId(item.getLocationId());
            if (StringUtils.isNull(location) || !StringUtils.equals(ENABLED_YES, location.getEnabled()))
            {
                throw new ServiceException(StringUtils.format("资产【{}】的入库位置不存在或已停用", asset.getAssetCode()));
            }
            if (!order.getWarehouseId().equals(location.getWarehouseId()))
            {
                throw new ServiceException(StringUtils.format("资产【{}】的入库位置不属于所选仓库", asset.getAssetCode()));
            }
            item.setAssetCode(asset.getAssetCode());
            item.setAssetName(asset.getAssetName());
            item.setCategoryId(asset.getCategoryId());
            item.setCategoryCode(asset.getCategoryCode());
            item.setCategoryName(asset.getCategoryName());
            item.setSpecModel(asset.getSpecModel());
            item.setBrand(asset.getBrand());
            item.setLocationCode(location.getLocationCode());
            item.setLocationName(location.getLocationName());
            item.setInboundQuantity(1);
            item.setInboundNo(order.getInboundNo());
            item.setDelFlag(DEL_FLAG_NORMAL);
        }
    }

    private void insertAmsInboundOrderItem(AmsInboundOrder order)
    {
        List<AmsInboundOrderItem> list = new ArrayList<>();
        Date now = DateUtils.getNowDate();
        for (AmsInboundOrderItem item : order.getAmsInboundOrderItemList())
        {
            item.setItemId(null);
            item.setOrderId(order.getOrderId());
            item.setCreateBy(StringUtils.isNotEmpty(order.getCreateBy()) ? order.getCreateBy() : order.getUpdateBy());
            item.setCreateTime(now);
            item.setUpdateBy(null);
            item.setUpdateTime(null);
            list.add(item);
        }
        if (amsInboundOrderMapper.batchAmsInboundOrderItem(list) != list.size())
        {
            throw new ServiceException("入库单明细保存失败");
        }
    }

    private void validateLength(String value, int maxLength, String fieldName)
    {
        if (StringUtils.isNotEmpty(value) && value.length() > maxLength)
        {
            throw new ServiceException(fieldName + "长度不能超过" + maxLength + "个字符");
        }
    }
}
