package com.ruoyi.asset.service.impl;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import com.ruoyi.asset.constant.AssetStatus;
import com.ruoyi.asset.domain.AmsAssetCategory;
import com.ruoyi.asset.service.IAmsAssetCategoryService;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.asset.mapper.AmsAssetMapper;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.service.IAmsAssetService;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 资产台账Service业务层处理
 * 
 * @author Yangk
 * @date 2026-06-04
 */
@Service
public class AmsAssetServiceImpl implements IAmsAssetService 
{
    private static final String ENABLED_YES = "Y";

    private static final String DEL_FLAG_NORMAL = "0";

    private static final String ASSET_STATUS_DICT_TYPE = "ams_asset_status";

    @Autowired
    private AmsAssetMapper amsAssetMapper;

    @Autowired
    private IAmsAssetCategoryService amsAssetCategoryService;

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    private ISysDictDataService sysDictDataService;

    /**
     * 查询资产台账
     * 
     * @param assetId 资产台账主键
     * @return 资产台账
     */
    @Override
    public AmsAsset selectAmsAssetByAssetId(Long assetId)
    {
        return amsAssetMapper.selectAmsAssetByAssetId(assetId);
    }

    /**
     * 查询资产台账列表
     * 
     * @param amsAsset 资产台账
     * @return 资产台账
     */
    @Override
    public List<AmsAsset> selectAmsAssetList(AmsAsset amsAsset)
    {
        return amsAssetMapper.selectAmsAssetList(amsAsset);
    }

    /**
     * 查询可绑定RFID标签的资产列表
     */
    @Override
    public List<AmsAsset> selectBindableAmsAssetList(AmsAsset amsAsset)
    {
        return amsAssetMapper.selectBindableAmsAssetList(amsAsset, AssetStatus.DISPOSED);
    }

