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

import com.tmerclub.cloud.common.constant.PurchaseAmountType;
import com.tmerclub.cloud.common.constant.SysTypeEnum;
import com.tmerclub.cloud.common.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.mongodb.util.MongoPageUtil;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.ExcelUtil;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.multishop.model.ShopDetail;
import com.tmerclub.cloud.multishop.service.ShopDetailService;
import com.tmerclub.cloud.supplier.dto.PurchaseAmountLogDTO;
import com.tmerclub.cloud.supplier.model.SupplierDetail;
import com.tmerclub.cloud.supplier.mongo.MongoPurchaseAmountLogBO;
import com.tmerclub.cloud.supplier.service.PurchaseAmountLogService;
import com.tmerclub.cloud.supplier.service.SupplierDetailService;
import com.tmerclub.cloud.supplier.vo.PurchaseAmountLogShopExcelVO;
import com.tmerclub.cloud.supplier.vo.PurchaseAmountLogSupplierExcelVO;
import com.tmerclub.cloud.supplier.vo.ShopSupplierNameVO;
import com.tmerclub.cloud.supplier.vo.TotalPurchaseAmountVO;
import com.tmerclub.cloud.supplier.vo.mongo.MongoPurchaseAmountLogVO;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author gaozijie
 * @date 2023-08-28
 */
@Service
public class PurchaseAmountLogServiceImpl implements PurchaseAmountLogService {

    private static final Logger logger = LoggerFactory.getLogger(PurchaseAmountLogServiceImpl.class);

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private MongoPageUtil mongoPageUtil;

    @Autowired
    private ShopDetailService shopDetailService;
    @Autowired
    private SupplierDetailService supplierDetailService;

    @Override
    public List<ShopSupplierNameVO> listSupplier(Long shopId) {
        // 查询供应商id
        Query query = new Query(Criteria.where("shopId").is(shopId));
        logger.info("listSupplier()的mongodb查询语句：{}", query);
        List<Long> supplierIds = mongoTemplate.findDistinct(query, "supplierId", MongoPurchaseAmountLogBO.class, Long.class);
        if (CollectionUtils.isEmpty(supplierIds)) {
            return new ArrayList<>();
        }
        // 查询供应商名称
        List<SupplierDetail> suppliers = supplierDetailService.listBySupplierIds(supplierIds);
        if (CollectionUtils.isEmpty(suppliers)) {
            return new ArrayList<>();
        }
        return suppliers.stream().map(supplier -> {
            ShopSupplierNameVO nameVO = new ShopSupplierNameVO();
            nameVO.setSupplierId(supplier.getSupplierId());
            nameVO.setSupplierName(supplier.getSupplierName());
            return nameVO;
        }).toList();
    }

    @Override
    public List<ShopSupplierNameVO> listShop(Long supplierId) {
        // 查询商家id
        Query query = new Query(Criteria.where("supplierId").is(supplierId));
        logger.info("listShop()的mongodb查询语句：{}", query);
        List<Long> shopIds = mongoTemplate.findDistinct(query, "shopId", MongoPurchaseAmountLogBO.class, Long.class);
        if (CollectionUtils.isEmpty(shopIds)) {
            return new ArrayList<>();
        }
        // 查询商家名称
        List<ShopDetail> shops = shopDetailService.listByShopIds(shopIds);
        if (CollectionUtils.isEmpty(shops)) {
            return new ArrayList<>();
        }
        return shops.stream().map(shop -> {
            ShopSupplierNameVO nameVO = new ShopSupplierNameVO();
            nameVO.setShopId(shop.getShopId());
            nameVO.setShopName(shop.getShopName());
            return nameVO;
        }).toList();
    }

    @Override
    public PageVO<MongoPurchaseAmountLogVO> pagePurchaseAmountLog(PageDTO pageDTO,
                                                                  PurchaseAmountLogDTO purchaseAmountLogDTO) {
        // 分页查询日志
        Criteria criteria = this.buildCriteria(purchaseAmountLogDTO);
        Query query = new Query(criteria).with(Sort.by(Sort.Direction.DESC, "createTime"));
        PageVO<MongoPurchaseAmountLogBO> boPage = mongoPageUtil.doPage(MongoPurchaseAmountLogBO.class, pageDTO, query);
        PageVO<MongoPurchaseAmountLogVO> voPage = new PageVO<>();
        voPage.setPages(boPage.getPages());
        voPage.setTotal(boPage.getTotal());
        if (CollectionUtils.isEmpty(boPage.getList())) {
            voPage.setList(new ArrayList<>());
            return voPage;
        }
        voPage.setList(BeanUtil.mapAsList(boPage.getList(), MongoPurchaseAmountLogVO.class));
        // 补充商家or供应商名称
        this.fillShopOrSupplierName(purchaseAmountLogDTO.getSysType(), voPage.getList());
        return voPage;
    }

