
package com.tmerclub.cloud.product.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.tmerclub.cloud.api.auth.constant.SysTypeEnum;
import com.tmerclub.cloud.api.auth.feign.AccountFeignClient;
import com.tmerclub.cloud.api.auth.vo.AuthAccountVO;
import com.tmerclub.cloud.api.product.vo.SkuStockVO;
import com.tmerclub.cloud.common.order.vo.WarehouseVO;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.util.PageUtil;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.i18n.I18nMessage;
import com.tmerclub.cloud.common.product.constant.StockPointType;
import com.tmerclub.cloud.common.product.constant.TakeStockBillStatus;
import com.tmerclub.cloud.common.product.constant.TakeStockSpuStatus;
import com.tmerclub.cloud.common.product.dto.SkuDTO;
import com.tmerclub.cloud.common.product.vo.SkuVO;
import com.tmerclub.cloud.common.security.AuthUserContext;
import com.tmerclub.cloud.common.util.ExcelUtil;
import com.tmerclub.cloud.product.dto.TakeStockDTO;
import com.tmerclub.cloud.product.manager.StockManager;
import com.tmerclub.cloud.product.mapper.SpuMapper;
import com.tmerclub.cloud.product.mapper.TakeStockMapper;
import com.tmerclub.cloud.product.mapper.TakeStockSpuMapper;
import com.tmerclub.cloud.product.model.TakeStock;
import com.tmerclub.cloud.product.model.TakeStockSpu;
import com.tmerclub.cloud.product.service.*;
import com.tmerclub.cloud.product.util.StockUtil;
import com.tmerclub.cloud.product.vo.TakeStockExcelVO;
import com.tmerclub.cloud.product.vo.TakeStockSpuExcelVO;
import com.tmerclub.cloud.product.vo.TakeStockSpuImportVO;
import com.tmerclub.cloud.product.vo.TakeStockSpuVO;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 盘点商品列表
 *
 * @author lhd
 * @date 2022-06-22 16:57:27
 */
