package com.red.circle.wallet.app.command.salaryDiamond.query;

import com.google.common.collect.Lists;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.parse.DataTypeUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.wallet.app.convertor.UserBankAppConvertor;
import com.red.circle.wallet.app.dto.clientobject.UserBankWaterCO;
import com.red.circle.wallet.app.dto.clientobject.UserBankWithdrawMoneyApplyCO;
import com.red.circle.wallet.app.dto.cmd.SalaryDiamondRunningWaterQueryCmd;
import com.red.circle.wallet.infra.database.mongo.entity.bank.UserBankWithdrawGoldApply;
import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankRunningWaterService;
import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankWithdrawGoldApplyService;
import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankWithdrawMoneyApplyService;
import com.red.circle.wallet.infra.database.rds.entity.diamond.SalaryDiamondRunningWater;
import com.red.circle.wallet.infra.database.rds.service.diamond.SalaryDiamondRunningWaterService;
import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent;
import com.red.circle.wallet.inner.model.enums.UserSalaryDiamondWaterEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 用户钻石工资流水查询.
 *
 * @author xiaofei on 2024/4/23
 */
@Component
@RequiredArgsConstructor
public class UserSalaryDiamondRunningWaterQryExe {

    private final SalaryDiamondRunningWaterService salaryDiamondRunningWaterService;
    private final UserProfileClient userProfileClient;
    private final UserBankAppConvertor userBankAppConvertor;
    private final UserBankRunningWaterService userBankRunningWaterService;
    private final UserBankWithdrawGoldApplyService userBankWithdrawGoldApplyService;
    private final UserBankWithdrawMoneyApplyService userBankWithdrawMoneyApplyService;

    public List<UserBankWaterCO> execute(SalaryDiamondRunningWaterQueryCmd cmd) {
        List<SalaryDiamondRunningWater> waters = salaryDiamondRunningWaterService.appPage(cmd.requiredReqUserId(), 20, cmd.getLastId());

        if (CollectionUtils.isEmpty(waters)) {
            return Lists.newArrayList();
        }

        Map<Long, UserBankWithdrawGoldApply> goldApplyMap = userBankWithdrawGoldApplyService.mapByIds(
                waters.stream().filter(apply -> Objects.equals(apply.getSalaryEvent(),
                                UserBankWaterEvent.EXCHANGE_GOLD_COINS.name()))
                        .map(SalaryDiamondRunningWater::getTrackId)
                        .filter(StringUtils::isNotBlank)
                        .map(DataTypeUtils::toLong)
                        .filter(Objects::nonNull)
                        .collect(Collectors.toSet())
        );

        Map<Long, UserBankWithdrawMoneyApplyCO> moneyApplyMap = userBankAppConvertor.toMapUserBankWithdrawMoneyApplyCO(
                userBankWithdrawMoneyApplyService.mapByIds(
                        waters.stream().filter(apply -> Objects.equals(apply.getSalaryEvent(),
                                        UserBankWaterEvent.WITHDRAW.name()))
                                .map(SalaryDiamondRunningWater::getTrackId)
                                .filter(StringUtils::isNotBlank)
                                .map(DataTypeUtils::toLong)
                                .filter(Objects::nonNull)
                                .collect(Collectors.toSet())
                )
        );

        Map<Long, UserProfileDTO> userProfileMap = ResponseAssert.requiredSuccess(
                userProfileClient.mapByUserIds(
                        waters.stream().filter(
                                        apply -> Objects.equals(apply.getSalaryEvent(), UserBankWaterEvent.TRANSFER.name())
                                                || Objects.equals(apply.getSalaryEvent(), UserBankWaterEvent.RECEIVE_TRANSFER.name()))
                                .map(SalaryDiamondRunningWater::getTrackId)
                                .filter(StringUtils::isNotBlank)
                                .map(DataTypeUtils::toLong)
                                .filter(Objects::nonNull)
                                .collect(Collectors.toSet())
                )
        );

        return waters.stream().filter(item -> (!item.getSalaryEvent().equals(UserSalaryDiamondWaterEvent.BILL_EXCHANGE_GOLD_COINS.name())
                        && !item.getSalaryEvent().equals(UserSalaryDiamondWaterEvent.SYSTEM_DAILY_SETTLEMENT_DEDUCT.name())
                        && !item.getSalaryEvent().equals(UserSalaryDiamondWaterEvent.SALARY_EXCHANGE_GOLD_COINS.name()))).map(water -> {

                    Object details = null;
                    if (Objects.equals(water.getSalaryEvent(),
                            UserBankWaterEvent.EXCHANGE_GOLD_COINS.name())) {
                        details = goldApplyMap.get(DataTypeUtils.toLong(water.getTrackId()));
                    }

                    if (Objects.equals(water.getSalaryEvent(), UserBankWaterEvent.WITHDRAW.name())) {
                        details = moneyApplyMap.get(DataTypeUtils.toLong(water.getTrackId()));
                    }

                    if (Objects.equals(water.getSalaryEvent(), UserBankWaterEvent.TRANSFER.name()) ||
                            Objects.equals(water.getSalaryEvent(), UserBankWaterEvent.RECEIVE_TRANSFER.name())) {
                        details = userProfileMap.get(DataTypeUtils.toLong(water.getTrackId()));
                    }


                    return new UserBankWaterCO()
                            .setId(water.getId())
                            .setType(water.getType())
                            .setAmount(water.getAmount())
                            .setServiceCharge(water.getServiceCharge())
                            .setActualAmount(water.getActualAmount())
                            .setEvent(water.getSalaryEvent())
                            .setEventDescribe(water.getEventDesc())
                            .setBalance(water.getBalance())
                            .setTrackId(water.getTrackId())
                            .setDetails(details)
                            .setCreateTime(water.getCreateTime());
                })
                .collect(Collectors.toList());
    }