    /**
     * 校验资产编码是否唯一
     *
     * @param amsAsset 资产台账
     * @return 结果
     */
    @Override
    public boolean checkAssetCodeUnique(AmsAsset amsAsset)
    {
        if (StringUtils.isEmpty(amsAsset.getAssetCode()))
        {
            return UserConstants.UNIQUE;
        }
        Long assetId = StringUtils.isNull(amsAsset.getAssetId()) ? -1L : amsAsset.getAssetId();
        AmsAsset info = amsAssetMapper.checkAssetCodeUnique(amsAsset.getAssetCode());
        if (StringUtils.isNotNull(info) && info.getAssetId().longValue() != assetId.longValue())
        {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 导入资产台账数据
     *
     * @param assetList 资产台账数据列表
     * @param isUpdateSupport 是否更新支持，一期资产台账不覆盖已有资产
     * @param operName 操作用户
     * @return 结果
     */
    @Override
    public String importAmsAsset(List<AmsAsset> assetList, Boolean isUpdateSupport, String operName)
    {
        if (assetList == null || assetList.isEmpty())
        {
            throw new ServiceException("导入资产台账数据不能为空！");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        for (AmsAsset asset : assetList)
        {
            try
            {
                if (!checkAssetCodeUnique(asset))
                {
                    throw new ServiceException("资产编码已存在，一期导入不覆盖已有资产");
                }
                asset.setCreateBy(operName);
                insertAmsAsset(asset);
                successNum++;
                successMsg.append("<br/>" + successNum + "、资产 " + asset.getAssetCode() + " 导入成功");
            }
            catch (Exception e)
            {
                failureNum++;
                String assetCode = StringUtils.isEmpty(asset.getAssetCode()) ? "第" + failureNum + "条" : asset.getAssetCode();
                failureMsg.append("<br/>" + failureNum + "、资产 " + assetCode + " 导入失败：" + e.getMessage());
            }
        }
        if (failureNum > 0)
        {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据不符合资产台账规则，错误如下：");
            throw new ServiceException(failureMsg.toString());
        }
        successMsg.insert(0, "恭喜您，资产台账数据已全部导入成功！共 " + successNum + " 条，数据如下：");
        return successMsg.toString();
    }

    /**
     * 新增资产台账
     * 
     * @param amsAsset 资产台账
     * @return 结果
     */
    @Override
    public int insertAmsAsset(AmsAsset amsAsset)
    {
        if (!checkAssetCodeUnique(amsAsset))
        {
            throw new ServiceException("资产编码已存在");
        }
        // 普通建档只表示实物已登记，仓位和使用归属必须由首次入库及后续流转业务写入。
        amsAsset.setAssetStatus(AssetStatus.PENDING_INBOUND);
        clearWarehouseLocation(amsAsset);
        clearUseOwnership(amsAsset);
        clearRfidBinding(amsAsset);
        fillBasicAssetForSave(amsAsset);
        amsAsset.setDelFlag(DEL_FLAG_NORMAL);
        amsAsset.setCreateTime(DateUtils.getNowDate());
        return amsAssetMapper.insertAmsAsset(amsAsset);
    }

    /**
     * 修改资产台账
     * 
     * @param amsAsset 资产台账
     * @return 结果
     */
    @Override
    public int updateAmsAsset(AmsAsset amsAsset)
    {
        AmsAsset current = selectAmsAssetByAssetId(amsAsset.getAssetId());
        if (StringUtils.isNull(current))
        {
            throw new ServiceException("资产不存在或已删除");
        }
        if (!checkAssetCodeUnique(amsAsset))
        {
            throw new ServiceException("资产编码已存在");
        }
        // 普通编辑不能改变状态、当前归属或 RFID 当前关系，避免绕过公共业务服务。
        preserveControlledFields(amsAsset, current);
        preserveOwnershipFields(amsAsset, current);
        fillBasicAssetForSave(amsAsset);
        amsAsset.setUpdateTime(DateUtils.getNowDate());
        return amsAssetMapper.updateAmsAsset(amsAsset);
    }

    /**
     * 批量删除资产台账
     * 
     * @param assetIds 需要删除的资产台账主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteAmsAssetByAssetIds(String assetIds)
    {
        String[] ids = Convert.toStrArray(assetIds);
        // 按ID升序逐行加锁删除，代替批量 IN(...) 避免间隙锁和锁序不确定的死锁风险
        Long[] sortedIds = Arrays.stream(ids).map(Long::valueOf).sorted().toArray(Long[]::new);
        int rows = 0;
        for (Long assetId : sortedIds)
        {
            AmsAsset asset = amsAssetMapper.selectAmsAssetByAssetIdForUpdate(assetId);
            if (StringUtils.isNull(asset))
            {
                continue;
            }
            checkAssetReferenced(asset);
            rows += amsAssetMapper.deleteAmsAssetByAssetId(assetId);
        }
        return rows;
    }

    /**
     * 删除资产台账信息
     * 
     * @param assetId 资产台账主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteAmsAssetByAssetId(Long assetId)
    {
        // 先加行锁再检查引用，避免检查与删除之间的并发窗口导致误删
        AmsAsset asset = amsAssetMapper.selectAmsAssetByAssetIdForUpdate(assetId);
        if (StringUtils.isNull(asset))
        {
            return 0;
        }
        checkAssetReferenced(asset);
        return amsAssetMapper.deleteAmsAssetByAssetId(assetId);
    }

    /**
     * 页面和导入只传业务选择值，快照字段统一在服务层回填，避免前端伪造名称造成台账不一致。
     */
    private void fillBasicAssetForSave(AmsAsset amsAsset)
    {
        validateBasicFields(amsAsset);
        fillCategorySnapshot(amsAsset);
        fillCustodianSnapshot(amsAsset);
        validateAssetStatus(amsAsset);
        validateNumberFields(amsAsset);
    }

    private void validateBasicFields(AmsAsset amsAsset)
    {
        if (StringUtils.isEmpty(amsAsset.getAssetCode()))
        {
            throw new ServiceException("资产编码不能为空");
        }
        if (StringUtils.isEmpty(amsAsset.getAssetName()))
        {
            throw new ServiceException("资产名称不能为空");
        }
    }

    private void fillCategorySnapshot(AmsAsset amsAsset)
    {
        AmsAssetCategory category = null;
        if (StringUtils.isNotNull(amsAsset.getCategoryId()))
        {
            category = amsAssetCategoryService.selectAmsAssetCategoryByCategoryId(amsAsset.getCategoryId());
        }
        else if (StringUtils.isNotEmpty(amsAsset.getCategoryCode()))
        {
            AmsAssetCategory query = new AmsAssetCategory();
            query.setCategoryCode(amsAsset.getCategoryCode());
            List<AmsAssetCategory> categoryList = amsAssetCategoryService.selectAmsAssetCategoryList(query);
            category = categoryList == null || categoryList.isEmpty() ? null : categoryList.get(0);
        }

        if (StringUtils.isNull(category))
        {
            throw new ServiceException("资产类别不能为空或不存在");
        }
        if (!StringUtils.equals(ENABLED_YES, category.getEnabled()))
        {
            throw new ServiceException("资产类别已停用");
        }
        amsAsset.setCategoryId(category.getCategoryId());
        amsAsset.setCategoryCode(category.getCategoryCode());
        amsAsset.setCategoryName(category.getCategoryName());
        if (StringUtils.isNull(amsAsset.getUseLife()))
        {
            amsAsset.setUseLife(category.getDefaultUseLife());
        }
        if (StringUtils.isNull(amsAsset.getMaintenanceCycle()))
        {
            amsAsset.setMaintenanceCycle(category.getDefaultMaintenanceCycle());
        }
    }

    private void fillCustodianSnapshot(AmsAsset amsAsset)
    {
        if (StringUtils.isNull(amsAsset.getCustodianId()))
        {
            amsAsset.setCustodianName(null);
            return;
        }
        SysUser user = selectNormalUser(amsAsset.getCustodianId(), "保管人");
        amsAsset.setCustodianName(user.getUserName());
    }

    private SysUser selectNormalUser(Long userId, String fieldName)
    {
        SysUser user = sysUserService.selectUserById(userId);
        if (StringUtils.isNull(user)
                || !StringUtils.equals(UserConstants.NORMAL, user.getStatus())
                || !StringUtils.equals(DEL_FLAG_NORMAL, user.getDelFlag()))
        {
            throw new ServiceException(fieldName + "不存在或已停用");
        }
        return user;
    }

    private void validateAssetStatus(AmsAsset amsAsset)
    {
        if (StringUtils.isEmpty(amsAsset.getAssetStatus()))
        {
            return;
        }
        SysDictData query = new SysDictData();
        query.setDictType(ASSET_STATUS_DICT_TYPE);
        query.setDictValue(amsAsset.getAssetStatus());
        query.setStatus(UserConstants.NORMAL);
        List<SysDictData> dictDataList = sysDictDataService.selectDictDataList(query);
        if (dictDataList == null || dictDataList.isEmpty())
        {
            throw new ServiceException("资产状态不存在或已停用");
        }
    }

    private void validateNumberFields(AmsAsset amsAsset)
    {
        if (StringUtils.isNotNull(amsAsset.getOriginalValue())
                && amsAsset.getOriginalValue().compareTo(BigDecimal.ZERO) < 0)
        {
            throw new ServiceException("资产原值不能小于0");
        }
        if (StringUtils.isNotNull(amsAsset.getUseLife()) && amsAsset.getUseLife() < 0)
        {
            throw new ServiceException("使用寿命不能小于0");
        }
        if (StringUtils.isNotNull(amsAsset.getMaintenanceCycle()) && amsAsset.getMaintenanceCycle() < 0)
        {
            throw new ServiceException("维保周期不能小于0");
        }
    }

    /** 防篡改：将状态和 RFID 关系强制恢复为数据库当前值，阻止前端直接修改这些受控字段 */
    private void preserveControlledFields(AmsAsset incoming, AmsAsset current)
    {
        incoming.setAssetStatus(current.getAssetStatus());
        incoming.setTagId(current.getTagId());
        incoming.setTagCode(current.getTagCode());
        incoming.setEpcCode(current.getEpcCode());
    }

    /** 普通编辑始终保留当前归属快照，归属只能通过业务流转服务修改。 */
    private void preserveOwnershipFields(AmsAsset incoming, AmsAsset current)
    {
        incoming.setWarehouseId(current.getWarehouseId());
        incoming.setWarehouseCode(current.getWarehouseCode());
        incoming.setWarehouseName(current.getWarehouseName());
        incoming.setLocationId(current.getLocationId());
        incoming.setLocationCode(current.getLocationCode());
        incoming.setLocationName(current.getLocationName());
        incoming.setUseDeptId(current.getUseDeptId());
        incoming.setUseDeptName(current.getUseDeptName());
        incoming.setUseUserId(current.getUseUserId());
        incoming.setUseUserName(current.getUseUserName());
    }

    /** 清除仓库和位置，待入库资产在首次确认入库前不能拥有仓位。 */
    private void clearWarehouseLocation(AmsAsset asset)
    {
        asset.setWarehouseId(null);
        asset.setWarehouseCode(null);
        asset.setWarehouseName(null);
        asset.setLocationId(null);
        asset.setLocationCode(null);
        asset.setLocationName(null);
    }

    /** 清除使用归属信息，待入库和在库资产均不应有使用归属。 */
    private void clearUseOwnership(AmsAsset asset)
    {
        asset.setUseDeptId(null);
        asset.setUseDeptName(null);
        asset.setUseUserId(null);
        asset.setUseUserName(null);
    }

    /** 清除RFID当前关系，资产建档不能绕过公共绑定服务直接建立标签关系 */
    private void clearRfidBinding(AmsAsset asset)
    {
        asset.setTagId(null);
        asset.setTagCode(null);
        asset.setEpcCode(null);
    }

    /**
     * 资产一旦进入单据、履历或 RFID 当前绑定，就不能删除；保留逻辑删除只服务未使用的建档错误数据。
     */
    private void checkAssetReferenced(AmsAsset asset)
    {
        if (StringUtils.isNotNull(asset.getTagId())
                || StringUtils.isNotEmpty(asset.getTagCode())
                || StringUtils.isNotEmpty(asset.getEpcCode()))
        {
            throw new ServiceException(String.format("资产【%1$s】已绑定 RFID 标签，不允许删除", asset.getAssetCode()));
        }
        if (amsAssetMapper.countAssetReferences(asset.getAssetId()) > 0)
        {
            throw new ServiceException(String.format("资产【%1$s】已有业务引用或履历，不允许删除", asset.getAssetCode()));
        }
    }
}
