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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.ruoyi.asset.constant.AssetStatus;
import com.ruoyi.asset.constant.InventoryIdentifyMethod;
import com.ruoyi.asset.constant.InventoryResult;
import com.ruoyi.asset.constant.InventoryScopeType;
import com.ruoyi.asset.constant.InventorySurplusStatus;
import com.ruoyi.asset.constant.InventorySurplusType;
import com.ruoyi.asset.constant.InventoryTaskStatus;
import com.ruoyi.asset.constant.RfidBindStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetCategory;
import com.ruoyi.asset.domain.AmsAssetLocation;
import com.ruoyi.asset.domain.AmsInventoryTask;
import com.ruoyi.asset.domain.AmsInventoryTaskItem;
import com.ruoyi.asset.domain.AmsInventoryTaskSurplus;
import com.ruoyi.asset.domain.AmsRfidTag;
import com.ruoyi.asset.domain.AmsWarehouse;
import com.ruoyi.asset.mapper.AmsAssetMapper;
import com.ruoyi.asset.mapper.AmsInventoryTaskMapper;
import com.ruoyi.asset.mapper.AmsRfidTagMapper;
import com.ruoyi.asset.service.IAmsAssetCategoryService;
import com.ruoyi.asset.service.IAmsAssetLocationService;
import com.ruoyi.asset.service.IAmsInventoryTaskService;
import com.ruoyi.asset.service.IAmsWarehouseService;
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 AmsInventoryTaskServiceImpl implements IAmsInventoryTaskService
{
    /** 盘点任务编号规则 */
    private static final String INVENTORY_TASK_RULE = "INVENTORY_TASK";

    /** 启用状态 */
    private static final String ENABLED_YES = "Y";

    /** 正常数据标识 */
    private static final String DEL_FLAG_NORMAL = "0";

    /** 一期盘点只纳入实物在账资产，排除待入库和已报废。 */
    private static final String[] INVENTORY_ASSET_STATUSES = new String[] {
            AssetStatus.IN_STOCK, AssetStatus.IN_USE, AssetStatus.BORROWED, AssetStatus.REPAIRING
    };

    @Autowired
    private AmsInventoryTaskMapper amsInventoryTaskMapper;

    @Autowired
    private AmsAssetMapper amsAssetMapper;

    @Autowired
    private AmsRfidTagMapper amsRfidTagMapper;

    @Autowired
    private ISysCodeRuleService sysCodeRuleService;

    @Autowired
    private IAmsWarehouseService amsWarehouseService;

    @Autowired
    private IAmsAssetLocationService amsAssetLocationService;

    @Autowired
    private IAmsAssetCategoryService amsAssetCategoryService;

    @Override
    public AmsInventoryTask selectAmsInventoryTaskByTaskId(Long taskId)
    {
        return amsInventoryTaskMapper.selectAmsInventoryTaskByTaskId(taskId);
    }

    @Override
    public List<AmsInventoryTask> selectAmsInventoryTaskList(AmsInventoryTask amsInventoryTask)
    {
        return amsInventoryTaskMapper.selectAmsInventoryTaskList(amsInventoryTask);
    }

    /**
     * 新增盘点任务草稿：任务号、负责人、状态均由服务端生成，前端只提交盘点范围。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertAmsInventoryTask(AmsInventoryTask amsInventoryTask, Long ownerUserId, String ownerUserName)
    {
        validateTaskRequest(amsInventoryTask);
        validateOperator(ownerUserId, ownerUserName, "负责人");
        fillScopeSnapshots(amsInventoryTask);

        amsInventoryTask.setTaskNo(sysCodeRuleService.nextCode(INVENTORY_TASK_RULE));
        amsInventoryTask.setOwnerUserId(ownerUserId);
        amsInventoryTask.setOwnerUserName(StringUtils.trim(ownerUserName));
        amsInventoryTask.setTaskStatus(InventoryTaskStatus.DRAFT);
        amsInventoryTask.setIssueTime(null);
        amsInventoryTask.setFinishTime(null);
        amsInventoryTask.setDelFlag(DEL_FLAG_NORMAL);
        amsInventoryTask.setCreateTime(DateUtils.getNowDate());
        amsInventoryTask.setUpdateBy(null);
        amsInventoryTask.setUpdateTime(null);

        if (amsInventoryTaskMapper.insertAmsInventoryTask(amsInventoryTask) != 1
                || StringUtils.isNull(amsInventoryTask.getTaskId()))
        {
            throw new ServiceException("盘点任务保存失败");
        }
        return 1;
    }

    /**
     * 修改盘点任务草稿：只允许调整范围和备注，不允许前端改任务号、负责人和状态。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateAmsInventoryTask(AmsInventoryTask amsInventoryTask)
    {
        if (StringUtils.isNull(amsInventoryTask) || StringUtils.isNull(amsInventoryTask.getTaskId()))
        {
            throw new ServiceException("盘点任务ID不能为空");
        }
        AmsInventoryTask current = requireTaskForUpdate(amsInventoryTask.getTaskId());
        requireStatus(current, InventoryTaskStatus.DRAFT, "仅草稿盘点任务允许修改");

        validateTaskRequest(amsInventoryTask);
        fillScopeSnapshots(amsInventoryTask);
        amsInventoryTask.setTaskNo(current.getTaskNo());
        amsInventoryTask.setOwnerUserId(current.getOwnerUserId());
        amsInventoryTask.setOwnerUserName(current.getOwnerUserName());
        amsInventoryTask.setTaskStatus(current.getTaskStatus());
        amsInventoryTask.setIssueTime(current.getIssueTime());
        amsInventoryTask.setFinishTime(current.getFinishTime());
        amsInventoryTask.setUpdateTime(DateUtils.getNowDate());

        if (amsInventoryTaskMapper.updateAmsInventoryTask(amsInventoryTask) != 1)
        {
            throw new ServiceException("盘点任务状态已变化，保存失败");
        }
        return 1;
    }

    /**
     * 下发盘点任务：重新校验范围主数据，按真实资产台账生成盘点明细快照。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int issueTask(Long taskId, String operateLoginName)
    {
        AmsInventoryTask task = requireTaskForUpdate(taskId);
        requireStatus(task, InventoryTaskStatus.DRAFT, "仅草稿盘点任务允许下发");
        validateLoginName(operateLoginName);

        validateTaskRequest(task);
        fillScopeSnapshots(task);
        Date now = DateUtils.getNowDate();
        task.setUpdateBy(StringUtils.trim(operateLoginName));
        task.setUpdateTime(now);
        if (amsInventoryTaskMapper.updateAmsInventoryTask(task) != 1)
        {
            throw new ServiceException("盘点任务状态已变化，下发失败");
        }

        List<AmsInventoryTaskItem> snapshotList =
                amsInventoryTaskMapper.selectInventoryAssetSnapshotList(task, INVENTORY_ASSET_STATUSES);
        if (snapshotList == null || snapshotList.isEmpty())
        {
            throw new ServiceException("盘点范围内没有可盘点的在账资产");
        }

        amsInventoryTaskMapper.deleteAmsInventoryTaskItemByTaskId(task.getTaskId());
        for (AmsInventoryTaskItem item : snapshotList)
        {
            item.setTaskId(task.getTaskId());
            item.setTaskNo(task.getTaskNo());
            item.setCreateBy(StringUtils.trim(operateLoginName));
            item.setCreateTime(now);
            item.setDelFlag(DEL_FLAG_NORMAL);
        }
        if (amsInventoryTaskMapper.batchAmsInventoryTaskItem(snapshotList) != snapshotList.size())
        {
            throw new ServiceException("盘点明细快照生成失败");
        }

        task.setTaskStatus(InventoryTaskStatus.ISSUED);
        task.setIssueTime(now);
        putExpectedStatus(task, InventoryTaskStatus.DRAFT);
        if (amsInventoryTaskMapper.updateAmsInventoryTaskStatus(task) != 1)
        {
            throw new ServiceException("盘点任务状态已变化，下发失败");
        }
        return 1;
    }

    /**
     * 提交盘点结果：后台 Web 作为一期闭环入口，盘点人使用当前登录用户快照。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int submitResult(AmsInventoryTask amsInventoryTask, Long operateUserId, String operateUserName,
            String operateLoginName)
    {
        if (StringUtils.isNull(amsInventoryTask) || StringUtils.isNull(amsInventoryTask.getTaskId()))
        {
            throw new ServiceException("盘点任务ID不能为空");
        }
        AmsInventoryTask current = requireTaskForUpdate(amsInventoryTask.getTaskId());
        requireAnyStatus(current, new String[] {InventoryTaskStatus.ISSUED, InventoryTaskStatus.INVENTORYING},
                "仅已下发或盘点中的任务允许提交结果");
        validateOperator(operateUserId, operateUserName, "盘点人");
        validateLoginName(operateLoginName);

        List<AmsInventoryTaskItem> currentItems = sortedItems(current);
        if (currentItems.isEmpty())
        {
            throw new ServiceException("盘点任务没有明细，不能提交结果");
        }
        Map<Long, AmsInventoryTaskItem> submittedItemMap = submittedItemMap(amsInventoryTask);
        Date now = DateUtils.getNowDate();
        int updatedRows = 0;
        for (AmsInventoryTaskItem currentItem : currentItems)
        {
            AmsInventoryTaskItem submittedItem = submittedItemMap.get(currentItem.getItemId());
            if (StringUtils.isNull(submittedItem))
            {
                throw new ServiceException(StringUtils.format("资产【{}】缺少盘点结果", currentItem.getAssetCode()));
            }
            AmsInventoryTaskItem resultItem = buildResultItem(currentItem, submittedItem, operateUserId,
                    operateUserName, operateLoginName, now);
            updatedRows += amsInventoryTaskMapper.updateAmsInventoryTaskItemResult(resultItem);
        }
        if (updatedRows != currentItems.size())
        {
            throw new ServiceException("盘点明细结果保存失败");
        }
        validateSurplusItemsBeforeSubmit(current);

        current.setTaskStatus(InventoryTaskStatus.PENDING_RESULT_CONFIRM);
        current.setUpdateBy(StringUtils.trim(operateLoginName));
        current.setUpdateTime(now);
        putExpectedStatuses(current, InventoryTaskStatus.ISSUED, InventoryTaskStatus.INVENTORYING);
        if (amsInventoryTaskMapper.updateAmsInventoryTaskStatus(current) != 1)
        {
            throw new ServiceException("盘点任务状态已变化，提交结果失败");
        }
        return 1;
    }

    /**
     * 新增盘盈明细：盘盈是任务范围外额外盘到的实物，不能复用应盘快照行。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addSurplusItem(AmsInventoryTaskSurplus surplus, Long operateUserId, String operateUserName,
            String operateLoginName)
    {
        if (StringUtils.isNull(surplus) || StringUtils.isNull(surplus.getTaskId()))
        {
            throw new ServiceException("盘点任务ID不能为空");
        }
        AmsInventoryTask task = requireTaskForUpdate(surplus.getTaskId());
        requireAnyStatus(task, new String[] {InventoryTaskStatus.ISSUED, InventoryTaskStatus.INVENTORYING},
                "仅已下发或盘点中的任务允许新增盘盈");
        validateOperator(operateUserId, operateUserName, "盘点人");
        validateLoginName(operateLoginName);

        Date now = DateUtils.getNowDate();
        AmsInventoryTaskSurplus result = buildSurplusItem(task, surplus, operateUserId, operateUserName,
                operateLoginName, now);
        ensureSurplusNotDuplicate(result);
        if (amsInventoryTaskMapper.insertAmsInventoryTaskSurplus(result) != 1
                || StringUtils.isNull(result.getSurplusId()))
        {
            throw new ServiceException("盘盈明细保存失败");
        }
        moveTaskToInventoryingIfIssued(task, operateLoginName, now);
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteSurplusItem(Long surplusId, String operateLoginName)
    {
        if (StringUtils.isNull(surplusId))
        {
            throw new ServiceException("盘盈明细ID不能为空");
        }
        validateLoginName(operateLoginName);
        AmsInventoryTaskSurplus surplus =
                amsInventoryTaskMapper.selectAmsInventoryTaskSurplusBySurplusIdForUpdate(surplusId);
        if (StringUtils.isNull(surplus))
        {
            throw new ServiceException("盘盈明细不存在或已删除");
        }
        AmsInventoryTask task = requireTaskForUpdate(surplus.getTaskId());
        requireAnyStatus(task, new String[] {InventoryTaskStatus.ISSUED, InventoryTaskStatus.INVENTORYING},
                "仅结果提交前允许删除盘盈");
        if (amsInventoryTaskMapper.deleteAmsInventoryTaskSurplusBySurplusId(surplusId,
                StringUtils.trim(operateLoginName)) != 1)
        {
            throw new ServiceException("盘盈明细状态已变化，删除失败");
        }
        return 1;
    }

    /**
     * 确认盘点结果：盘点只确认任务事实，不更新资产台账，也不写资产履历。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int confirmResult(Long taskId, Long operateUserId, String operateUserName, String operateLoginName)
    {
        AmsInventoryTask task = requireTaskForUpdate(taskId);
        requireStatus(task, InventoryTaskStatus.PENDING_RESULT_CONFIRM, "仅待结果确认的任务允许确认");
        validateOperator(operateUserId, operateUserName, "确认人");
        validateLoginName(operateLoginName);

        List<AmsInventoryTaskItem> items = sortedItems(task);
        if (items.isEmpty())
        {
            throw new ServiceException("盘点任务没有明细，不能确认结果");
        }
        Date now = DateUtils.getNowDate();
        for (AmsInventoryTaskItem item : items)
        {
            if (!InventoryResult.isExpectedItemResult(item.getInventoryResult()))
            {
                throw new ServiceException(StringUtils.format("资产【{}】盘点结果无效", item.getAssetCode()));
            }
        }
        List<AmsInventoryTaskSurplus> surplusItems = sortedSurplusItems(task);
        for (AmsInventoryTaskSurplus surplus : surplusItems)
        {
            requireSurplusRecorded(surplus);
        }
        amsInventoryTaskMapper.confirmAmsInventoryTaskSurplusByTaskId(task.getTaskId(),
                InventorySurplusStatus.CONFIRMED, StringUtils.trim(operateLoginName));

        task.setTaskStatus(InventoryTaskStatus.INVENTORY_DONE);
        task.setFinishTime(now);
        task.setUpdateBy(StringUtils.trim(operateLoginName));
        task.setUpdateTime(now);
        putExpectedStatus(task, InventoryTaskStatus.PENDING_RESULT_CONFIRM);
        if (amsInventoryTaskMapper.updateAmsInventoryTaskStatus(task) != 1)
        {
            throw new ServiceException("盘点任务状态已变化，确认失败");
        }
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int closeTask(Long taskId, String operateLoginName)
    {
        AmsInventoryTask task = requireTaskForUpdate(taskId);
        requireNotStatus(task, new String[] {InventoryTaskStatus.INVENTORY_DONE, InventoryTaskStatus.CLOSED},
                "已完成或已关闭的盘点任务不能关闭");
        validateLoginName(operateLoginName);

        Date now = DateUtils.getNowDate();
        task.setTaskStatus(InventoryTaskStatus.CLOSED);
        task.setFinishTime(now);
        task.setUpdateBy(StringUtils.trim(operateLoginName));
        task.setUpdateTime(now);
        putExcludedStatuses(task, InventoryTaskStatus.INVENTORY_DONE, InventoryTaskStatus.CLOSED);
        if (amsInventoryTaskMapper.updateAmsInventoryTaskStatus(task) != 1)
        {
            throw new ServiceException("盘点任务状态已变化，关闭失败");
        }
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteAmsInventoryTaskByTaskIds(String taskIds)
    {
        Long[] sortedIds = Arrays.stream(Convert.toStrArray(taskIds))
                .map(Long::valueOf).sorted().toArray(Long[]::new);
        int rows = 0;
        for (Long taskId : sortedIds)
        {
            rows += deleteAmsInventoryTaskByTaskId(taskId);
        }
        return rows;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteAmsInventoryTaskByTaskId(Long taskId)
    {
        AmsInventoryTask task = requireTaskForUpdate(taskId);
        requireStatus(task, InventoryTaskStatus.DRAFT, "仅草稿盘点任务允许删除");
        amsInventoryTaskMapper.deleteAmsInventoryTaskItemByTaskId(taskId);
        amsInventoryTaskMapper.deleteAmsInventoryTaskSurplusByTaskId(taskId);
        if (amsInventoryTaskMapper.deleteAmsInventoryTaskByTaskId(taskId) != 1)
        {
            throw new ServiceException("盘点任务状态已变化，删除失败");
        }
        return 1;
    }

    private AmsInventoryTask requireTaskForUpdate(Long taskId)
    {
        if (StringUtils.isNull(taskId))
        {
            throw new ServiceException("盘点任务ID不能为空");
        }
        AmsInventoryTask task = amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(taskId);
        if (StringUtils.isNull(task))
        {
            throw new ServiceException("盘点任务不存在或已删除");
        }
        return task;
    }

    private void validateTaskRequest(AmsInventoryTask task)
    {
        if (StringUtils.isNull(task))
        {
            throw new ServiceException("盘点任务不能为空");
        }
        if (StringUtils.isEmpty(StringUtils.trim(task.getScopeType())))
        {
            task.setScopeType(InventoryScopeType.ALL);
        }
        task.setScopeType(StringUtils.trim(task.getScopeType()));
        if (!StringUtils.equalsAny(task.getScopeType(), InventoryScopeType.ALL, InventoryScopeType.WAREHOUSE,
                InventoryScopeType.LOCATION, InventoryScopeType.CATEGORY, InventoryScopeType.WAREHOUSE_CATEGORY,
                InventoryScopeType.LOCATION_CATEGORY))
        {
            throw new ServiceException("盘点范围类型不合法");
        }
        validateLength(task.getRemark(), 500, "备注");
    }

    /**
     * 按范围类型回填主数据快照，并清空不属于当前范围的字段，防止前端伪造冗余条件。
     */
    private void fillScopeSnapshots(AmsInventoryTask task)
    {
        String scopeType = task.getScopeType();
        AmsWarehouse warehouse = null;
        AmsAssetLocation location = null;
        AmsAssetCategory category = null;

        if (StringUtils.equalsAny(scopeType, InventoryScopeType.WAREHOUSE, InventoryScopeType.LOCATION,
                InventoryScopeType.WAREHOUSE_CATEGORY, InventoryScopeType.LOCATION_CATEGORY))
        {
            warehouse = requireEnabledWarehouse(task.getWarehouseId());
        }
        if (StringUtils.equalsAny(scopeType, InventoryScopeType.LOCATION, InventoryScopeType.LOCATION_CATEGORY))
        {
            location = requireEnabledLocation(task.getLocationId(), warehouse);
        }
        if (StringUtils.equalsAny(scopeType, InventoryScopeType.CATEGORY, InventoryScopeType.WAREHOUSE_CATEGORY,
                InventoryScopeType.LOCATION_CATEGORY))
        {
            category = requireEnabledCategory(task.getCategoryId());
        }

        fillWarehouseSnapshot(task, warehouse);
        fillLocationSnapshot(task, location);
        fillCategorySnapshot(task, category);
    }

    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, AmsWarehouse warehouse)
    {
        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("盘点范围位置不存在或已停用");
        }
        if (StringUtils.isNotNull(warehouse) && !Objects.equals(warehouse.getWarehouseId(), location.getWarehouseId()))
        {
            throw new ServiceException("盘点范围位置不属于所选仓库");
        }
        return location;
    }

    private AmsAssetCategory requireEnabledCategory(Long categoryId)
    {
        if (StringUtils.isNull(categoryId))
        {
            throw new ServiceException("盘点范围资产类别不能为空");
        }
        AmsAssetCategory category = amsAssetCategoryService.selectAmsAssetCategoryByCategoryId(categoryId);
        if (StringUtils.isNull(category) || !StringUtils.equals(ENABLED_YES, category.getEnabled()))
        {
            throw new ServiceException("盘点范围资产类别不存在或已停用");
        }
        return category;
    }

    private void fillWarehouseSnapshot(AmsInventoryTask task, AmsWarehouse warehouse)
    {
        task.setWarehouseId(StringUtils.isNull(warehouse) ? null : warehouse.getWarehouseId());
        task.setWarehouseCode(StringUtils.isNull(warehouse) ? null : warehouse.getWarehouseCode());
        task.setWarehouseName(StringUtils.isNull(warehouse) ? null : warehouse.getWarehouseName());
    }

    private void fillLocationSnapshot(AmsInventoryTask task, AmsAssetLocation location)
    {
        task.setLocationId(StringUtils.isNull(location) ? null : location.getLocationId());
        task.setLocationCode(StringUtils.isNull(location) ? null : location.getLocationCode());
        task.setLocationName(StringUtils.isNull(location) ? null : location.getLocationName());
    }

    private void fillCategorySnapshot(AmsInventoryTask task, AmsAssetCategory category)
    {
        task.setCategoryId(StringUtils.isNull(category) ? null : category.getCategoryId());
        task.setCategoryCode(StringUtils.isNull(category) ? null : category.getCategoryCode());
        task.setCategoryName(StringUtils.isNull(category) ? null : category.getCategoryName());
    }

    private AmsInventoryTaskItem buildResultItem(AmsInventoryTaskItem currentItem, AmsInventoryTaskItem submittedItem,
            Long operateUserId, String operateUserName, String operateLoginName, Date now)
    {
        String inventoryResult = StringUtils.trim(submittedItem.getInventoryResult());
        if (!InventoryResult.isExpectedItemResult(inventoryResult))
        {
            throw new ServiceException(StringUtils.format("资产【{}】盘点结果不合法", currentItem.getAssetCode()));
        }
        validateLength(submittedItem.getRemark(), 500, "明细备注");

        AmsInventoryTaskItem resultItem = new AmsInventoryTaskItem();
        resultItem.setItemId(currentItem.getItemId());
        resultItem.setTaskId(currentItem.getTaskId());
        resultItem.setInventoryResult(inventoryResult);
        fillInventoryLocation(resultItem, currentItem, submittedItem);
        resultItem.setInventoryUserId(operateUserId);
        resultItem.setInventoryUserName(StringUtils.trim(operateUserName));
        resultItem.setInventoryTime(now);
        resultItem.setUpdateBy(StringUtils.trim(operateLoginName));
        resultItem.setUpdateTime(now);
        resultItem.setRemark(StringUtils.trim(submittedItem.getRemark()));
        return resultItem;
    }

    private AmsInventoryTaskSurplus buildSurplusItem(AmsInventoryTask task, AmsInventoryTaskSurplus submittedSurplus,
            Long operateUserId, String operateUserName, String operateLoginName, Date now)
    {
        String identifyMethod = StringUtils.trim(submittedSurplus.getIdentifyMethod());
        if (!InventoryIdentifyMethod.isValid(identifyMethod))
        {
            throw new ServiceException("盘盈识别方式不合法");
        }
        validateLength(submittedSurplus.getRemark(), 500, "盘盈备注");

        AmsInventoryTaskSurplus surplus;
        if (StringUtils.equals(InventoryIdentifyMethod.EPC, identifyMethod))
        {
            surplus = buildEpcSurplus(task, submittedSurplus);
        }
        else if (StringUtils.equals(InventoryIdentifyMethod.ASSET_CODE, identifyMethod))
        {
            surplus = buildAssetCodeSurplus(task, submittedSurplus);
        }
        else
        {
            surplus = buildManualSurplus(submittedSurplus);
        }

        surplus.setTaskId(task.getTaskId());
        surplus.setTaskNo(task.getTaskNo());
        fillSurplusInventoryLocation(surplus, submittedSurplus);
        surplus.setInventoryUserId(operateUserId);
        surplus.setInventoryUserName(StringUtils.trim(operateUserName));
        surplus.setInventoryTime(now);
        surplus.setConfirmStatus(InventorySurplusStatus.RECORDED);
        surplus.setCreateBy(StringUtils.trim(operateLoginName));
        surplus.setCreateTime(now);
        surplus.setRemark(StringUtils.trim(submittedSurplus.getRemark()));
        surplus.setDelFlag(DEL_FLAG_NORMAL);
        return surplus;
    }

    private AmsInventoryTaskSurplus buildEpcSurplus(AmsInventoryTask task, AmsInventoryTaskSurplus submittedSurplus)
    {
        String epcCode = StringUtils.trim(submittedSurplus.getEpcCode());
        if (StringUtils.isEmpty(epcCode))
        {
            throw new ServiceException("EPC识别盘盈时EPC不能为空");
        }
        validateLength(epcCode, 128, "EPC编码");

        AmsInventoryTaskSurplus surplus = new AmsInventoryTaskSurplus();
        surplus.setIdentifyMethod(InventoryIdentifyMethod.EPC);
        surplus.setEpcCode(epcCode);
        AmsRfidTag tag = amsRfidTagMapper.selectAmsRfidTagByEpcForUpdate(epcCode);
        if (StringUtils.isNotNull(tag))
        {
            fillTagSnapshot(surplus, tag);
            if (StringUtils.equals(RfidBindStatus.BOUND, tag.getBindStatus()) && StringUtils.isNotNull(tag.getAssetId()))
            {
                AmsAsset asset = amsAssetMapper.selectAmsAssetByAssetIdForUpdate(tag.getAssetId());
                if (StringUtils.isNull(asset))
                {
                    throw new ServiceException("EPC绑定的资产不存在或已删除");
                }
                requireAssetNotInTaskItems(task.getTaskId(), asset.getAssetId(), asset.getAssetCode());
                surplus.setSurplusType(InventorySurplusType.KNOWN_OUT_OF_SCOPE);
                fillAssetSnapshot(surplus, asset);
                return surplus;
            }
        }

        surplus.setSurplusType(InventorySurplusType.UNKNOWN_OBJECT);
        fillUnknownSurplusDescription(surplus, submittedSurplus);
        return surplus;
    }

    private AmsInventoryTaskSurplus buildAssetCodeSurplus(AmsInventoryTask task,
            AmsInventoryTaskSurplus submittedSurplus)
    {
        String assetCode = StringUtils.trim(submittedSurplus.getAssetCode());
        if (StringUtils.isEmpty(assetCode))
        {
            throw new ServiceException("资产编码识别盘盈时资产编码不能为空");
        }
        validateLength(assetCode, 64, "资产编码");

        AmsAsset asset = amsAssetMapper.selectAmsAssetByAssetCodeForUpdate(assetCode);
        if (StringUtils.isNull(asset))
        {
            throw new ServiceException("资产编码不存在或已删除");
        }
        requireAssetNotInTaskItems(task.getTaskId(), asset.getAssetId(), asset.getAssetCode());

        AmsInventoryTaskSurplus surplus = new AmsInventoryTaskSurplus();
        surplus.setSurplusType(InventorySurplusType.KNOWN_OUT_OF_SCOPE);
        surplus.setIdentifyMethod(InventoryIdentifyMethod.ASSET_CODE);
        fillAssetSnapshot(surplus, asset);
        return surplus;
    }

    private AmsInventoryTaskSurplus buildManualSurplus(AmsInventoryTaskSurplus submittedSurplus)
    {
        if (StringUtils.isNotEmpty(StringUtils.trim(submittedSurplus.getEpcCode()))
                || StringUtils.isNotEmpty(StringUtils.trim(submittedSurplus.getAssetCode())))
        {
            throw new ServiceException("手工盘盈不填写EPC或资产编码，请选择对应识别方式");
        }
        if (StringUtils.isEmpty(StringUtils.trim(submittedSurplus.getAssetName())))
        {
            throw new ServiceException("手工盘盈资产名称不能为空");
        }
        AmsInventoryTaskSurplus surplus = new AmsInventoryTaskSurplus();
        surplus.setSurplusType(InventorySurplusType.UNKNOWN_OBJECT);
        surplus.setIdentifyMethod(InventoryIdentifyMethod.MANUAL);
        fillUnknownSurplusDescription(surplus, submittedSurplus);
        return surplus;
    }

    private void fillAssetSnapshot(AmsInventoryTaskSurplus surplus, AmsAsset asset)
    {
        surplus.setAssetId(asset.getAssetId());
        surplus.setAssetCode(asset.getAssetCode());
        surplus.setAssetName(asset.getAssetName());
        surplus.setCategoryId(asset.getCategoryId());
        surplus.setCategoryCode(asset.getCategoryCode());
        surplus.setCategoryName(asset.getCategoryName());
        surplus.setSpecModel(asset.getSpecModel());
        surplus.setBrand(asset.getBrand());
        surplus.setTagId(asset.getTagId());
        surplus.setTagCode(asset.getTagCode());
        surplus.setEpcCode(asset.getEpcCode());
        surplus.setBookAssetStatus(asset.getAssetStatus());
        surplus.setBookWarehouseId(asset.getWarehouseId());
        surplus.setBookWarehouseCode(asset.getWarehouseCode());
        surplus.setBookWarehouseName(asset.getWarehouseName());
        surplus.setBookLocationId(asset.getLocationId());
        surplus.setBookLocationCode(asset.getLocationCode());
        surplus.setBookLocationName(asset.getLocationName());
    }

    private void fillTagSnapshot(AmsInventoryTaskSurplus surplus, AmsRfidTag tag)
    {
        surplus.setTagId(tag.getTagId());
        surplus.setTagCode(tag.getTagCode());
        surplus.setEpcCode(tag.getEpcCode());
    }

    private void fillUnknownSurplusDescription(AmsInventoryTaskSurplus surplus,
            AmsInventoryTaskSurplus submittedSurplus)
    {
        validateLength(submittedSurplus.getAssetName(), 100, "盘盈资产名称");
        validateLength(submittedSurplus.getSpecModel(), 100, "盘盈规格型号");
        validateLength(submittedSurplus.getBrand(), 100, "盘盈品牌");
        surplus.setAssetName(StringUtils.trim(submittedSurplus.getAssetName()));
        surplus.setSpecModel(StringUtils.trim(submittedSurplus.getSpecModel()));
        surplus.setBrand(StringUtils.trim(submittedSurplus.getBrand()));
        if (StringUtils.isNotNull(submittedSurplus.getCategoryId()))
        {
            AmsAssetCategory category = requireEnabledCategory(submittedSurplus.getCategoryId());
            surplus.setCategoryId(category.getCategoryId());
            surplus.setCategoryCode(category.getCategoryCode());
            surplus.setCategoryName(category.getCategoryName());
        }
    }

    private void fillSurplusInventoryLocation(AmsInventoryTaskSurplus surplus,
            AmsInventoryTaskSurplus submittedSurplus)
    {
        if (StringUtils.isNull(submittedSurplus.getInventoryWarehouseId())
                || StringUtils.isNull(submittedSurplus.getInventoryLocationId()))
        {
            throw new ServiceException("盘盈明细需要填写盘点仓库和位置");
        }
        AmsWarehouse warehouse = requireEnabledWarehouse(submittedSurplus.getInventoryWarehouseId());
        AmsAssetLocation location = requireEnabledLocation(submittedSurplus.getInventoryLocationId(), warehouse);
        surplus.setInventoryWarehouseId(warehouse.getWarehouseId());
        surplus.setInventoryWarehouseCode(warehouse.getWarehouseCode());
        surplus.setInventoryWarehouseName(warehouse.getWarehouseName());
        surplus.setInventoryLocationId(location.getLocationId());
        surplus.setInventoryLocationCode(location.getLocationCode());
        surplus.setInventoryLocationName(location.getLocationName());
    }

    private void ensureSurplusNotDuplicate(AmsInventoryTaskSurplus surplus)
    {
        if (StringUtils.isNotNull(surplus.getAssetId())
                && amsInventoryTaskMapper.countInventoryTaskSurplusByAssetId(surplus.getTaskId(),
                        surplus.getAssetId()) > 0)
        {
            throw new ServiceException(StringUtils.format("资产【{}】已登记为盘盈", surplus.getAssetCode()));
        }
        if (StringUtils.isNotEmpty(surplus.getEpcCode())
                && amsInventoryTaskMapper.countInventoryTaskSurplusByEpcCode(surplus.getTaskId(),
                        surplus.getEpcCode()) > 0)
        {
            throw new ServiceException(StringUtils.format("EPC【{}】已登记为盘盈", surplus.getEpcCode()));
        }
    }

    private void requireAssetNotInTaskItems(Long taskId, Long assetId, String assetCode)
    {
        if (amsInventoryTaskMapper.countInventoryTaskItemByTaskIdAndAssetId(taskId, assetId) > 0)
        {
            throw new ServiceException(StringUtils.format("资产【{}】已在本任务应盘明细中，请直接录入盘点结果", assetCode));
        }
    }

    private void moveTaskToInventoryingIfIssued(AmsInventoryTask task, String operateLoginName, Date now)
    {
        if (!StringUtils.equals(InventoryTaskStatus.ISSUED, task.getTaskStatus()))
        {
            return;
        }
        task.setTaskStatus(InventoryTaskStatus.INVENTORYING);
        task.setUpdateBy(StringUtils.trim(operateLoginName));
        task.setUpdateTime(now);
        putExpectedStatus(task, InventoryTaskStatus.ISSUED);
        if (amsInventoryTaskMapper.updateAmsInventoryTaskStatus(task) != 1)
        {
            throw new ServiceException("盘点任务状态已变化，盘盈登记失败");
        }
    }

    private void fillInventoryLocation(AmsInventoryTaskItem resultItem, AmsInventoryTaskItem currentItem,
            AmsInventoryTaskItem submittedItem)
    {
        if (StringUtils.equals(InventoryResult.LOCATION_ERROR, resultItem.getInventoryResult()))
        {
            requireSubmittedInventoryLocation(submittedItem, currentItem);
            fillSubmittedInventoryLocation(resultItem, submittedItem);
            return;
        }

        if (StringUtils.isNotNull(submittedItem.getInventoryWarehouseId())
                || StringUtils.isNotNull(submittedItem.getInventoryLocationId()))
        {
            requireSubmittedInventoryLocation(submittedItem, currentItem);
            fillSubmittedInventoryLocation(resultItem, submittedItem);
            return;
        }

        if (!StringUtils.equals(InventoryResult.LOSS, resultItem.getInventoryResult()))
        {
            resultItem.setInventoryWarehouseId(currentItem.getBookWarehouseId());
            resultItem.setInventoryWarehouseName(currentItem.getBookWarehouseName());
            resultItem.setInventoryLocationId(currentItem.getBookLocationId());
            resultItem.setInventoryLocationName(currentItem.getBookLocationName());
        }
    }

    private void requireSubmittedInventoryLocation(AmsInventoryTaskItem submittedItem, AmsInventoryTaskItem currentItem)
    {
        if (StringUtils.isNull(submittedItem.getInventoryWarehouseId())
                || StringUtils.isNull(submittedItem.getInventoryLocationId()))
        {
            throw new ServiceException(StringUtils.format("资产【{}】需要填写盘点仓库和位置", currentItem.getAssetCode()));
        }
    }

    private void fillSubmittedInventoryLocation(AmsInventoryTaskItem resultItem, AmsInventoryTaskItem submittedItem)
    {
        AmsWarehouse warehouse = requireEnabledWarehouse(submittedItem.getInventoryWarehouseId());
        AmsAssetLocation location = requireEnabledLocation(submittedItem.getInventoryLocationId(), warehouse);
        resultItem.setInventoryWarehouseId(warehouse.getWarehouseId());
        resultItem.setInventoryWarehouseName(warehouse.getWarehouseName());
        resultItem.setInventoryLocationId(location.getLocationId());
        resultItem.setInventoryLocationName(location.getLocationName());
    }

    private Map<Long, AmsInventoryTaskItem> submittedItemMap(AmsInventoryTask amsInventoryTask)
    {
        List<AmsInventoryTaskItem> submittedItems = amsInventoryTask.getAmsInventoryTaskItemList();
        if (submittedItems == null || submittedItems.isEmpty())
        {
            throw new ServiceException("盘点结果明细不能为空");
        }
        Map<Long, AmsInventoryTaskItem> itemMap = new HashMap<>();
        for (AmsInventoryTaskItem item : submittedItems)
        {
            if (StringUtils.isNull(item.getItemId()))
            {
                throw new ServiceException("盘点结果明细ID不能为空");
            }
            if (itemMap.put(item.getItemId(), item) != null)
            {
                throw new ServiceException("盘点结果明细不能重复提交");
            }
        }
        return itemMap;
    }

    private List<AmsInventoryTaskItem> sortedItems(AmsInventoryTask task)
    {
        List<AmsInventoryTaskItem> items = task.getAmsInventoryTaskItemList();
        if (items == null)
        {
            return new ArrayList<>();
        }
        List<AmsInventoryTaskItem> sortedItems = new ArrayList<>(items);
        sortedItems.sort(Comparator.comparing(AmsInventoryTaskItem::getAssetId,
                Comparator.nullsLast(Long::compareTo)));
        return sortedItems;
    }

    private List<AmsInventoryTaskSurplus> sortedSurplusItems(AmsInventoryTask task)
    {
        List<AmsInventoryTaskSurplus> surplusItems = task.getAmsInventoryTaskSurplusList();
        if (surplusItems == null)
        {
            return new ArrayList<>();
        }
        List<AmsInventoryTaskSurplus> sortedSurplusItems = new ArrayList<>(surplusItems);
        sortedSurplusItems.sort(Comparator.comparing(AmsInventoryTaskSurplus::getSurplusId,
                Comparator.nullsLast(Long::compareTo)));
        return sortedSurplusItems;
    }

    private void validateSurplusItemsBeforeSubmit(AmsInventoryTask task)
    {
        for (AmsInventoryTaskSurplus surplus : sortedSurplusItems(task))
        {
            requireSurplusRecorded(surplus);
        }
    }

    private void requireSurplusRecorded(AmsInventoryTaskSurplus surplus)
    {
        if (!StringUtils.equals(InventorySurplusStatus.RECORDED, surplus.getConfirmStatus()))
        {
            throw new ServiceException(StringUtils.format("盘盈明细【{}】状态无效，不能提交或确认",
                    StringUtils.nvl(surplus.getAssetCode(), surplus.getEpcCode())));
        }
    }

    private void requireStatus(AmsInventoryTask task, String requiredStatus, String message)
    {
        if (!StringUtils.equals(requiredStatus, task.getTaskStatus()))
        {
            throw new ServiceException(message);
        }
    }

    private void requireAnyStatus(AmsInventoryTask task, String[] requiredStatuses, String message)
    {
        if (!StringUtils.equalsAny(task.getTaskStatus(), requiredStatuses))
        {
            throw new ServiceException(message);
        }
    }

    private void requireNotStatus(AmsInventoryTask task, String[] excludedStatuses, String message)
    {
        if (StringUtils.equalsAny(task.getTaskStatus(), excludedStatuses))
        {
            throw new ServiceException(message);
        }
    }

    private void putExpectedStatus(AmsInventoryTask task, String expectedStatus)
    {
        task.getParams().put("expectedStatus", expectedStatus);
    }

    private void putExpectedStatuses(AmsInventoryTask task, String... expectedStatuses)
    {
        task.getParams().put("expectedStatuses", expectedStatuses);
    }

    private void putExcludedStatuses(AmsInventoryTask task, String... excludedStatuses)
    {
        task.getParams().put("excludedStatuses", excludedStatuses);
    }

    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 + "个字符");
        }
    }
}
