package com.ruoyi.inventory.service.impl;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.asset.domain.TbAssetInformation;
import com.ruoyi.asset.domain.TbLocation;
import com.ruoyi.asset.mapper.TbAssetInformationMapper;
import com.ruoyi.asset.mapper.TbLocationMapper;
import com.ruoyi.inventory.domain.TbInventoryDetail;
import com.ruoyi.inventory.domain.dto.*;
import com.ruoyi.inventory.mapper.TbInventoryDetailMapper;
import com.ruoyi.utils.IDUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.inventory.mapper.TbAssetInventoryMapper;
import com.ruoyi.inventory.domain.TbAssetInventory;
import com.ruoyi.inventory.service.ITbAssetInventoryService;
import org.springframework.web.bind.annotation.RequestMapping;

import static com.ruoyi.inventory.domain.enums.InventoryPlanResult.*;
import static com.ruoyi.inventory.domain.enums.InventoryPlanStatus.PLAN_FINISH;
import static com.ruoyi.inventory.domain.enums.InventoryResult.*;
import static com.ruoyi.inventory.domain.enums.InventoryStatus.COUNTED;
import static com.ruoyi.inventory.domain.enums.InventoryStatus.UNCOUNTED;

/**
 * 资产盘点记录Service业务层处理
 *
 * @author ydl
 * @date 2023-06-01
 */
@Service
public class TbAssetInventoryServiceImpl implements ITbAssetInventoryService
{
    @Autowired
    private TbAssetInventoryMapper tbAssetInventoryMapper;

    @Autowired
    private TbAssetInformationMapper informationMapper;

    @Autowired
    private TbInventoryDetailMapper tbInventoryDetailMapper;

    @Autowired
    private TbLocationMapper locationMapper;

    /**
     * 查询资产盘点记录
     *
     * @param id 资产盘点记录主键
     * @return 资产盘点记录
     */
    @Override
    public TbAssetInventory selectTbAssetInventoryById(Long id)
    {
        TbAssetInventory tbAssetInventory = tbAssetInventoryMapper.selectTbAssetInventoryById(id);
        String inventoryLocation = tbAssetInventory.getInventoryLocation();
        String locationName = getLocationName(inventoryLocation);
        tbAssetInventory.setInventoryLocationName(locationName);
        return tbAssetInventory;
    }

    @Override
    public TbAssetInventory selectTbAssetInventoryByNumber(String num) {
        TbAssetInventory tbAssetInventory = tbAssetInventoryMapper.selectTbAssetInventoryByNumber(num);
        String inventoryLocation = tbAssetInventory.getInventoryLocation();
        String locationName = getLocationName(inventoryLocation);
        tbAssetInventory.setInventoryLocationName(locationName);
        return tbAssetInventory;
    }

    /**
     * 查询资产盘点记录列表
     *
     * @param tbAssetInventory 资产盘点记录
     * @return 资产盘点记录
     */
    @Override
    public List<TbAssetInventory> selectTbAssetInventoryList(TbAssetInventory tbAssetInventory)
    {
        List<TbAssetInventory> tbAssetInventories = tbAssetInventoryMapper.selectTbAssetInventoryList(tbAssetInventory);
        for (TbAssetInventory assetInventory : tbAssetInventories) {
            String inventoryLocation = assetInventory.getInventoryLocation();
            String locationName = getLocationName(inventoryLocation);
            assetInventory.setInventoryLocationName(locationName);
        }
        return tbAssetInventories;
    }

