package com.zhongshu.card.server.core.dao.payment.impl;

import com.github.microservice.components.data.mongo.mongo.helper.DBHelper;
import com.zhongshu.card.client.model.payment.ExpenseFlowCount;
import com.zhongshu.card.client.model.payment.ExpenseFlowSearch;
import com.zhongshu.card.server.core.dao.BaseImpl;
import com.zhongshu.card.server.core.dao.payment.extend.ExpenseFlowDaoExtend;
import com.zhongshu.card.server.core.domain.payment.ExpenseFlow;
import com.zhongshu.card.server.core.util.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Author TRX
 * @CreateDate: 2023/4/12
 * @Version: 1.0
 */
@Slf4j
public class ExpenseFlowDaoImpl extends BaseImpl implements ExpenseFlowDaoExtend {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private DBHelper dbHelper;

    private Criteria buildSearcCriteria(ExpenseFlowSearch param) {
        Criteria criteria = buildCriteriaNotOid(param);

        if (StringUtils.isNotEmpty(param.getUserId())) {
            criteria.and("userId").is(param.getUserId());
        }

        // 年
        if (param.getYear() != null) {
            criteria.and("year").is(param.getYear());
        }
        // 月
        if (param.getMonth() != null) {
            criteria.and("month").is(param.getMonth());
        }
        // 周
        if (param.getWeek() != null) {
            criteria.and("week").is(param.getWeek());
        }

        if (param.getDayOfMonth() != null) {
            criteria.and("dayOfMonth").is(param.getDayOfMonth());
        }

        if (param.getDayOfYear() != null) {
            criteria.and("dayOfYear").is(param.getDayOfYear());
        }

        if (param.getHourOfDay() != null) {
            criteria.and("hourOfDay").is(param.getHourOfDay());
        }

        // 是否支付成功
        if (param.getIsPaySuccess() != null) {
            criteria.and("isPaySuccess").is(param.getIsPaySuccess());
        }

        // 项目
        if (StringUtils.isNotEmpty(param.getProjectOid())) {
            criteria.and("projectOid").is(param.getProjectOid());
        }

        // 学校
        if (StringUtils.isNotEmpty(param.getSchoolOid())) {
            criteria.and("schoolOid").is(param.getSchoolOid());
        }

        // 商户
        if (StringUtils.isNotEmpty(param.getShopOid())) {
            criteria.and("shopOid").is(param.getShopOid());
        }

        // 商户集合
        if (ObjectUtils.isNotEmpty(param.getShopOids())) {
            criteria.and("shopOid").in(param.getShopOids());
        }

        // 退款状态
        if (param.getRefundState() != null) {
            criteria.and("refundState").is(param.getRefundState());
        }

        // 订单类型 支付、查询余额...
        if (param.getOrderModeType() != null) {
            criteria.and("orderModeType").is(param.getOrderModeType());
        }

        // 是否退款
        if (param.getIsRefund() != null) {
            criteria.and("isRefund").is(param.getIsRefund());
        }

        // 订单状态
        if (ObjectUtils.isNotEmpty(param.getOrderTypes())) {
            criteria.and("orderTypes").in(param.getOrderTypes());
        }
        // 支付方式
        if (param.getPaymentType() != null) {
            criteria.and("paymentType").is(param.getPaymentType());
        }

        // 模糊搜索
        List<Criteria> criterias = new ArrayList<>();
        if (StringUtils.isNotEmpty(param.getOrderNo())) {
            Pattern pattern = Pattern.compile("^.*" + param.getOrderNo() + ".*$");
            criterias.add(Criteria.where("orderNo").is(pattern));
        }
        if (StringUtils.isNotEmpty(param.getPaymentNo())) {
            Pattern pattern = Pattern.compile("^.*" + param.getPaymentNo() + ".*$");
            criterias.add(Criteria.where("paymentNo").is(pattern));
        }

        if (!CollectionUtils.isEmpty(criterias)) {
            criteria.andOperator(criterias.toArray(new Criteria[]{}));
        }
        return criteria;
    }

    /**
     * 查询订单列表
     *
     * @param pageable
     * @param param
     * @return
     */
    @Override
    public Page<ExpenseFlow> page(Pageable pageable, ExpenseFlowSearch param) {
        Criteria criteria = buildSearcCriteria(param);
        Sort sort = buildSort(param);
        Query query = Query.query(criteria);
        query.with(sort);
        return dbHelper.pages(query, pageable, ExpenseFlow.class);
    }

    @Override
    public long countExpenseFlows(ExpenseFlowSearch param) {
        Criteria criteria = buildSearcCriteria(param);
        Query query = Query.query(criteria);
        return mongoTemplate.count(query, ExpenseFlow.class);
    }