@Service
public class TakeStockSpuServiceImpl implements TakeStockSpuService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TakeStockSpuServiceImpl.class);

    @Autowired
    private TakeStockSpuMapper takeStockSpuMapper;
    @Autowired
    private TakeStockMapper takeStockMapper;
    @Autowired
    private SpuMapper spuMapper;

    @Autowired
    private SkuService skuService;
    @Autowired
    private SpuService spuService;
    @Autowired
    private StockPointSkuService stockPointSkuService;

    @Autowired
    private StockManager stockManager;

    @DubboReference
    private AccountFeignClient accountFeignClient;
    @Autowired
    private WarehouseService warehouseService;

    @Override
    public PageVO<TakeStockSpu> page(PageDTO pageDTO) {
        return PageUtil.doPage(pageDTO, () -> takeStockSpuMapper.list());
    }

    @Override
    public TakeStockSpu getByTakeStockSpuId(Long takeStockSpuId) {
        return takeStockSpuMapper.getByTakeStockSpuId(takeStockSpuId);
    }

    @Override
    public void save(TakeStockSpu takeStockSpu) {
        takeStockSpuMapper.save(takeStockSpu);
    }

    @Override
    public void update(TakeStockSpu takeStockSpu) {
        takeStockSpuMapper.update(takeStockSpu);
    }

    @Override
    public void deleteById(Long takeStockSpuId) {
        takeStockSpuMapper.deleteById(takeStockSpuId);
    }

    @Override
    public void updateByTakeStockDTO(TakeStockDTO takeStockDTO) {
        // 先删后增
        Long takeStockId = takeStockDTO.getTakeStockId();
        takeStockSpuMapper.deleteByTackStockId(takeStockId);
        if (CollectionUtils.isEmpty(takeStockDTO.getTakeStockSpuList())) {
            return;
        }
        takeStockSpuMapper.saveBatch(takeStockDTO.getTakeStockSpuList());
    }

    @Override
    public List<TakeStockSpuVO> listByTakeStockId(Long takeStockId) {
        return takeStockSpuMapper.listByTakeStockId(takeStockId, I18nMessage.getLang());
    }

    @Override
    public void downloadModel(HttpServletResponse response) {
        ExcelWriter excelWriter = null;
        try {
            excelWriter = ExcelUtil.getExcelWriterMerge(
                    response,
                    TakeStockSpuExcelVO.EXCEL_NAME,
                    TakeStockSpuExcelVO.MERGE_ROW_INDEX,
                    TakeStockSpuExcelVO.MERGE_COLUMN_INDEX
            ).build();
            // 进行写入操作
            WriteSheet sheetWriter = EasyExcel
                    .writerSheet(TakeStockSpuExcelVO.SHEET_NAME)
                    .head(TakeStockSpuExcelVO.class)
                    .build();
            excelWriter.write(Collections.emptyList(), sheetWriter);
        } catch (Exception e) {
            LOGGER.error("导出实物盘点导入模板excel失败，原因{}", e.getMessage());
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    @Override
    public void importExcel(List<TakeStockSpuExcelVO> list,
                            List<String> errorList,
                            Set<String> partyCodes,
                            Long takeStockId,
                            TakeStockSpuImportVO takeStockSpuImportVO) {
        if (checkInfo(list, takeStockId, takeStockSpuImportVO)) {
            return;
        }
        List<TakeStockSpuVO> takeStockSpuVOList = this.listByTakeStockId(takeStockId);
        if (CollectionUtil.isNotEmpty(takeStockSpuVOList)) {
            takeStockSpuImportVO.setTips("该盘点单已有商品导入，无法使用批量导入功能");
            return;
        }
        Map<String, Integer> changeStockMap = list.stream().collect(Collectors.toMap(TakeStockSpuExcelVO::getPartyCode, TakeStockSpuExcelVO::getChangeStock));
        TakeStock dbTakeStock = takeStockMapper.getByTakeStockId(takeStockId);
        // 查询sku信息
        SkuDTO skuDTO = new SkuDTO();
        skuDTO.setPartyCodes(new ArrayList<>(partyCodes));
        List<SkuVO> skuVOList = skuService.listSku(skuDTO);
        if (CollectionUtils.isEmpty(skuVOList)) {
            takeStockSpuImportVO.setTips("导入商品不存在");
            return;
        }
        Map<Long, SkuVO> skuMap = skuVOList.stream().collect(Collectors.toMap(SkuVO::getSkuId, x -> x));
        // 校验仓库是否存在这些skuId
        List<Long> skuIds = stockPointSkuService.checkStockPointContainSku(Collections.singletonList(dbTakeStock.getWarehouseId()), new ArrayList<>(skuMap.keySet()));
        if (CollectionUtils.isEmpty(skuIds)) {
            takeStockSpuImportVO.setTips("导入商品含有非当前仓库的商品");
            return;
        }
        // 查询库存
        List<SkuStockVO> skuStocks = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (Long skuId : skuIds) {
            skuStocks.add(new SkuStockVO(skuId, dbTakeStock.getWarehouseId()));
        }
        Map<String, SkuStockVO> skuStockMap = stockManager.mapSkuStockBySkuStocks(skuStocks);
        // 构建实物盘点商品对象
        List<TakeStockSpu> takeStockSpuList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        Integer ioType;
        for (Long skuId : skuIds) {
            TakeStockSpu takeStockSpu = new TakeStockSpu();
            SkuVO skuVO = skuMap.get(skuId);
            takeStockSpu.setTakeStockId(takeStockId);
            takeStockSpu.setSpuId(skuVO.getSpuId());
            takeStockSpu.setSkuId(skuVO.getSkuId());
            SkuStockVO skuStockVO = skuStockMap.get(StockUtil.skuPointKey(skuId, dbTakeStock.getWarehouseId()));
            takeStockSpu.setStocks(Objects.isNull(skuStockVO) ? 0 : skuStockVO.getStock());
            takeStockSpu.setTotalStock(changeStockMap.get(skuVO.getPartyCode()));
            if (takeStockSpu.getStocks().compareTo(takeStockSpu.getTotalStock()) == 0) {
                ioType = TakeStockSpuStatus.EQUAL.value();
            } else if (takeStockSpu.getStocks().compareTo(takeStockSpu.getTotalStock()) > 0) {
                ioType = TakeStockSpuStatus.PROFIT.value();
            } else {
                ioType = TakeStockSpuStatus.LOSS.value();
            }
            takeStockSpu.setIoType(ioType);
            takeStockSpuList.add(takeStockSpu);
        }
        // 新增数据
        if (takeStockSpuList.size() > 0) {
            takeStockSpuMapper.saveBatch(takeStockSpuList);
        }
        String tips = "检查到" + list.size() + "条商品信息，成功导入" +
                (takeStockSpuList.size()) + "条商品信息！错误" + (list.size() - takeStockSpuList.size()) + "条商品信息!" +
                (CollUtil.isNotEmpty(errorList) ? errorList.toString() : "");
        takeStockSpuImportVO.setTips(tips);
        takeStockSpuImportVO.setTakeStockSpuList(takeStockSpuList);
    }

    private boolean checkInfo(List<TakeStockSpuExcelVO> list, Long takeStockId, TakeStockSpuImportVO takeStockSpuImportVO) {
        if (CollectionUtils.isEmpty(list)) {
            takeStockSpuImportVO.setTips("导入数据为空，请检查下文件");
            return true;
        }
        TakeStock takeStock = takeStockMapper.getByTakeStockId(takeStockId);
        if (takeStock == null) {
            takeStockSpuImportVO.setTips("实物盘点不存在");
            return true;
        }
        if (Objects.equals(takeStock.getBillStatus(), TakeStockBillStatus.VOIDED.value()) ||
                Objects.equals(takeStock.getBillStatus(), TakeStockBillStatus.COMPLETED.value())) {
            takeStockSpuImportVO.setTips("当前盘点任务已完成或已作废，请返回列表页面后刷新");
            return true;
        }
        return false;
    }

    @Override
    public void exportExcel(Long takeStockId, HttpServletResponse response) {
        TakeStock dbTakeStock = takeStockMapper.getByTakeStockId(takeStockId);
        if (Objects.isNull(dbTakeStock)) {
            throw new LuckException("实物判断不存在");
        }
        // 获取excel导出数据
        TakeStockDTO takeStockDTO = new TakeStockDTO();
        takeStockDTO.setTakeStockId(takeStockId);
        takeStockDTO.setShopId(AuthUserContext.get().getTenantId());
        List<TakeStockExcelVO> takeStockExcelVOList = takeStockMapper.listTakeStockExportData(takeStockDTO, I18nMessage.getLang());
        // 获取仓库信息
        String warehouseName = "";
        if (!Objects.isNull(dbTakeStock.getWarehouseId())) {
            WarehouseVO warehouse = warehouseService.getByWarehouseId(dbTakeStock.getWarehouseId());
            warehouseName = Objects.isNull(warehouse) ? "" : warehouse.getWarehouseName();
        }
        // 制单人名称数据准备
        Map<Long, String> userIdNameMap = getUserIdNameMap(takeStockExcelVOList.stream().map(TakeStockExcelVO::getMaker));
        // 字典数据信息补充
        TakeStockBillStatus billStatusEnum;
        TakeStockSpuStatus spuStatusEnum;
        for (TakeStockExcelVO takeStockExcelVO : takeStockExcelVOList) {
            billStatusEnum = TakeStockBillStatus.instance(takeStockExcelVO.getBillStatus());
            takeStockExcelVO.setBillStatusStr(billStatusEnum == null ? "" : billStatusEnum.strCn());
            spuStatusEnum = TakeStockSpuStatus.instance(takeStockExcelVO.getIoType());
            takeStockExcelVO.setIoTypeStr(spuStatusEnum == null ? "" : spuStatusEnum.str());
            // 制单人名称
            takeStockExcelVO.setMakerName(userIdNameMap.get(takeStockExcelVO.getMaker()));
            // 仓库信息
            takeStockExcelVO.setWarehouseName(warehouseName);
            takeStockExcelVO.setStockPointTypeStr(Objects.equals(StockPointType.STATION.getValue(), dbTakeStock.getStockPointType()) ? StockPointType.STATION.getDesc() : StockPointType.WAREHOUSE.getDesc());
        }
        // 数据导出为excel
        ExcelWriter excelWriter = null;
        try {
            // 先执行合并策略
            excelWriter = ExcelUtil.getExcelWriterMerge(response, TakeStockExcelVO.EXCEL_NAME, TakeStockExcelVO.MERGE_ROW_INDEX, TakeStockExcelVO.MERGE_COLUMN_INDEX).build();
            if (CollUtil.isNotEmpty(takeStockExcelVOList)) {
                // 进行写入操作
                WriteSheet sheetWriter = EasyExcel
                        .writerSheet(TakeStockExcelVO.SHEET_NAME)
                        .head(TakeStockExcelVO.class)
                        .build();
                excelWriter.write(takeStockExcelVOList, sheetWriter);
            }
        } catch (Exception e) {
            LOGGER.error("导出excel失败{} ", e.getMessage());
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    private Map<Long, String> getUserIdNameMap(Stream<Long> takeStockList) {
        Set<Long> userIds = takeStockList.collect(Collectors.toSet());
        List<AuthAccountVO> accounts = accountFeignClient.listByUserIdsAndPhoneAndType(new ArrayList<>(userIds), null, SysTypeEnum.MULTISHOP.value()).getData();
        Map<Long, String> userIdNameMap = new HashMap<>(16);
        if (!CollectionUtils.isEmpty(accounts)) {
            userIdNameMap = accounts.stream().collect(Collectors.toMap(AuthAccountVO::getUserId, AuthAccountVO::getUsername));
        }
        return userIdNameMap;
    }
}