    private String getLocationName(String locationIds) {
        String[] ids = locationIds.split(",");
        if (ids.length < 1) {
            return "";
        }
        List<TbLocation> tbLocations = locationMapper.selectTbLocationByIds(List.of(ids));
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < tbLocations.size(); i++) {
            if (i == tbLocations.size() - 1) {
                stringBuilder.append(tbLocations.get(i).getName());
                continue;
            }
            stringBuilder.append(tbLocations.get(i).getName());
            stringBuilder.append(",");
        }
        return stringBuilder.toString();
    }

    /**
     * 新增资产盘点记录
     *
     * @param tbAssetInventory 资产盘点记录
     * @return 结果
     */
    @Override
    public int insertTbAssetInventory(TbAssetInventory tbAssetInventory)
    {
        String orderNumber = IDUtil.getUUID1();
        tbAssetInventory.setOrderNumber(orderNumber);

        Integer recordStatus = tbAssetInventory.getRecordStatus();
        if (recordStatus > 0) {
            tbAssetInventory.setInventoryStatus(1);
        } else {
            tbAssetInventory.setInventoryStatus(0);
        }

        String inventoryLocation = tbAssetInventory.getInventoryLocation();
        List<TbInventoryDetail> inventoryDetails = getInventoryDetails(orderNumber, inventoryLocation);

        int insertTbAssetInventory = tbAssetInventoryMapper.insertTbAssetInventory(tbAssetInventory);
        if (insertTbAssetInventory == 0) {
            return 0;
        }

        for (TbInventoryDetail tbInventoryDetail : inventoryDetails) {
            tbInventoryDetailMapper.insertTbInventoryDetail(tbInventoryDetail);
        }

        return 1;
    }

    /**
     * 手持机盘点
     *
     * @param takeStockDTOList
     * @return
     */
    @Override
    public int handheldInventory(List<TakeStockDTO> takeStockDTOList) {
        int result = 0;
        for (TakeStockDTO takeStockDTO : takeStockDTOList) {
            // 获取单据编码
            String orderNumber = takeStockDTO.getOrderNumber();
            // 获取手持机的epc
            Set<String> epcSet = takeStockDTO.getAssetList()
                    .stream()
                    .map(tagInfo -> {
                        String epc = tagInfo.getEpc();
                        // 删除字符串中前导的零
                        epc = epc.replaceFirst("^0+", "");
                        return epc;
                    })
                    .collect(Collectors.toSet());
            if (StrUtil.isNotBlank(orderNumber)) { // 存在盘点计划
                TbAssetInventory tbAssetInventory = tbAssetInventoryMapper.selectTbAssetInventoryByNumber(orderNumber);
                if (tbAssetInventory == null) {
                    continue;
                }

                Integer stockResult = startStock(tbAssetInventory,  epcSet);
                if (stockResult > 0) {
                    result++;
                }
            } else { // 未存在盘点计划
                // 获取盘点位置编码
                String inventoryLocation = takeStockDTO.getInventoryLocation();
                List<String> locationNumbers = List.of(inventoryLocation.split(","));

                // 根据编号获取有效位置ID
                List<Long> locationIds = locationMapper.selectTbLocationIdByNumbers(locationNumbers);
                if (locationIds.isEmpty()) {
                    continue;
                }

                TbAssetInventory inventory = new TbAssetInventory();
                Date now = new Date();
                String nowDateTime = DateUtil.format(now, "yyyy年MM月dd日 HH:mm:ss");
                inventory.setName(nowDateTime + "临时盘点任务");
                inventory.setInventoryDepartment("100");
                inventory.setInventoryBy("admin");
                inventory.setInventoryDate(now);

                StringBuilder locationStrIds = new StringBuilder();
                for (int i = 0; i < locationIds.size(); i++) {
                    locationStrIds.append(locationIds.get(i));
                    if (i < locationIds.size() - 1) {
                        locationStrIds.append(",");
                    }
                }
                inventory.setInventoryLocation(locationStrIds.toString());
                inventory.setRecordStatus(1);

                // 新增盘点计划
                int insertResult = insertTbAssetInventory(inventory);
                if (insertResult < 1) { // 新增失败
                    continue;
                }

                // 获取单点单号
                String newOrderNumber = inventory.getOrderNumber();
                // 获取盘点计划
                TbAssetInventory tbAssetInventory = tbAssetInventoryMapper.selectTbAssetInventoryByNumber(newOrderNumber);

                Integer stockResult = startStock(tbAssetInventory, epcSet);
                if (stockResult > 0) {
                    result++;
                }
            }

        }
        return result > 0 ? 1 : 0;
    }

    /**
     * 开始盘点
     *
     * @param tbAssetInventory 盘点计划信息
     * @param epcSet 盘点epc集合
     */
    private Integer startStock(TbAssetInventory tbAssetInventory, Set<String> epcSet) {
        // 获取盘点单号
        String orderNumber = tbAssetInventory.getOrderNumber();

        int result = 0;

        // 根据盘点单号获取盘点资产详细
        List<TbInventoryDetail> tbInventoryDetails = tbInventoryDetailMapper.selectTbInventoryDetailByOrderNumber(orderNumber);
        for (TbInventoryDetail tbInventoryDetail : tbInventoryDetails) {
            // 获取资产元数据
            String inventoryMetadata = tbInventoryDetail.getInventoryMetadata();
            // 将元数据转换为资产信息
            TbAssetInformation assetInformation = JSONUtil.toBean(inventoryMetadata, TbAssetInformation.class);
            Integer assetQuantity = assetInformation.getQuantity();

            // 实际盘点数量统计
            int inventoryQuantity;

            // 获取该资产的所有epc
            Set<String> assetEpcSet = epcSet
                    .stream()
                    .filter(epc -> TbAssetInformation.encodeChildEpc2Epc(epc).equals(assetInformation.getEpc()))
                    .collect(Collectors.toSet());

            inventoryQuantity = assetEpcSet.size();
            // 删除已盘点epc
            epcSet.removeAll(assetEpcSet);

            if (inventoryQuantity > 0) {
                // 设置盘点更新状态：已盘点
                tbInventoryDetail.setInventoryStatus(COUNTED.getCode());
                // 设置盘点数量
                tbInventoryDetail.setInventoryQuantity(inventoryQuantity);

                if (inventoryQuantity > assetQuantity) { // 盘盈: 实际盘点数量 大于 资产账面数量
                    tbInventoryDetail.setInventoryResult(INVENTORY_PROFIT.getCode());
                } else if (inventoryQuantity == assetQuantity) { // 正常: 实际盘点数量 等于 资产账面数量
                    tbInventoryDetail.setInventoryResult(INVENTORY_NORMAL.getCode());
                } else {  // 盘亏: 实际盘点数量 小于 资产账面数量
                    tbInventoryDetail.setInventoryResult(INVENTORY_SHORTAGES.getCode());
                }
            } else {
                // 设置盘点更新状态：未盘点
                tbInventoryDetail.setInventoryStatus(UNCOUNTED.getCode());
            }

            int update = tbInventoryDetailMapper.updateTbInventoryDetail(tbInventoryDetail);
            if (update > 0) {
                if (result < 1) {
                    result = 1;
                }
            }
        }

        // 统计未盘点
        int uncountedCount = tbInventoryDetailMapper.countUncounted(orderNumber);

        if (uncountedCount > 0) { // 存在未盘点 盘亏
            tbAssetInventory.setInventoryResult(PLAN_INVENTORY_SHORTAGES.getCode());
        } else { // 不存在未盘点
            // 统计盘亏
            int inventoryShortagesCount = tbInventoryDetailMapper.countInventoryShortages(orderNumber);
            // 是否存在盘亏
            if (inventoryShortagesCount > 0) {
                tbAssetInventory.setInventoryResult(PLAN_INVENTORY_SHORTAGES.getCode());
            } else {
                // 统计盘盈
                int inventoryProfitCount = tbInventoryDetailMapper.countInventoryProfit(orderNumber);
                // 是否存在盘盈
                if (inventoryProfitCount > 0) {
                    tbAssetInventory.setInventoryResult(PLAN_INVENTORY_PROFIT.getCode());
                } else {
                    tbAssetInventory.setInventoryResult(PLAN_INVENTORY_NORMAL.getCode());
                }
            }
        }

        // 意外盘点资产EPC
        Set<String> unCountEpcSet = new HashSet<>();
        for (String epc : epcSet) {
            String unCountEpc = TbAssetInformation.encodeChildEpc2Epc(epc);
            // 存入未盘点资产epc中
            unCountEpcSet.add(unCountEpc);
        }

        // 根据意外资产epc获取资产信息
        List<TbAssetInformation> tbAssetInformationList = informationMapper.selectTbAssetInformationByEpcList(unCountEpcSet);
        for (TbAssetInformation tbAssetInformation : tbAssetInformationList) { // 盘点异常
            // 盘点意外的资产信息
            TbInventoryDetail tbInventoryDetail = TbInventoryDetail.errCounted(orderNumber, tbAssetInformation);

            Integer assetQuantity = tbAssetInformation.getQuantity();

            // 实际盘点数量统计
            int inventoryQuantity;

            // 获取该资产的所有epc
            Set<String> assetEpcSet = epcSet
                    .stream()
                    .filter(epc -> TbAssetInformation.encodeChildEpc2Epc(epc).equals(tbAssetInformation.getEpc()))
                    .collect(Collectors.toSet());

            inventoryQuantity = assetEpcSet.size();
            // 删除已盘点epc
            epcSet.removeAll(assetEpcSet);

            // 设置盘点数量
            tbInventoryDetail.setInventoryQuantity(inventoryQuantity);
            if (inventoryQuantity > assetQuantity) { // 盘盈: 实际盘点数量 大于 资产账面数量
                tbInventoryDetail.setInventoryResult(INVENTORY_PROFIT.getCode());
            } else if (inventoryQuantity == assetQuantity) { // 正常: 实际盘点数量 等于 资产账面数量
                tbInventoryDetail.setInventoryResult(INVENTORY_NORMAL.getCode());
            } else {  // 盘亏: 实际盘点数量 小于 资产账面数量
                tbInventoryDetail.setInventoryResult(INVENTORY_SHORTAGES.getCode());
            }

            tbInventoryDetailMapper.insertTbInventoryDetail(tbInventoryDetail);
        }

        // 设置盘点状态：盘点完毕
        tbAssetInventory.setInventoryStatus(PLAN_FINISH.getCode());
        tbAssetInventoryMapper.updateTbAssetInventory(tbAssetInventory);
        return result;
    }

    /**
     * 修改资产盘点记录
     *
     * @param tbAssetInventory 资产盘点记录
     * @return 结果
     */
    @Override
    public int updateTbAssetInventory(TbAssetInventory tbAssetInventory)
    {
        Integer recordStatus = tbAssetInventory.getRecordStatus();
        if (recordStatus > 0) {
            tbAssetInventory.setInventoryStatus(1);
        } else {
            tbAssetInventory.setInventoryStatus(0);
        }

        int update = tbAssetInventoryMapper.updateTbAssetInventory(tbAssetInventory);
        if (update < 1) {
            return 0;
        }

        String orderNumber = tbAssetInventory.getOrderNumber();
        tbInventoryDetailMapper.deleteTbInventoryDetailByOrderNumber(orderNumber);

        String inventoryLocation = tbAssetInventory.getInventoryLocation();
        List<TbInventoryDetail> inventoryDetails = getInventoryDetails(orderNumber, inventoryLocation);

        for (TbInventoryDetail tbInventoryDetail : inventoryDetails) {
            tbInventoryDetailMapper.insertTbInventoryDetail(tbInventoryDetail);
        }

        return 1;
    }

    private List<TbInventoryDetail> getInventoryDetails(String orderNumber, String inventoryLocation) {
        String[] locationIds = inventoryLocation.split(",");

        Set<String> locationNumberSet = new HashSet<>();
        for (String locationId : locationIds) {
            List<String> locationNumberList = locationMapper.selectChildTbLocationById(Long.valueOf(locationId));
            locationNumberSet.addAll(locationNumberList);
        }
        List<TbAssetInformation> tbAssetInformationList = informationMapper.selectAssetInformationByLocations(locationNumberSet);

        List<TbInventoryDetail> tbInventoryDetails = new ArrayList<>();
        for (TbAssetInformation tbAssetInformation : tbAssetInformationList) {
            // 未盘点
            TbInventoryDetail uncounted = TbInventoryDetail.uncounted(orderNumber, tbAssetInformation);
            tbInventoryDetails.add(uncounted);
        }

        return tbInventoryDetails;
    }

    /**
     * 批量删除资产盘点记录
     *
     * @param ids 需要删除的资产盘点记录主键
     * @return 结果
     */
    @Override
    public int deleteTbAssetInventoryByIds(Long[] ids)
    {
        List<String> orderNumbers = tbAssetInventoryMapper.selectOrderNumberByIds(List.of(ids));
        tbInventoryDetailMapper.deleteTbInventoryDetailByOrderNumbers(orderNumbers);
        return tbAssetInventoryMapper.deleteTbAssetInventoryByIds(ids);
    }

    /**
     * 删除资产盘点记录信息
     *
     * @param id 资产盘点记录主键
     * @return 结果
     */
    @Override
    public int deleteTbAssetInventoryById(Long id)
    {
        TbAssetInventory tbAssetInventory = tbAssetInventoryMapper.selectTbAssetInventoryById(id);
        if (tbAssetInventory == null) {
            return 0;
        }
        String orderNumber = tbAssetInventory.getOrderNumber();
        tbInventoryDetailMapper.deleteTbInventoryDetailByOrderNumbers(Collections.singletonList(orderNumber));
        return tbAssetInventoryMapper.deleteTbAssetInventoryById(id);
    }

    @Override
    public List<TbAssetInventoryDTO> selectTbAssetInventoryDTOList(TbAssetInventoryDTO dto) {
        List<TbAssetInventoryDTO> tbAssetInventoryDTOS = tbAssetInventoryMapper.selectTbAssetInventoryDTOList(dto);
        for (TbAssetInventoryDTO tbAssetInventoryDTO : tbAssetInventoryDTOS) {
            setLocationNameDTO(tbAssetInventoryDTO);
        }
        return tbAssetInventoryDTOS;
    }

    private void setLocationNameDTO(TbAssetInventoryDTO tbAssetInventoryDTO) {
        String inventoryLocation = tbAssetInventoryDTO.getInventoryLocation();
        String[] strLocationIds = inventoryLocation.split(",");
        List<TbLocation> tbLocations = locationMapper.selectTbLocationByIds(List.of(strLocationIds));
        String locationName = Arrays.toString(tbLocations.stream().map(TbLocation::getName).toArray());
        tbAssetInventoryDTO.setInventoryLocationName(locationName);
    }

    /**
     * 根据部门id统计最近两次盘点间的部门资产损耗情况
     * @param depId
     * @return
     */
    public List<DepAssetLossResultDto> inventoryDepAssetById(String depId){

       /* 本想这样写sql的：
        select inventory_metadata metadata
        from tb_inventory_detail
        where order_number in
            (select order_number
            from tb_asset_inventory
            where inventory_status = 2 and inventory_department = 206
            order by inventory_date desc
            limit 0,2)
        但是，语法不支持，会报错：This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
        暂时选择以下方法解决，但要连接四次数据库，很可能需要优化
        */

        //历史盘点计划编码和日期
        List<AssetInvOrderDto> invs = tbAssetInventoryMapper.getOrderNumberByDepId(depId);
        //前前一次盘点计划编码和日期
//        TbAssetInventoryDTO old_inv = tbAssetInventoryMapper.getOrderNumberByDepId(depId);

        if (ObjectUtil.isEmpty(invs) || invs.size()==0) return null;

        AssetInvOrderDto new_inv = invs.get(0);
        System.out.println("new-盘点计划："+new_inv);
        AssetInvOrderDto old_inv = null;
        if (invs.size()>1) old_inv = invs.get(1);

        DepAssetLossResultDto old_resultDto = null;
        DepAssetLossResultDto new_resultDto = null;
        if (new_inv!=null){
            new_resultDto = lossResult(new_inv);
        }
        if (!ObjectUtil.isNull(old_inv)) old_resultDto = lossResult(old_inv);

        List<DepAssetLossResultDto> results = new ArrayList<>();
        results.add(old_resultDto);
        results.add(new_resultDto);
        return results;
    }

    /**
     * 处理资产信息，统计每一样资产的损耗情况
     * @param assetInfos
     * @param assetMap
     * @param lossValMap
     */
    private void handleAssetInfo(List<TbAssetInformation> assetInfos,
                                 Map<String, DepAssetLossDto> assetMap,
                                 Map<String, Double> lossValMap){
        for (TbAssetInformation asset : assetInfos) {
            if (!assetMap.containsKey(asset.getName())){

                DepAssetLossDto lossDto = new DepAssetLossDto();
                lossDto.setAssetName(asset.getName());
                lossDto.setCategory(asset.getCategoryName());
                lossDto.setNowQuantity(asset.getQuantity());

                List<String> locations = new ArrayList<>();
                if (asset.getManageStatus()==7) {
                    lossDto.setLossQuantity(asset.getQuantity());
                    locations.add(asset.getAncestorLocationName()+asset.getParentLocationName()+asset.getLocationName());
                    lossDto.setLocations(locations);
                    lossValMap.put(asset.getName(),(asset.getOriginalValue()-asset.getNetValue())*asset.getQuantity());
                }
                assetMap.put(asset.getName(),lossDto);
            } else {
                DepAssetLossDto lossDto = assetMap.get(asset.getName());
                lossDto.setNowQuantity(lossDto.getNowQuantity()+asset.getQuantity());
                if (asset.getManageStatus()==7) {
                    lossDto.setLossQuantity(lossDto.getLossQuantity()+asset.getQuantity());
                    lossDto.getLocations()
                            .add(asset.getAncestorLocationName()+asset.getParentLocationName()+asset.getLocationName());
                    lossValMap.put(asset.getName(),
                                   Double.sum(lossValMap.get(asset.getName()),
                                           (asset.getOriginalValue()-asset.getNetValue())*asset.getQuantity()));
                }
            }

        }
    }

    /**
     * 根据盘点计划的编号查询出资产元数据，并统计损耗
     * @param invPlan
     * @return
     */
    private DepAssetLossResultDto lossResult(AssetInvOrderDto invPlan){

        List<String> asset_list = tbAssetInventoryMapper.getDepAssetByOrderNumber(invPlan.getOrderNumber());
        //盘点资产详细信息-元数据
        List<TbAssetInformation> assetInfos = new ArrayList<>();
        //json数据 --> java对象
        for (String asset : asset_list) {
            System.out.println(asset);
            assetInfos.add(JSONUtil.toBean(asset, TbAssetInformation.class));
        }

        Map<String, DepAssetLossDto> assetMap = new HashMap<>();
        //各资产损耗值map
        HashMap<String, Double> lossValMap = new HashMap<>();
        //统计两次盘点计划每一样资产（同资产名）的相关信息
        handleAssetInfo(assetInfos,assetMap,lossValMap);
        //将map中统计好的资产信息转到list
        ArrayList<DepAssetLossDto> assetResultList = new ArrayList<>();
        Set<Map.Entry<String, DepAssetLossDto>> entrySet = assetMap.entrySet();
        for (Map.Entry<String, DepAssetLossDto> entry : entrySet) {
            DepAssetLossDto dto = entry.getValue();
            if (lossValMap.containsKey(dto.getAssetName()))dto.setLossVal(lossValMap.get(dto.getAssetName()));
            assetResultList.add(dto);
        }
        //计算损耗总值
        Set<Map.Entry<String, Double>> lossValSet = lossValMap.entrySet();
        double lossVal = 0.0;
        for (Map.Entry<String, Double> entry : lossValSet) {
            lossVal+=entry.getValue();
        }

//        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        //封装结果
        DepAssetLossResultDto resultDto = new DepAssetLossResultDto();
        resultDto.setTotalLossVal(lossVal);
        resultDto.setAssetResultList(assetResultList);
        resultDto.setDate(invPlan.getInventDate());

        return resultDto;
    }


}