    public List<UserBankWaterCO> executeBill(SalaryDiamondRunningWaterQueryCmd cmd) {
        List<SalaryDiamondRunningWater> waters = salaryDiamondRunningWaterService.appPage(cmd.requiredReqUserId(), 20, cmd.getLastId());

        if (CollectionUtils.isEmpty(waters)) {
            return Lists.newArrayList();
        }

        return waters.stream().filter(item -> (item.getSalaryEvent().equals(UserSalaryDiamondWaterEvent.BILL_EXCHANGE_GOLD_COINS.name())
                || item.getSalaryEvent().equals(UserSalaryDiamondWaterEvent.SYSTEM_DAILY_SETTLEMENT_DEDUCT.name()))).map(water -> {
                    return new UserBankWaterCO()
                            .setId(water.getId())
                            .setType(water.getType())
                            .setAmount(water.getAmount())
                            //.setServiceCharge(water.getServiceCharge())
                            //.setActualAmount(water.getActualAmount())
                            .setEvent(water.getSalaryEvent())
                            .setEventDescribe(water.getEventDesc())
                            .setBalance(water.getBalance())
                            .setTrackId(water.getTrackId())
                            .setDetails(null)
                            .setCreateTime(water.getCreateTime());
                })
                .collect(Collectors.toList());
    }

    public List<UserBankWaterCO> executeSalary(SalaryDiamondRunningWaterQueryCmd cmd) {
        List<SalaryDiamondRunningWater> waters = salaryDiamondRunningWaterService.appPage(cmd.requiredReqUserId(), 20, cmd.getLastId(), UserSalaryDiamondWaterEvent.SALARY_EXCHANGE_GOLD_COINS.name());

        if (CollectionUtils.isEmpty(waters)) {
            return Lists.newArrayList();
        }

        return waters.stream().map(water -> {
                    return new UserBankWaterCO()
                            .setId(water.getId())
                            .setType(water.getType())
                            .setAmount(water.getAmount())
                            //.setServiceCharge(water.getServiceCharge())
                            //.setActualAmount(water.getActualAmount())
                            .setEvent(water.getSalaryEvent())
                            .setEventDescribe(water.getEventDesc())
                            .setBalance(water.getBalance())
                            .setTrackId(water.getTrackId())
                            .setDetails(null)
                            .setCreateTime(water.getCreateTime());
                })
                .collect(Collectors.toList());
    }
}
