package com.fjhx.service.apply.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.base.Condition;
import com.fjhx.base.StorageBaseEntity;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.apply.ApplyPurchase;
import com.fjhx.entity.water.WaterDetail;
import com.fjhx.entity.water.WaterTag;
import com.fjhx.mapper.apply.ApplyPurchaseMapper;
import com.fjhx.service.apply.ApplyPurchaseService;
import com.fjhx.service.water.WaterDetailService;
import com.fjhx.service.water.WaterTagService;
import com.fjhx.utils.BigDecimalUtil;
import com.fjhx.utils.UserClientUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/**
 * <p>
 * 物料申购 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-09-14
 */
@Service
public class ApplyPurchaseServiceImpl extends ServiceImpl<ApplyPurchaseMapper, ApplyPurchase> implements ApplyPurchaseService {

    @Autowired
    private WaterTagService waterTagService;

    @Autowired
    private WaterDetailService waterDetailService;

    @Override
    public String getCode() {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return "PB-" + DateUtil.format(new Date(), "yyMMdd-HHmmss-SSS");
    }

    @Override
    public Map<Integer, Map<String, Object>> typeStatistics(Condition condition) {

        Map<Integer, Map<String, Object>> result = new HashMap<>();

        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        String keyword = condition.getKeyword();

        // 查询条件
        QueryWrapper<Object> wrapper = Wrappers.query()
                .between("ap.create_time", beginTime, endTime)
                .and(ObjectUtil.isNotEmpty(keyword), q -> q.like("m.name", keyword).or().like("m.code", keyword));

        List<Map<String, Object>> list = baseMapper.getTypeStatisticsList(wrapper);

        Map<Integer, List<Map<String, Object>>> mapByType = list.stream()
                .collect(Collectors.groupingBy(item -> Convert.toInt(item.get("type"))));

        // 面料申购统计（0直喷 + 1热转 + 2打纸 + 4其他）
        Map<String, Object> fabricMap = null;

        for (int i = 0; i < 5; i++) {
            Map<String, Object> typeStatistics = getTypeStatistics(i, mapByType.get(i));
            result.put(i, typeStatistics);

            if (i != 3) {
                if (fabricMap == null) {
                    fabricMap = ObjectUtil.clone(typeStatistics);
                    fabricMap.put("technologyType", 5);
                } else {
                    fabricMap.put("num", Convert.toInt(fabricMap.get("num")) + Convert.toInt(typeStatistics.get("num")));
                    fabricMap.put("purchaseQty", BigDecimalUtil.add(fabricMap.get("purchaseQty"), typeStatistics.get("purchaseQty")));
                    fabricMap.put("area", BigDecimalUtil.add(fabricMap.get("area"), typeStatistics.get("area")));
                }
            }
        }
        result.put(5, fabricMap);

        return result;
    }

    @Override
    public List<Map<String, Object>> flowStatistics(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        String keyword = condition.getKeyword();
        Integer technologyType = condition.getInt("technologyType");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between("ap.create_time", beginTime, endTime)
                .and(ObjectUtil.isNotEmpty(keyword), q -> q.like("m.Code", keyword).or().like("m.Name", keyword))
                .func(ObjectUtil.isNotEmpty(technologyType), q -> {
                    if (technologyType.equals(5)) {
                        q.in("m.technology_type", 0, 1, 2, 4);
                    } else {
                        q.eq("m.technology_type", technologyType);
                    }
                })
                .groupBy("ap.flow_status");

        List<Map<String, Object>> list = baseMapper.flowStatistics(wrapper);

        Map<Integer, Long> collect = list.stream().collect(Collectors.toMap(
                item -> (Integer) item.get("flowStatus"),
                item -> (Long) item.get("count")
        ));

        Map<String, Object> approvedMap = new HashMap<>();
        approvedMap.put("key", "审批中");
        approvedMap.put("value", 1);
        approvedMap.put("count", (collect.get(0) == null ? 0 : collect.get(0)) + (collect.get(1) == null ? 0 : collect.get(1)));

        Map<String, Object> passedMap = new HashMap<>();
        passedMap.put("key", "已通过");
        passedMap.put("value", 2);
        passedMap.put("count", collect.get(2) == null ? 0 : collect.get(2));

        Map<String, Object> rejectedMap = new HashMap<>();
        rejectedMap.put("key", "已驳回");
        rejectedMap.put("value", 3);
        rejectedMap.put("count", collect.get(3) == null ? 0 : collect.get(3));

        Map<String, Object> wholeMap = new HashMap<>();
        wholeMap.put("key", "全部");
        wholeMap.put("count", (Long) approvedMap.get("count") + (Long) passedMap.get("count") + (Long) rejectedMap.get("count"));

        return Arrays.asList(wholeMap, approvedMap, rejectedMap, passedMap);
    }

