package com.ruoyi.asset.service.impl;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetLifecycleLog;
import com.ruoyi.asset.mapper.AmsAssetLifecycleLogMapper;
import com.ruoyi.asset.constant.AssetLifecycleBusinessType;
import com.ruoyi.asset.service.IAssetLifecycleService;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysDictDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 资产履历公共服务实现
 *
 * @author Yangk
 */
@Service
public class AssetLifecycleServiceImpl implements IAssetLifecycleService
{
    private static final String LIFECYCLE_BUSINESS_TYPE_DICT = "ams_lifecycle_business_type";

    private static final String DEL_FLAG_NORMAL = "0";

    @Autowired
    private AmsAssetLifecycleLogMapper lifecycleLogMapper;

    @Autowired
    private ISysDictDataService sysDictDataService;

    @Override
    public List<AmsAssetLifecycleLog> selectByAssetId(Long assetId)
    {
        if (StringUtils.isNull(assetId))
        {
            throw new ServiceException("资产ID不能为空");
        }
        return lifecycleLogMapper.selectAmsAssetLifecycleLogByAssetId(assetId);
    }

    /**
     * 通过履历表的 source_order_id 关联标签ID，判断该标签是否曾参与过 RFID 绑定/解绑操作。
     * 用于删除前置检查：有履历的标签只允许作废而不允许物理删除，确保审计可追溯。
     */
    @Override
    public boolean hasRfidLifecycle(Long tagId)
    {
        if (StringUtils.isNull(tagId))
        {
            throw new ServiceException("RFID标签ID不能为空");
        }
        return lifecycleLogMapper.countRfidLifecycleByTagId(tagId,
                new String[] {AssetLifecycleBusinessType.RFID_BIND, AssetLifecycleBusinessType.RFID_UNBIND}) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int recordLifecycle(AmsAsset beforeAsset, AmsAsset afterAsset, AmsAssetLifecycleLog lifecycleLog)
    {
        // 1. 规范化处理传入的日志数据，统一去除空格
        normalizeLifecycleLog(lifecycleLog);
        // 2. 强校验：执行核心业务边界与入参有效性校验（Fail-Fast）
        validateRequest(beforeAsset, afterAsset, lifecycleLog);
        // 3. 确定资产身份标识快照（优先使用变动后快照，若被删除/归空则使用变动前快照）
        AmsAsset assetSnapshot = StringUtils.isNotNull(afterAsset) ? afterAsset : beforeAsset;
        fillAssetIdentity(lifecycleLog, assetSnapshot);
        fillBeforeSnapshot(lifecycleLog, beforeAsset);
        fillAfterSnapshot(lifecycleLog, afterAsset);

        Date operateTime = StringUtils.nvl(lifecycleLog.getOperateTime(), DateUtils.getNowDate());
        lifecycleLog.setOperateTime(operateTime);
        lifecycleLog.setCreateTime(operateTime);
        lifecycleLog.setDelFlag(DEL_FLAG_NORMAL);
        lifecycleLog.setUpdateBy(null);
        lifecycleLog.setUpdateTime(null);

        int rows = lifecycleLogMapper.insertAmsAssetLifecycleLog(lifecycleLog);
        if (rows != 1)
        {
            throw new ServiceException("资产履历写入失败");
        }
        return rows;
    }

    private void validateRequest(AmsAsset beforeAsset, AmsAsset afterAsset, AmsAssetLifecycleLog lifecycleLog)
    {
        // 落实 Fail Fast 原则：防范核心主表快照为空，确保履历能关联到有效实体
        if (StringUtils.isNull(beforeAsset) && StringUtils.isNull(afterAsset))
        {
            throw new ServiceException("资产变更前后快照不能同时为空");
        }
        if (StringUtils.isNull(lifecycleLog))
        {
            throw new ServiceException("资产履历业务信息不能为空");
        }
        // 强一致性校验：防止因操作失误传入不同资产的快照造成履历数据交叉污染
        if (StringUtils.isNotNull(beforeAsset) && StringUtils.isNotNull(afterAsset)
                && !Objects.equals(beforeAsset.getAssetId(), afterAsset.getAssetId()))
        {
            throw new ServiceException("资产变更前后快照不属于同一资产");
        }

        AmsAsset assetSnapshot = StringUtils.isNotNull(afterAsset) ? afterAsset : beforeAsset;
        if (StringUtils.isNull(assetSnapshot.getAssetId())
                || StringUtils.isEmpty(assetSnapshot.getAssetCode())
                || StringUtils.isEmpty(assetSnapshot.getAssetName()))
        {
            throw new ServiceException("资产履历缺少资产ID、资产编码或资产名称");
        }
        validateBusinessType(lifecycleLog.getBusinessType());
        validateLength(lifecycleLog.getSourceOrderNo(), 64, "来源单据号");
        validateLength(lifecycleLog.getOperateUserName(), 100, "操作用户名称");
        validateLength(lifecycleLog.getChangeSummary(), 500, "变更摘要");
        validateLength(lifecycleLog.getCreateBy(), 64, "创建账号");
        validateLength(lifecycleLog.getRemark(), 500, "备注");
    }

    private void normalizeLifecycleLog(AmsAssetLifecycleLog lifecycleLog)
    {
        if (StringUtils.isNull(lifecycleLog))
        {
            return;
        }
        lifecycleLog.setBusinessType(StringUtils.trim(lifecycleLog.getBusinessType()));
        lifecycleLog.setSourceOrderNo(StringUtils.trim(lifecycleLog.getSourceOrderNo()));
        lifecycleLog.setOperateUserName(StringUtils.trim(lifecycleLog.getOperateUserName()));
        lifecycleLog.setChangeSummary(StringUtils.trim(lifecycleLog.getChangeSummary()));
        lifecycleLog.setCreateBy(StringUtils.trim(lifecycleLog.getCreateBy()));
        lifecycleLog.setRemark(StringUtils.trim(lifecycleLog.getRemark()));
    }

    private void validateBusinessType(String businessType)
    {
        if (StringUtils.isEmpty(businessType))
        {
            throw new ServiceException("资产履历业务类型不能为空");
        }
        // 动态数据字典校验：通过系统字典 ams_lifecycle_business_type 验证，而非硬编码，实现未来灵活扩展
        SysDictData query = new SysDictData();
        query.setDictType(LIFECYCLE_BUSINESS_TYPE_DICT);
        query.setDictValue(businessType);
        query.setStatus(UserConstants.DICT_NORMAL);
        List<SysDictData> dictDataList = sysDictDataService.selectDictDataList(query);
        boolean exists = dictDataList != null && dictDataList.stream()
                .anyMatch(data -> StringUtils.equals(businessType, data.getDictValue()));
        if (!exists)
        {
            throw new ServiceException("资产履历业务类型不存在或已停用");
        }
    }

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

    private void fillAssetIdentity(AmsAssetLifecycleLog lifecycleLog, AmsAsset asset)
    {
        lifecycleLog.setAssetId(asset.getAssetId());
        lifecycleLog.setAssetCode(asset.getAssetCode());
        lifecycleLog.setAssetName(asset.getAssetName());
    }

    private void fillBeforeSnapshot(AmsAssetLifecycleLog lifecycleLog, AmsAsset asset)
    {
        lifecycleLog.setBeforeStatus(StringUtils.isNull(asset) ? null : asset.getAssetStatus());
        lifecycleLog.setBeforeWarehouseId(StringUtils.isNull(asset) ? null : asset.getWarehouseId());
        lifecycleLog.setBeforeWarehouseName(StringUtils.isNull(asset) ? null : asset.getWarehouseName());
        lifecycleLog.setBeforeLocationId(StringUtils.isNull(asset) ? null : asset.getLocationId());
        lifecycleLog.setBeforeLocationName(StringUtils.isNull(asset) ? null : asset.getLocationName());
        lifecycleLog.setBeforeUserId(StringUtils.isNull(asset) ? null : asset.getUseUserId());
        lifecycleLog.setBeforeUserName(StringUtils.isNull(asset) ? null : asset.getUseUserName());
        lifecycleLog.setBeforeDeptId(StringUtils.isNull(asset) ? null : asset.getUseDeptId());
        lifecycleLog.setBeforeDeptName(StringUtils.isNull(asset) ? null : asset.getUseDeptName());
    }

    private void fillAfterSnapshot(AmsAssetLifecycleLog lifecycleLog, AmsAsset asset)
    {
        lifecycleLog.setAfterStatus(StringUtils.isNull(asset) ? null : asset.getAssetStatus());
        lifecycleLog.setAfterWarehouseId(StringUtils.isNull(asset) ? null : asset.getWarehouseId());
        lifecycleLog.setAfterWarehouseName(StringUtils.isNull(asset) ? null : asset.getWarehouseName());
        lifecycleLog.setAfterLocationId(StringUtils.isNull(asset) ? null : asset.getLocationId());
        lifecycleLog.setAfterLocationName(StringUtils.isNull(asset) ? null : asset.getLocationName());
        lifecycleLog.setAfterUserId(StringUtils.isNull(asset) ? null : asset.getUseUserId());
        lifecycleLog.setAfterUserName(StringUtils.isNull(asset) ? null : asset.getUseUserName());
        lifecycleLog.setAfterDeptId(StringUtils.isNull(asset) ? null : asset.getUseDeptId());
        lifecycleLog.setAfterDeptName(StringUtils.isNull(asset) ? null : asset.getUseDeptName());
    }
}
