package com.red.circle.wallet.infra.database.rds.service.diamond.impl;


import com.google.common.collect.Maps;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.infra.database.rds.dao.SalaryDiamondBalanceDAO;
import com.red.circle.wallet.infra.database.rds.entity.diamond.DiamondBalance;
import com.red.circle.wallet.infra.database.rds.entity.diamond.SalaryDiamondBalance;
import com.red.circle.wallet.infra.database.rds.service.diamond.SalaryDiamondBalanceService;
import com.red.circle.wallet.inner.model.cmd.UserSalaryDiamondBalanceQryCmd;
import com.red.circle.wallet.inner.model.cmd.UserSalaryDiamondCmd;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 用户工资钻石余额 服务实现类.
 * </p>
 *
 * @author zongpubin on 2024-03-13 10:56
 */
@Service
@RequiredArgsConstructor
public class SalaryDiamondBalanceServiceImpl extends
        BaseServiceImpl<SalaryDiamondBalanceDAO, SalaryDiamondBalance> implements
        SalaryDiamondBalanceService {

    @Override
    public PageResult<SalaryDiamondBalance> listUserSalaryDiamondBalance(
            UserSalaryDiamondBalanceQryCmd query) {
        return query()
                .eq(Objects.nonNull(query.getUserId()), SalaryDiamondBalance::getUserId,
                        query.getUserId())
                .eq(StringUtils.isNotBlank(query.getSysOrigin()), SalaryDiamondBalance::getSysOrigin,
                        query.getSysOrigin())
                .orderByDesc(SalaryDiamondBalance::getCreateTime)
                .page(query.getPageQuery());
    }

    @Override
    public boolean exists(Long userId) {
        return Optional.ofNullable(
                        query().eq(SalaryDiamondBalance::getUserId, userId).last(PageConstant.LIMIT_ONE).getOne())
                .map(salaryDiamondBalance -> Objects.nonNull(salaryDiamondBalance.getId()))
                .orElse(Boolean.FALSE);
    }

    @Override
    public boolean incr(UserSalaryDiamondCmd param) {
        if (exists(param.getUserId())) {
            return update()
                    .setSql("earn_points=earn_points+" + param.getQuantity())
                    .setSql("balance=balance+" + param.getQuantity())
                    .set(SalaryDiamondBalance::getUpdateUser, param.getOperationUserId())
                    .set(SalaryDiamondBalance::getUpdateTime, TimestampUtils.now())
                    .eq(SalaryDiamondBalance::getUserId, param.getUserId())
                    .last(PageConstant.LIMIT_ONE)
                    .execute();
        }
        SalaryDiamondBalance salaryDiamondBalance = new SalaryDiamondBalance();
        salaryDiamondBalance.setId(IdWorkerUtils.getId());
        salaryDiamondBalance.setUserId(param.getUserId());
        salaryDiamondBalance.setSysOrigin(param.getSysOrigin());
        salaryDiamondBalance.setEarnPoints(param.getQuantity());
        salaryDiamondBalance.setConsumptionPoints(BigDecimal.ZERO);
        salaryDiamondBalance.setBalance(param.getQuantity());
        salaryDiamondBalance.setCreateTime(TimestampUtils.now());
        salaryDiamondBalance.setUpdateTime(TimestampUtils.now());
        return save(salaryDiamondBalance);
    }

    @Override
    public SalaryDiamondBalance getBalance(Long userId) {
        return query().eq(SalaryDiamondBalance::getUserId, userId).getOne();
    }

    @Override
    public SalaryDiamondBalance decr(UserSalaryDiamondCmd param) {

        if (exists(param.getUserId())) {
            update()
                    .setSql("consumption_points=consumption_points+" + param.getQuantity())
                    .setSql("balance=balance-" + param.getQuantity())
                    .set(SalaryDiamondBalance::getUpdateUser, param.getOperationUserId())
                    .set(SalaryDiamondBalance::getUpdateTime, TimestampUtils.now())
                    .eq(SalaryDiamondBalance::getUserId, param.getUserId())
                    .last(PageConstant.LIMIT_ONE)
                    .execute();
            return getBalance(param.getUserId());
        }
        SalaryDiamondBalance salaryDiamondBalance = new SalaryDiamondBalance()
                .setSysOrigin(param.getSysOrigin())
                .setConsumptionPoints(param.getQuantity())
                .setUserId(param.getUserId())
                .setBalance(param.getQuantity().negate());
        salaryDiamondBalance.setCreateUser(param.getOperationUserId());
        salaryDiamondBalance.setCreateTime(TimestampUtils.now());
        save(salaryDiamondBalance);
        return getBalance(param.getUserId());
    }

    @Override
    public SalaryDiamondBalance decr(Long userId, PennyAmount amount) {
        if (exists(userId)) {
            update()
                    .setSql("consumption_points=consumption_points+" + amount.getDollarAmount())
                    .setSql("balance=balance-" + amount.getDollarAmount())
                    .set(SalaryDiamondBalance::getUpdateUser, userId)
                    .set(SalaryDiamondBalance::getUpdateTime, TimestampUtils.now())
                    .eq(SalaryDiamondBalance::getUserId, userId)
                    .last(PageConstant.LIMIT_ONE)
                    .execute();
        }
        return getBalance(userId);
    }

    @Override
    public Map<Long, BigDecimal> mapBalance(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return Maps.newHashMap();
        }
        return Optional.ofNullable(
                        query().in(SalaryDiamondBalance::getUserId, userIds.stream().collect(Collectors.toSet())).list())
                .map(diamondBalances -> diamondBalances.stream()
                        .collect(Collectors.toMap(SalaryDiamondBalance::getUserId, SalaryDiamondBalance::getBalance)))
                .orElseGet(Maps::newHashMap);
    }

    @Override
    public Map<Long, BigDecimal> mapBalanceBySysOrigin(String sysOrigin) {
        if (StringUtils.isBlank(sysOrigin)) {
            return Maps.newHashMap();
        }
        return Optional.ofNullable(
                        query().eq(SalaryDiamondBalance::getSysOrigin, sysOrigin).list())
                .map(diamondBalances -> diamondBalances.stream()
                        .collect(Collectors.toMap(SalaryDiamondBalance::getUserId, SalaryDiamondBalance::getBalance)))
                .orElseGet(Maps::newHashMap);
    }
}