    @Override
    public Page<Map<String, Object>> getPage(Condition condition) {
        String keyword = condition.getKeyword();
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Integer technologyType = condition.getInt("technologyType");
        Integer status = condition.getInt("status");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between("ap.create_time", beginTime, endTime)
                .and(ObjectUtil.isNotEmpty(keyword), q -> q.like("m.Code", keyword).or().like("m.Name", keyword))
                .func(ObjectUtil.isNotEmpty(technologyType), q -> {
                    if (technologyType.equals(5)) {
                        q.in("m.technology_type", 0, 1, 2, 4);
                    } else {
                        q.eq("m.technology_type", technologyType);
                    }
                })
                .func(ObjectUtil.isNotEmpty(status), q -> {
                    if (status.equals(1)) {
                        q.in("ap.flow_status", 0, 1);
                    } else {
                        q.eq("ap.flow_status", status);
                    }
                })
                .orderByAsc("ap.flow_status")
                .orderByDesc("ap.create_time");

        Page<Map<String, Object>> page = baseMapper.getPage(createPage(condition), wrapper);

        List<Map<String, Object>> records = page.getRecords();
        if (records.size() == 0) {
            return page;
        }

        // 获取物料id
        List<Long> materialIdList = records.stream().map(item -> Convert.toLong(item.get("materialId")))
                .distinct().collect(Collectors.toList());

        Date date = new Date();

        // 最长滞留时间
        Map<Long, Long> maximumDetentionTimeMap = getMaximumDetentionTime(date, materialIdList);

        // 滞留超过30天数量
        Map<Long, BigDecimal> moreThanOneMonthMap = getMoreThanOneMonth(date, materialIdList);

        // 近30天消耗量
        Map<Long, BigDecimal> useOneMonthMap = getUseOneMonth(date, materialIdList);

        for (Map<String, Object> record : records) {

            Long materialId = Convert.toLong(record.get("materialId"));

            // 赋值最长滞留周期
            Long maxDelayTime = maximumDetentionTimeMap.get(materialId);
            record.put("maxDelayTime", maxDelayTime == null ? 0 : maxDelayTime);

            // 赋值滞留超过30天数量
            BigDecimal delayQuantity = moreThanOneMonthMap.get(materialId);
            record.put("delayQuantity", delayQuantity == null ? BigDecimal.ZERO : delayQuantity);

            // 赋值近30天消耗量
            BigDecimal useQuantity = useOneMonthMap.get(materialId);
            record.put("useQuantity", useQuantity == null ? BigDecimal.ZERO : delayQuantity);
        }

        return page;
    }

    public Page<Map<String, Object>> getApplyPurchasePage(Condition condition) {
        String keyword = condition.getKeyword();

        LambdaQueryWrapper<ApplyPurchase> wrapper = Wrappers.<ApplyPurchase>lambdaQuery()
                .like(ObjectUtil.isNotEmpty(keyword), ApplyPurchase::getCode, keyword);

        Page<Map<String, Object>> page = pageMaps(condition, wrapper);

        List<Map<String, Object>> records = page.getRecords();
        if (records.size() == 0) {
            return page;
        }

        Map<Long, Map<String, Object>> createUser = UserClientUtil
                .getNameByFunction(records, item -> Convert.toLong(item.get("createUser")));

        for (Map<String, Object> record : records) {
            Long createUserId = Convert.toLong(record.get("createUser"));
            Map<String, Object> map = createUser.get(createUserId);
            if (map != null) {
                record.putAll(map);
            }
        }

        return page;
    }

    @Override
    public Map<String, Object> getApplyPurchaseDetailsList(Condition condition) {
        String applyPurchaseId = condition.getStr("applyPurchaseId", "申购单id不能为空");

        ApplyPurchase applyPurchase = getById(applyPurchaseId);

        Map<String, Object> result = BeanUtil.beanToMap(applyPurchase);
        result.put("purchaseDept", "采购部");

        // 获取申购部门和申购人
        Map<String, Object> userNameAndPostMap = UserClientUtil.getNameByUserId(applyPurchase.getCreateUser());
        if (userNameAndPostMap != null) {
            result.putAll(userNameAndPostMap);
        }

        List<Map<String, Object>> applyPurchaseDetailsList =
                baseMapper.getApplyPurchaseDetailsList(createPage(condition), applyPurchaseId);

        result.put("details", applyPurchaseDetailsList);

        return result;
    }