    /**
     * 统计 用户的消费金额
     *
     * @param param
     * @return
     */
    @Override
    public ExpenseFlowCount countUserPayment(ExpenseFlowSearch param) {
        ExpenseFlowCount expenseFlowCount = new ExpenseFlowCount();
        Criteria criteria = buildSearcCriteria(param);

        List<AggregationOperation> operations = new ArrayList<>(2);
        AggregationOperation match = Aggregation.match(criteria);
        operations.add(match);

        AggregationOperation group = Aggregation.group("$userId").sum("$payAmount").as("count");
        operations.add(group);

        BigDecimal number = BigDecimal.ZERO;
        Aggregation groupAggregation = Aggregation.newAggregation(operations);
        List<Map> countObjs = mongoTemplate.aggregate(groupAggregation, ExpenseFlow.class, Map.class).getMappedResults();
        if (countObjs.size() > 0) {
            Map map = countObjs.get(0);
            Object obj = map.get("count");
            number = CommonUtil.getBigDecimalByObj(obj);
        }
        expenseFlowCount.setPayAmount(number);
        return expenseFlowCount;
    }

    /**
     * 分组统计
     *
     * @param param
     * @return
     */
    public Map<String, BigDecimal> statisticItems(ExpenseFlowSearch param) {
        Map<String, BigDecimal> map = new HashMap<>();
        Criteria criteria = buildSearcCriteria(param);

        String fieldName = param.getFieldName();

        List<AggregationOperation> operations = new ArrayList<>(3);
        // 条件
        AggregationOperation match = Aggregation.match(criteria);
        operations.add(match);

        // 分组
        AggregationOperation group = Aggregation.group("$" + fieldName).sum("$payAmount").as("count");
        operations.add(group);

        // 排序
        AggregationOperation sort = Aggregation.sort(Sort.Direction.ASC, fieldName);
        operations.add(sort);

        Aggregation groupAggregation = Aggregation.newAggregation(operations);
        List<Map> countObjs = mongoTemplate.aggregate(groupAggregation, ExpenseFlow.class, Map.class).getMappedResults();
        if (countObjs.size() > 0) {
            countObjs.stream().forEach(it -> {
                String name = (String) it.get(fieldName);
                if (StringUtils.isEmpty(name)) {
                    Object _id = it.get("_id");
                    name = String.valueOf(_id);
                }
                BigDecimal amount = CommonUtil.getBigDecimalByObj(it.get("count"));
                // 单位分，转为 元
                amount = amount.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
                map.put(name, amount);
            });
        }
        return map;
    }

    /**
     * 商户收入总数
     *
     * @param param
     * @return
     */
    @Override
    public ExpenseFlowCount countBuinessMainPayment(ExpenseFlowSearch param) {
        ExpenseFlowCount expenseFlowCount = new ExpenseFlowCount();
        Criteria criteria = buildSearcCriteria(param);

        List<AggregationOperation> operations = new ArrayList<>(2);
        AggregationOperation match = Aggregation.match(criteria);
        operations.add(match);

        AggregationOperation group = Aggregation.group("$shopOid").sum("$payAmount").as("count");
        operations.add(group);

        BigDecimal number = BigDecimal.ZERO;
        Aggregation groupAggregation = Aggregation.newAggregation(operations);
        List<Map> countObjs = mongoTemplate.aggregate(groupAggregation, ExpenseFlow.class, Map.class).getMappedResults();
        if (countObjs.size() > 0) {
            List<BigDecimal> numbers = countObjs.stream().map(it -> {
                Object obj = it.get("count");
                return CommonUtil.getBigDecimalByObj(obj);
            }).collect(Collectors.toList());
            number = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
            // 单位分，转为 元
            number = number.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
        }
        // 总金额
        expenseFlowCount.setPayAmount(number);
        // 数据条数
        expenseFlowCount.setTotal(countExpenseFlows(param));
        return expenseFlowCount;
    }


    public ExpenseFlow init(String mqttDataId, String token) {
        ExpenseFlow doc = null;
        try {
            Query query = Query.query(Criteria.where("mqttDataId").is(mqttDataId).and("token").isNull());
            Update update = new Update()
                    .set("token", token)
                    .set("createTime", System.currentTimeMillis());
            FindAndModifyOptions options = new FindAndModifyOptions().upsert(true)
                    .returnNew(true);
            doc = mongoTemplate.findAndModify(query, update, options,
                    ExpenseFlow.class);
        } catch (Exception e) {
            log.error("init {}", e.getMessage());
        }
        return doc;
    }

}