    @Override
    public void savePurchaseLog(MongoPurchaseAmountLogBO mongoPurchaseAmountLogBO) {
        logger.info("添加采购金额日志到mongodb{}", Json.toJsonString(mongoPurchaseAmountLogBO));
        mongoTemplate.insert(mongoPurchaseAmountLogBO);
    }

    @Override
    public TotalPurchaseAmountVO getTotalPurchaseAmount(PurchaseAmountLogDTO purchaseAmountLogDTO) {
        // group聚合统计数据
        Criteria criteria = this.buildCriteria(purchaseAmountLogDTO);
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("purchaseAmountType")
                        .first("purchaseAmountType").as("purchaseAmountType")
                        .sum("changeAmount").as("changeAmount")
        );
        logger.info("getTotalPurchaseAmount()的mongodb查询语句：{}", aggregation);
        List<MongoPurchaseAmountLogVO> purchaseAmountLogs = mongoTemplate.aggregate(aggregation, MongoPurchaseAmountLogBO.class, MongoPurchaseAmountLogVO.class).getMappedResults();
        // 构建VO类
        TotalPurchaseAmountVO amountVO = new TotalPurchaseAmountVO();
        amountVO.setTotalPurchaseAmount(0L);
        amountVO.setTotalCancelAmount(0L);
        for (MongoPurchaseAmountLogVO purchaseAmountLog : purchaseAmountLogs) {
            if (Objects.equals(purchaseAmountLog.getPurchaseAmountType(), PurchaseAmountType.IN_OR_OUT.getValue())) {
                amountVO.setTotalPurchaseAmount(purchaseAmountLog.getChangeAmount());
            } else if (Objects.equals(purchaseAmountLog.getPurchaseAmountType(), PurchaseAmountType.CANCEL.getValue())) {
                amountVO.setTotalCancelAmount(purchaseAmountLog.getChangeAmount());
            }
        }
        return amountVO;
    }

    @Override
    public void exportExcel(HttpServletResponse response, PurchaseAmountLogDTO purchaseAmountLogDTO) {
        // 查询日志
        Criteria criteria = this.buildCriteria(purchaseAmountLogDTO);
        Query query = new Query(criteria).with(Sort.by(Sort.Direction.DESC, "createTime"));
        logger.info("exportExcel()的mongodb查询语句：{}", query);
        List<MongoPurchaseAmountLogBO> amountLogBOList = mongoTemplate.find(query, MongoPurchaseAmountLogBO.class);
        List<MongoPurchaseAmountLogVO> amountLogVOList = BeanUtil.mapAsList(amountLogBOList, MongoPurchaseAmountLogVO.class);
        // 补充商家or供应商名称
        this.fillShopOrSupplierName(purchaseAmountLogDTO.getSysType(), amountLogVOList);
        // 导出excel
        boolean isShop = Objects.equals(purchaseAmountLogDTO.getSysType(), SysTypeEnum.MULTISHOP.value());
        int seq = 1;
        if (isShop) {
            logger.info("导出商家采购金额日志");
            List<PurchaseAmountLogShopExcelVO> excels = new ArrayList<>(amountLogVOList.size());
            for (MongoPurchaseAmountLogVO amountLog : amountLogVOList) {
                excels.add(this.buildShopExcel(amountLog, seq++));
            }
            ExcelUtil.soleExcel(response, excels, PurchaseAmountLogShopExcelVO.EXCEL_NAME,
                    PurchaseAmountLogShopExcelVO.MERGE_ROW_INDEX, PurchaseAmountLogShopExcelVO.MERGE_COLUMN_INDEX, PurchaseAmountLogShopExcelVO.class);
        } else {
            logger.info("导出供应商采购金额日志");
            List<PurchaseAmountLogSupplierExcelVO> excels = new ArrayList<>(amountLogVOList.size());
            for (MongoPurchaseAmountLogVO amountLog : amountLogVOList) {
                excels.add(this.buildSupplierExcel(amountLog, seq++));
            }
            ExcelUtil.soleExcel(response, excels, PurchaseAmountLogSupplierExcelVO.EXCEL_NAME,
                    PurchaseAmountLogSupplierExcelVO.MERGE_ROW_INDEX, PurchaseAmountLogSupplierExcelVO.MERGE_COLUMN_INDEX, PurchaseAmountLogSupplierExcelVO.class);
        }
    }

    /**
     * 补充商家/供应商名称
     * @param sysType 系统类型
     * @param amountLogs 采购金额日志
     */
    private void fillShopOrSupplierName(Integer sysType, List<MongoPurchaseAmountLogVO> amountLogs) {
        if (CollectionUtils.isEmpty(amountLogs)) {
            return;
        }
        boolean isShop = Objects.equals(sysType, SysTypeEnum.MULTISHOP.value());
        if (isShop) {
            // 查询供应商信息，补充供应商名称
            logger.info("采购金额日志补充供应商信息");
            Set<Long> supplierIds = amountLogs.stream().map(MongoPurchaseAmountLogVO::getSupplierId).collect(Collectors.toSet());
            List<SupplierDetail> suppliers = supplierDetailService.listBySupplierIds(new ArrayList<>(supplierIds));
            Map<Long, String> supplierMap = suppliers.stream().collect(Collectors.toMap(SupplierDetail::getSupplierId, SupplierDetail::getSupplierName));
            for (MongoPurchaseAmountLogVO amountLog : amountLogs) {
                amountLog.setSupplierName(supplierMap.get(amountLog.getSupplierId()));
            }
        } else {
            // 查询商家信息，补充商家名称
            logger.info("采购金额日志补充商家信息");
            Set<Long> shopIds = amountLogs.stream().map(MongoPurchaseAmountLogVO::getShopId).collect(Collectors.toSet());
            List<ShopDetail> shops = shopDetailService.listByShopIds(new ArrayList<>(shopIds));
            Map<Long, String> shopMap = shops.stream().collect(Collectors.toMap(ShopDetail::getShopId, ShopDetail::getShopName));
            for (MongoPurchaseAmountLogVO amountLog : amountLogs) {
                amountLog.setShopName(shopMap.get(amountLog.getShopId()));
            }
        }
    }

    /**
     * 构建mongo匹配条件
     * @param purchaseAmountLogDTO 查询参数
     * @return 匹配条件
     */
    private Criteria buildCriteria(PurchaseAmountLogDTO purchaseAmountLogDTO) {
        Criteria criteria = new Criteria();
        if (Objects.nonNull(purchaseAmountLogDTO.getShopId())) {
            criteria.and("shopId").is(purchaseAmountLogDTO.getShopId());
        }
        if (Objects.nonNull(purchaseAmountLogDTO.getSupplierId())) {
            criteria.and("supplierId").is(purchaseAmountLogDTO.getSupplierId());
        }
        if (Objects.nonNull(purchaseAmountLogDTO.getPurchaseOrderId())) {
            criteria.and("purchaseOrderId").is(purchaseAmountLogDTO.getPurchaseOrderId());
        }
        if (Objects.nonNull(purchaseAmountLogDTO.getStartTime()) && Objects.nonNull(purchaseAmountLogDTO.getEndTime())) {
            criteria.and("createTime").gte(purchaseAmountLogDTO.getStartTime()).lte(purchaseAmountLogDTO.getEndTime());
        }
        return criteria;
    }

    /**
     * 构建采购金额日志商家导出excel
     * @param amountLog 采购金额日志
     * @param seq 序号
     * @return 商家导出excel对象
     */
    private PurchaseAmountLogShopExcelVO buildShopExcel(MongoPurchaseAmountLogVO amountLog, int seq) {
        PurchaseAmountLogShopExcelVO shopExcel = new PurchaseAmountLogShopExcelVO();
        BeanUtils.copyProperties(amountLog, shopExcel);
        shopExcel.setSeq(String.valueOf(seq));
        PurchaseAmountType amountType = PurchaseAmountType.instance(amountLog.getPurchaseAmountType());
        shopExcel.setPurchaseAmountType(Objects.isNull(amountType) ? "" : amountType.getShopDesc());
        shopExcel.setPurchaseOrderId(String.valueOf(amountLog.getPurchaseOrderId()));
        shopExcel.setChangeAmount(this.centToYuan(amountLog.getChangeAmount()));
        shopExcel.setPurchaseTotalAmount(this.centToYuan(amountLog.getPurchaseTotalAmount()));
        return shopExcel;
    }

    /**
     * 构建采购金额日志供应商导出excel
     * @param amountLog 采购金额日志
     * @param seq 序号
     * @return 供应商导出excel对象
     */
    private PurchaseAmountLogSupplierExcelVO buildSupplierExcel(MongoPurchaseAmountLogVO amountLog, int seq) {
        PurchaseAmountLogSupplierExcelVO supplierExcel = new PurchaseAmountLogSupplierExcelVO();
        BeanUtils.copyProperties(amountLog, supplierExcel);
        supplierExcel.setSeq(String.valueOf(seq));
        PurchaseAmountType amountType = PurchaseAmountType.instance(amountLog.getPurchaseAmountType());
        supplierExcel.setPurchaseAmountType(Objects.isNull(amountType) ? "" : amountType.getSupplierDesc());
        supplierExcel.setPurchaseOrderId(String.valueOf(amountLog.getPurchaseOrderId()));
        supplierExcel.setChangeAmount(this.centToYuan(amountLog.getChangeAmount()));
        supplierExcel.setPurchaseTotalAmount(this.centToYuan(amountLog.getPurchaseTotalAmount()));
        return supplierExcel;
    }

    /**
     * 分转元
     * @param amount 金额
     * @return
     */
    private BigDecimal centToYuan(Long amount) {
        return BigDecimal.valueOf(amount).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
    }
}