    @Override
    public Map<String, Object> amountStatistics(Condition condition) {

        QueryWrapper<Object> wrapper = getAmountWrapper(condition);

        List<Map<String, Object>> list = baseMapper.getAmountStatisticsList(wrapper);

        Map<String, Object> result = new HashMap<>();

        /*
            0 =》申购金额
            1 =》面料采购
            2 =》墨水采购
            3 =》其他
         */
        Map<String, BigDecimal> money = list.stream().collect(Collectors.toMap(
                item -> {
                    String materialType = item.get("materialType").toString();
                    switch (materialType) {
                        case "3":
                            return "2";
                        case "4":
                            return "3";
                        default:
                            return "1";
                    }
                },
                item -> ((BigDecimal) item.get("money")).setScale(2, RoundingMode.HALF_UP),
                BigDecimal::add
        ));
        money.putIfAbsent("1", BigDecimal.ZERO);
        money.putIfAbsent("2", BigDecimal.ZERO);
        money.putIfAbsent("3", BigDecimal.ZERO);
        money.put("0", money.get("1").add(money.get("2")).add(money.get("3")));

         /*
            4 =》45天账期
            5 =》30天账期
            6 =》15天账期
            7 =》预付款
         */
        Map<String, BigDecimal> collect2 = list.stream().collect(Collectors.toMap(
                item -> {
                    Integer accountDate = (Integer) item.get("accountDate");
                    switch (accountDate) {
                        case 45:
                            return "4";
                        case 30:
                            return "5";
                        case 15:
                            return "6";
                        default:
                            return "7";
                    }
                },
                item -> ((BigDecimal) item.get("money")).setScale(2, RoundingMode.HALF_UP),
                BigDecimal::add
        ));
        money.putIfAbsent("4", BigDecimal.ZERO);
        money.putIfAbsent("5", BigDecimal.ZERO);
        money.putIfAbsent("6", BigDecimal.ZERO);
        money.putIfAbsent("7", BigDecimal.ZERO);

        money.putAll(collect2);

        // 赋值全部金额查询信息
        result.put("money", money);


        // 根据供应商id，统计出向每家公司付款的金额
        List<Map<String, Object>> values = list.stream().collect(Collectors.toMap(
                        item -> item.get("supplierId").toString(),
                        item -> {
                            Map<String, Object> map = new HashMap<>();
                            map.put("supplierId", item.get("supplierId"));
                            map.put("supplierName", item.get("supplierName"));
                            map.put("money", item.get("money"));
                            return map;
                        },
                        (v1, v2) -> {
                            v1.put("money", Convert.toBigDecimal(v1.get("money")).add(Convert.toBigDecimal(v2.get("money"))));
                            return v1;
                        }
                )).values().stream()
                .sorted(Comparator.comparing((Map<String, Object> v) -> ((BigDecimal) v.get("money"))).reversed())
                .collect(Collectors.toList());

        /*
         0 全部公司统计
         1 50万以上
         2 20~50万
         3 10~20万
         4 5~20万
         5 5万以下
         */
        Map<String, List<Map<String, Object>>> statisticsSupplier = values.stream()

                .collect(Collectors.groupingBy(item -> {
                    BigDecimal amountMoney = Convert.toBigDecimal(item.get("money"));
                    if (amountMoney.compareTo(new BigDecimal("50000")) < 0) {
                        return "5";
                    }
                    if (amountMoney.compareTo(new BigDecimal("100000")) < 0) {
                        return "4";
                    }
                    if (amountMoney.compareTo(new BigDecimal("200000")) < 0) {
                        return "3";
                    }
                    if (amountMoney.compareTo(new BigDecimal("500000")) < 0) {
                        return "2";
                    }
                    return "1";
                }));

        statisticsSupplier.put("0", values);
        statisticsSupplier.putIfAbsent("1", new ArrayList<>());
        statisticsSupplier.putIfAbsent("2", new ArrayList<>());
        statisticsSupplier.putIfAbsent("3", new ArrayList<>());
        statisticsSupplier.putIfAbsent("4", new ArrayList<>());
        statisticsSupplier.putIfAbsent("5", new ArrayList<>());

        result.put("statisticsSupplier", statisticsSupplier);

        return result;
    }

    @Override
    public Page<Map<String, Object>> amountPage(Condition condition) {

        Long supplierId = condition.getLong("supplierId");

        QueryWrapper<Object> wrapper = getAmountWrapper(condition);
        wrapper.eq(ObjectUtil.isNotEmpty(supplierId), "c.supplier_id", supplierId);

        return baseMapper.amountPage(condition.getPage(), wrapper);
    }

