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.Objects;
import java.util.Set;
import com.ruoyi.asset.constant.AssetStatus;
import com.ruoyi.asset.constant.TransferOrderStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetLocation;
import com.ruoyi.asset.domain.AmsTransferOrder;
import com.ruoyi.asset.domain.AmsTransferOrderItem;
import com.ruoyi.asset.domain.AmsWarehouse;
import com.ruoyi.asset.domain.AssetTransitionContext;
import com.ruoyi.asset.mapper.AmsAssetMapper;
import com.ruoyi.asset.mapper.AmsTransferOrderMapper;
import com.ruoyi.asset.service.IAmsAssetLocationService;
import com.ruoyi.asset.service.IAmsTransferOrderService;
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 AmsTransferOrderServiceImpl implements IAmsTransferOrderService
{
    /** 编号规则业务标识 */
    private static final String TRANSFER_ORDER_RULE = "TRANSFER_ORDER";

    private static final String ENABLED_YES = "Y";

    private static final String DEL_FLAG_NORMAL = "0";

    @Autowired
    private AmsTransferOrderMapper amsTransferOrderMapper;

    @Autowired
    private AmsAssetMapper amsAssetMapper;

    @Autowired
    private ISysCodeRuleService sysCodeRuleService;

    @Autowired
    private IAmsWarehouseService amsWarehouseService;

    @Autowired
    private IAmsAssetLocationService amsAssetLocationService;

    @Autowired
    private IAssetStatusTransitionService assetStatusTransitionService;

    @Override
    public AmsTransferOrder selectAmsTransferOrderByOrderId(Long orderId)
    {
        return amsTransferOrderMapper.selectAmsTransferOrderByOrderId(orderId);
    }

    @Override
    public List<AmsTransferOrder> selectAmsTransferOrderList(AmsTransferOrder amsTransferOrder)
    {
        return amsTransferOrderMapper.selectAmsTransferOrderList(amsTransferOrder);
    }

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

    /**
     * 新增调拨草稿：生成单号 → 锁定资产回填快照 → 保存主表+明细。
     * <p>申请人信息由Controller层从当前登录用户获取，不信任前端传值。</p>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertAmsTransferOrder(AmsTransferOrder order, Long applicantId, String applicantName)
    {
        validateOrderRequest(order);
        validateOperator(applicantId, applicantName, "申请人");
        order.setTransferNo(sysCodeRuleService.nextCode(TRANSFER_ORDER_RULE));
        order.setApplicantId(applicantId);
        order.setApplicantName(StringUtils.trim(applicantName));
        order.setOrderStatus(TransferOrderStatus.DRAFT);
        order.setConfirmUserId(null);
        order.setConfirmUserName(null);
        order.setConfirmTime(null);
        order.setDelFlag(DEL_FLAG_NORMAL);
        order.setCreateTime(DateUtils.getNowDate());
        fillOrderSnapshots(order, null);

        if (amsTransferOrderMapper.insertAmsTransferOrder(order) != 1 || StringUtils.isNull(order.getOrderId()))
        {
            throw new ServiceException("调拨单保存失败");
        }
        insertTransferOrderItems(order);
        return 1;
    }

    /**
     * 修改草稿：单据号、申请人、状态和确认信息均由服务层维护，前端不可篡改。
     * <p>先逻辑删除旧明细再重新插入，简化差异比对逻辑。</p>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateAmsTransferOrder(AmsTransferOrder order)
    {
        if (StringUtils.isNull(order) || StringUtils.isNull(order.getOrderId()))
        {
            throw new ServiceException("调拨单ID不能为空");
        }
        AmsTransferOrder current = requireOrderForUpdate(order.getOrderId(), TransferOrderStatus.DRAFT,
                "仅草稿调拨单允许修改");
        validateOrderRequest(order);

        // 单据号、申请人、状态和确认信息均由服务层维护，普通编辑不得篡改。
        order.setTransferNo(current.getTransferNo());
        order.setApplicantId(current.getApplicantId());
        order.setApplicantName(current.getApplicantName());
        order.setOrderStatus(current.getOrderStatus());
        order.setConfirmUserId(current.getConfirmUserId());
        order.setConfirmUserName(current.getConfirmUserName());
        order.setConfirmTime(current.getConfirmTime());
        order.setUpdateTime(DateUtils.getNowDate());
        fillOrderSnapshots(order, order.getOrderId());

        amsTransferOrderMapper.deleteAmsTransferOrderItemByOrderId(order.getOrderId());
        insertTransferOrderItems(order);
        if (amsTransferOrderMapper.updateAmsTransferOrder(order) != 1)
        {
            throw new ServiceException("调拨单状态已变化，保存失败");
        }
        return 1;
    }

    /**
     * 草稿 → 待确认：提交前重新校验资产当前归属是否与草稿保存时一致。
     * <p>如果草稿期间资产归属已被其他业务修改，阻断提交避免覆盖。</p>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int submitTransfer(Long orderId, String operateLoginName)
    {
        AmsTransferOrder order = requireOrderForUpdate(orderId, TransferOrderStatus.DRAFT,
                "仅草稿调拨单允许提交");
        validateLoginName(operateLoginName);
        validateOrderReadyForFlow(order);

        order.setOrderStatus(TransferOrderStatus.PENDING_CONFIRM);
        order.setUpdateBy(StringUtils.trim(operateLoginName));
        order.setUpdateTime(DateUtils.getNowDate());
        if (amsTransferOrderMapper.submitAmsTransferOrder(order) != 1)
        {
            throw new ServiceException("调拨单状态已变化，提交失败");
        }
        return 1;
    }

    /**
     * 待确认 → 已调拨：逐条委托公共流转服务更新资产当前归属并写入履历。
     * <p>确认前同样校验归属未变化，确保从提交到确认期间无其他业务干扰。</p>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int confirmTransfer(Long orderId, Long operateUserId, String operateUserName, String operateLoginName)
    {
        AmsTransferOrder order = requireOrderForUpdate(orderId, TransferOrderStatus.PENDING_CONFIRM,
                "仅待确认调拨单允许确认");
        validateOperator(operateUserId, operateUserName, "确认人");
        validateLoginName(operateLoginName);
        validateOrderReadyForFlow(order);

        List<AmsTransferOrderItem> sortedItems = sortedItems(order);
        for (AmsTransferOrderItem item : sortedItems)
        {
            AssetTransitionContext context = new AssetTransitionContext();
            context.setSourceOrderId(order.getOrderId());
            context.setSourceOrderNo(order.getTransferNo());
            context.setSourceItemId(item.getItemId());
            context.setOperateUserId(operateUserId);
            context.setOperateUserName(StringUtils.trim(operateUserName));
            context.setOperateLoginName(StringUtils.trim(operateLoginName));
            context.setChangeSummary("确认资产调拨");
            context.setRemark(order.getRemark());
            assetStatusTransitionService.confirmTransfer(item.getAssetId(), item.getNewWarehouseId(),
                    item.getNewLocationId(), context);
        }

        Date now = DateUtils.getNowDate();
        order.setOrderStatus(TransferOrderStatus.TRANSFERRED);
        order.setConfirmUserId(operateUserId);
        order.setConfirmUserName(StringUtils.trim(operateUserName));
        order.setConfirmTime(now);
        order.setUpdateBy(StringUtils.trim(operateLoginName));
        order.setUpdateTime(now);
        if (amsTransferOrderMapper.confirmAmsTransferOrder(order) != 1)
        {
            throw new ServiceException("调拨单状态已变化，确认调拨失败");
        }
        return 1;
    }

    /** 批量删除：逐条加行锁校验草稿状态后删除，按ID升序避免死锁。 */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteAmsTransferOrderByOrderIds(String orderIds)
    {
        Long[] sortedIds = Arrays.stream(Convert.toStrArray(orderIds))
                .map(Long::valueOf).sorted().toArray(Long[]::new);
        int rows = 0;
        for (Long orderId : sortedIds)
        {
            AmsTransferOrder order = amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(orderId);
            if (StringUtils.isNull(order))
            {
                continue;
            }
            requireDraft(order);
            amsTransferOrderMapper.deleteAmsTransferOrderItemByOrderId(orderId);
            rows += amsTransferOrderMapper.deleteAmsTransferOrderByOrderId(orderId);
        }
        return rows;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteAmsTransferOrderByOrderId(Long orderId)
    {
        AmsTransferOrder order = amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(orderId);
        if (StringUtils.isNull(order))
        {
            return 0;
        }
        requireDraft(order);
        amsTransferOrderMapper.deleteAmsTransferOrderItemByOrderId(orderId);
        return amsTransferOrderMapper.deleteAmsTransferOrderByOrderId(orderId);
    }

    private void validateOrderRequest(AmsTransferOrder order)
    {
        if (StringUtils.isNull(order))
        {
            throw new ServiceException("调拨单不能为空");
        }
        if (order.getAmsTransferOrderItemList() == null || order.getAmsTransferOrderItemList().isEmpty())
        {
            throw new ServiceException("调拨单明细不能为空");
        }
        validateLength(order.getTransferReason(), 500, "调拨原因");
        validateLength(order.getRemark(), 500, "备注");
    }

    /**
     * 保存草稿时按资产ID升序锁定资产，并从当前台账和主数据回填调拨前后快照。
     */
    private void fillOrderSnapshots(AmsTransferOrder order, Long currentOrderId)
    {
        Set<Long> assetIds = new HashSet<>();
        List<AmsTransferOrderItem> sortedItems = sortedItems(order);
        order.setAmsTransferOrderItemList(sortedItems);
        for (AmsTransferOrderItem item : sortedItems)
        {
            AmsAsset asset = lockTransferableAsset(item.getAssetId());
            if (!assetIds.add(asset.getAssetId()))
            {
                throw new ServiceException("同一调拨单不能重复选择资产");
            }
            validateNotOccupied(asset, currentOrderId);
            fillAssetSnapshots(item, asset);
            fillTargetSnapshots(item, asset);
            validateActualChange(item, asset);
            item.setTransferNo(order.getTransferNo());
            item.setDelFlag(DEL_FLAG_NORMAL);
            validateLength(item.getRemark(), 500, "明细备注");
        }
    }

    /**
     * 提交和确认前必须保证资产仍处于草稿保存时的原归属，避免陈旧单据覆盖其他业务变更。
     */
    private void validateOrderReadyForFlow(AmsTransferOrder order)
    {
        List<AmsTransferOrderItem> items = sortedItems(order);
        if (items.isEmpty())
        {
            throw new ServiceException("调拨单明细不能为空");
        }
        Set<Long> assetIds = new HashSet<>();
        for (AmsTransferOrderItem item : items)
        {
            AmsAsset asset = lockTransferableAsset(item.getAssetId());
            if (!assetIds.add(asset.getAssetId()))
            {
                throw new ServiceException("同一调拨单不能重复选择资产");
            }
            validateNotOccupied(asset, order.getOrderId());
            validateOldSnapshot(item, asset);
            fillTargetSnapshots(item, asset);
            validateActualChange(item, asset);
        }
    }

    /** 加行锁并校验资产为可调拨的在库状态，同时校验仓位和使用归属完整性。 */
    private AmsAsset lockTransferableAsset(Long assetId)
    {
        if (StringUtils.isNull(assetId))
        {
            throw new ServiceException("调拨资产不能为空");
        }
        AmsAsset asset = amsAssetMapper.selectAmsAssetByAssetIdForUpdate(assetId);
        if (StringUtils.isNull(asset))
        {
            throw new ServiceException("调拨资产不存在或已删除");
        }
        if (!StringUtils.equals(asset.getAssetStatus(), AssetStatus.IN_STOCK))
        {
            throw new ServiceException(StringUtils.format("资产【{}】当前状态不允许调拨", asset.getAssetCode()));
        }
        if (StringUtils.isNull(asset.getWarehouseId()) || StringUtils.isNull(asset.getLocationId()))
        {
            throw new ServiceException(StringUtils.format("资产【{}】缺少当前仓库或位置", asset.getAssetCode()));
        }
        if (StringUtils.isNotNull(asset.getUseDeptId()) || StringUtils.isNotNull(asset.getUseUserId())
                || StringUtils.isNotEmpty(asset.getUseDeptName()) || StringUtils.isNotEmpty(asset.getUseUserName()))
        {
            throw new ServiceException(StringUtils.format("在库资产【{}】仍存在使用归属，请先修正后再调拨", asset.getAssetCode()));
        }
        return asset;
    }

    /** 校验资产未被其他有效调拨单（草稿+待确认）占用 */
    private void validateNotOccupied(AmsAsset asset, Long currentOrderId)
    {
        if (amsTransferOrderMapper.countOtherActiveTransferOrderByAssetId(asset.getAssetId(), currentOrderId,
                TransferOrderStatus.DRAFT, TransferOrderStatus.PENDING_CONFIRM) > 0)
        {
            throw new ServiceException(StringUtils.format("资产【{}】已被其他有效调拨单占用", asset.getAssetCode()));
        }
    }

    /** 从资产台账回填「调拨前」的全部归属快照，确保明细记录完整的变更历史。 */
    private void fillAssetSnapshots(AmsTransferOrderItem item, AmsAsset asset)
    {
        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.setAssetStatus(asset.getAssetStatus());
        item.setOldDeptId(null);
        item.setOldDeptName(null);
        item.setOldUserId(null);
        item.setOldUserName(null);
        item.setOldWarehouseId(asset.getWarehouseId());
        item.setOldWarehouseCode(asset.getWarehouseCode());
        item.setOldWarehouseName(asset.getWarehouseName());
        item.setOldLocationId(asset.getLocationId());
        item.setOldLocationCode(asset.getLocationCode());
        item.setOldLocationName(asset.getLocationName());
    }

    /**
     * 校验并回填「调拨后」的目标归属快照。
     * <p>在库资产仅需仓库+位置，在用资产还需部门+使用人且校验使用人归属。
     * 在库资产即使前端传了部门或使用人也强制清空，防止伪造。</p>
     */
    private void fillTargetSnapshots(AmsTransferOrderItem item, AmsAsset asset)
    {
        AmsWarehouse warehouse = requireEnabledWarehouse(item.getNewWarehouseId());
        AmsAssetLocation location = requireEnabledLocation(item.getNewLocationId());
        if (!warehouse.getWarehouseId().equals(location.getWarehouseId()))
        {
            throw new ServiceException(StringUtils.format("资产【{}】的新位置不属于所选新仓库", asset.getAssetCode()));
        }
        item.setNewWarehouseCode(warehouse.getWarehouseCode());
        item.setNewWarehouseName(warehouse.getWarehouseName());
        item.setNewLocationCode(location.getLocationCode());
        item.setNewLocationName(location.getLocationName());

        item.setNewDeptId(null);
        item.setNewDeptName(null);
        item.setNewUserId(null);
        item.setNewUserName(null);
    }

    /** 陈旧归属校验：若草稿保存后资产台账的归属已被其他业务修改，阻断当前流转。 */
    private void validateOldSnapshot(AmsTransferOrderItem item, AmsAsset asset)
    {
        if (!Objects.equals(item.getOldWarehouseId(), asset.getWarehouseId())
                || !Objects.equals(item.getOldLocationId(), asset.getLocationId()))
        {
            throw new ServiceException(StringUtils.format("资产【{}】当前归属已变化，请重新编辑并提交调拨单",
                    asset.getAssetCode()));
        }
    }

    /** 调拨前后归属不能完全相同，否则调拨没有实际意义。 */
    private void validateActualChange(AmsTransferOrderItem item, AmsAsset asset)
    {
        boolean changed = !Objects.equals(asset.getWarehouseId(), item.getNewWarehouseId())
                || !Objects.equals(asset.getLocationId(), item.getNewLocationId());
        if (!changed)
        {
            throw new ServiceException(StringUtils.format("资产【{}】的调拨前后归属不能完全相同", asset.getAssetCode()));
        }
    }

    private AmsWarehouse requireEnabledWarehouse(Long warehouseId)
    {
        if (StringUtils.isNull(warehouseId))
        {
            throw new ServiceException("新仓库不能为空");
        }
        AmsWarehouse warehouse = amsWarehouseService.selectAmsWarehouseByWarehouseId(warehouseId);
        if (StringUtils.isNull(warehouse) || !StringUtils.equals(ENABLED_YES, warehouse.getEnabled()))
        {
            throw new ServiceException("新仓库不存在或已停用");
        }
        return warehouse;
    }

    private AmsAssetLocation requireEnabledLocation(Long locationId)
    {
        if (StringUtils.isNull(locationId))
        {
            throw new ServiceException("新位置不能为空");
        }
        AmsAssetLocation location = amsAssetLocationService.selectAmsAssetLocationByLocationId(locationId);
        if (StringUtils.isNull(location) || !StringUtils.equals(ENABLED_YES, location.getEnabled()))
        {
            throw new ServiceException("新位置不存在或已停用");
        }
        return location;
    }

    private AmsTransferOrder requireOrderForUpdate(Long orderId, String requiredStatus, String message)
    {
        if (StringUtils.isNull(orderId))
        {
            throw new ServiceException("调拨单ID不能为空");
        }
        AmsTransferOrder order = amsTransferOrderMapper.selectAmsTransferOrderByOrderIdForUpdate(orderId);
        if (StringUtils.isNull(order))
        {
            throw new ServiceException("调拨单不存在或已删除");
        }
        if (!StringUtils.equals(requiredStatus, order.getOrderStatus()))
        {
            throw new ServiceException(message);
        }
        return order;
    }

    private void requireDraft(AmsTransferOrder order)
    {
        if (!StringUtils.equals(TransferOrderStatus.DRAFT, order.getOrderStatus()))
        {
            throw new ServiceException("仅草稿调拨单允许删除");
        }
    }

    /** 按 assetId 升序排列明细行，确保与其他业务使用相同锁序消除死锁。 */
    private List<AmsTransferOrderItem> sortedItems(AmsTransferOrder order)
    {
        List<AmsTransferOrderItem> items = order.getAmsTransferOrderItemList();
        if (items == null)
        {
            return new ArrayList<>();
        }
        List<AmsTransferOrderItem> sortedItems = new ArrayList<>(items);
        sortedItems.sort(Comparator.comparing(AmsTransferOrderItem::getAssetId,
                Comparator.nullsFirst(Long::compareTo)));
        return sortedItems;
    }

    private void insertTransferOrderItems(AmsTransferOrder order)
    {
        List<AmsTransferOrderItem> list = new ArrayList<>();
        Date now = DateUtils.getNowDate();
        for (AmsTransferOrderItem item : order.getAmsTransferOrderItemList())
        {
            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 (amsTransferOrderMapper.batchAmsTransferOrderItem(list) != list.size())
        {
            throw new ServiceException("调拨单明细保存失败");
        }
    }

    private void validateOperator(Long userId, String userName, String fieldName)
    {
        if (StringUtils.isNull(userId) || StringUtils.isEmpty(StringUtils.trim(userName)))
        {
            throw new ServiceException(fieldName + "ID和名称不能为空");
        }
        validateLength(StringUtils.trim(userName), 100, fieldName + "名称");
    }

    private void validateLoginName(String loginName)
    {
        if (StringUtils.isEmpty(StringUtils.trim(loginName)))
        {
            throw new ServiceException("操作登录账号不能为空");
        }
        validateLength(StringUtils.trim(loginName), 64, "操作登录账号");
    }

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