    /**
     * 根据类型统计物料采购情况
     */
    private Map<String, Object> getTypeStatistics(int technologyType, List<Map<String, Object>> list) {

        HashMap<String, Object> map = new HashMap<>();

        int num = 0;
        BigDecimal purchaseQty = BigDecimal.ZERO;
        BigDecimal area = BigDecimal.ZERO;

        if (ObjectUtil.isNotEmpty(list)) {
            for (Map<String, Object> item : list) {

                BigDecimal quantity = Convert.toBigDecimal(item.get("quantity"));
                BigDecimal width = Convert.toBigDecimal(item.get("width"));

                num++;
                purchaseQty = purchaseQty.add(BigDecimalUtil.keepDecimals(quantity, 1));
                if (technologyType != 3) {
                    area = area.add(BigDecimalUtil.init(quantity).multiply(width).divide(100, 1).getValue());
                }

            }
        }

        // 统计
        map.put("technologyType", technologyType);
        map.put("num", num);
        map.put("purchaseQty", purchaseQty);
        if (technologyType != 3) {
            map.put("area", area);
        }

        return map;
    }

    /**
     * 获取最长滞留时间
     *
     * @param date           当前时间
     * @param materialIdList 物料id列表
     * @return map<物料id, 最长滞留时间>
     */
    private Map<Long, Long> getMaximumDetentionTime(Date date, List<Long> materialIdList) {

        List<Map<String, Object>> list = waterTagService.listMaps(Wrappers.<WaterTag>query()
                .select("min(create_time) createTime")
                .lambda()
                .select(WaterTag::getMaterialId)
                .eq(WaterTag::getInHouse, StatusConstant.YES)
                .in(WaterTag::getMaterialId, materialIdList)
                .groupBy(WaterTag::getMaterialId)
        );

        return list.stream().collect(Collectors.toMap(
                item -> Convert.toLong(item.get("materialId")),
                item -> DateUtil.betweenDay(date, Convert.toDate(item.get("createTime")), true)
        ));

    }

    /**
     * 滞留超过30天数量
     *
     * @param date           当前时间
     * @param materialIdList 物料id列表
     * @return map<物料id, 滞留超过30天数量>
     */
    private Map<Long, BigDecimal> getMoreThanOneMonth(Date date, List<Long> materialIdList) {


        List<Map<String, Object>> list = waterTagService.listMaps(Wrappers.<WaterTag>query()
                .select("sum(quantity) delayQuantity")
                .lambda()
                .select(WaterTag::getMaterialId)
                .eq(WaterTag::getInHouse, StatusConstant.YES)
                .le(StorageBaseEntity::getCreateTime, DateUtil.offsetDay(date, -30))
                .in(WaterTag::getMaterialId, materialIdList)
                .groupBy(WaterTag::getMaterialId)
        );

        return list.stream().collect(Collectors.toMap(
                item -> Convert.toLong(item.get("materialId")),
                item -> Convert.toBigDecimal(item.get("delayQuantity"))
        ));

    }

    /**
     * 近30天使用量
     *
     * @param date           当前时间
     * @param materialIdList 物料id列表
     * @return map<物料id, 滞留超过30天数量>
     */
    private Map<Long, BigDecimal> getUseOneMonth(Date date, List<Long> materialIdList) {

        List<Map<String, Object>> list = waterDetailService.listMaps(Wrappers.<WaterDetail>query()
                .select("sum(quantity) useQuantity")
                .lambda()
                .select(WaterDetail::getMaterialId)
                .ge(StorageBaseEntity::getCreateTime, DateUtil.offsetDay(date, -30))
                .in(WaterDetail::getType, 20, 23)
                .in(WaterDetail::getMaterialId, materialIdList)
                .groupBy(WaterDetail::getMaterialId)
        );

        return list.stream().collect(Collectors.toMap(
                item -> Convert.toLong(item.get("materialId")),
                item -> Convert.toBigDecimal(item.get("useQuantity"))
        ));

    }

    private QueryWrapper<Object> getAmountWrapper(Condition condition) {
        QueryWrapper<Object> wrapper = Wrappers.query();

        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();

        wrapper.between(ObjectUtil.isAllNotEmpty(beginTime, endTime), "c.approval_time", beginTime, endTime);
        wrapper.eq("c.del_flag", StatusConstant.NOT_DELETED);
        wrapper.in("c.account_date", 45, 30, 15, 0);

        return wrapper;
    }

}
