package com.ai.da.service.impl;

import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.ResultEnum;
import com.ai.da.common.utils.CopyUtil;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.SendEmailUtil;
import com.ai.da.mapper.primary.AffiliateIncomeMapper;
import com.ai.da.mapper.primary.AffiliateMapper;
import com.ai.da.mapper.primary.ProductCouponsMapper;
import com.ai.da.mapper.primary.SubscriptionInfoMapper;
import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.AffiliateEmailParamsDTO;
import com.ai.da.model.dto.AffiliateQueryDTO;
import com.ai.da.model.vo.AffiliateInvitationDetailsVO;
import com.ai.da.model.vo.AffiliateVO;
import com.ai.da.model.vo.AuthPrincipalVo;
import com.ai.da.service.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.util.StringUtils;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;

@Service
@Slf4j
public class AffiliateServiceImpl extends ServiceImpl<AffiliateMapper, Affiliate> implements AffiliateService {

    @Resource
    private OrderInfoService orderInfoService;
    @Resource
    private AccountService accountService;
    @Resource
    private PaymentInfoService paymentInfoService;
    @Resource
    private SubscriptionInfoMapper subscriptionInfoMapper;
    @Resource
    private AffiliateIncomeMapper affiliateIncomeMapper;
    @Resource
    private StripeService stripeService;
    @Resource
    private ProductCouponsMapper productCouponsMapper;
    @Resource
    private RedisUtil redisUtil;

    // 推广者注册
    public Boolean registerAsAnAffiliate(String promotionMethod){
        AuthPrincipalVo userHolder = UserContext.getUserHolder();
        // 判断该用户是否已注册
        QueryWrapper<Affiliate> qw = new QueryWrapper<>();
        qw.eq("account_id", userHolder.getId());
        Affiliate affiliate = baseMapper.selectOne(qw);
        if (Objects.isNull(affiliate)){
            affiliate = new Affiliate();
            affiliate.setAccountId(userHolder.getId());
            affiliate.setStatus("Pending");
            affiliate.setCreateTime(LocalDateTime.now());
            affiliate.setPromotionMethod(promotionMethod);
            baseMapper.insert(affiliate);
            // 邮件通知审批者
            String merchantEmail = "kimwong@code-create.com.hk";
            String developer = "xupei3360@163.com";
            String[] receiverEmail = {merchantEmail, developer};
            SendEmailUtil.affiliateEmailReminder(receiverEmail, new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new");
        }else {
            throw new BusinessException("You have registered an Affiliate", ResultEnum.PROMPT.getCode());
        }
        return true;
    }

    public PageBaseResponse<AffiliateVO> getAffiliateList(AffiliateQueryDTO affiliateQueryDTO){
        log.info("parameter => {}", affiliateQueryDTO.toString());

        int offset = (affiliateQueryDTO.getPage() - 1) * affiliateQueryDTO.getSize();
        List<AffiliateVO> affiliateList = baseMapper.getAffiliateList(affiliateQueryDTO.getStatus(),
                affiliateQueryDTO.getStartTime(),
                affiliateQueryDTO.getEndTime(),
                affiliateQueryDTO.getOrder(),
                affiliateQueryDTO.getAffiliateId(),
                affiliateQueryDTO.getSize(),
                offset
        );
        if (CollectionUtils.isEmpty(affiliateList)) {
            return PageBaseResponse.success(new Page<>());
        }else {
            int totalCount = baseMapper.queryAffiliateTotalCount(affiliateQueryDTO.getStatus(),
                    affiliateQueryDTO.getStartTime(),
                    affiliateQueryDTO.getEndTime(),
                    affiliateQueryDTO.getAffiliateId()
            );
            IPage<AffiliateVO> orderListVOIPage = new Page<>();
            Integer size = affiliateQueryDTO.getSize();
            orderListVOIPage.setSize(size);
            orderListVOIPage.setRecords(affiliateList);
            orderListVOIPage.setCurrent(affiliateQueryDTO.getPage());
            orderListVOIPage.setPages((long)Math.ceil((double) totalCount / size));
            orderListVOIPage.setTotal(totalCount);
            return PageBaseResponse.success(orderListVOIPage);
        }
        /*QueryWrapper<Affiliate> qw = new QueryWrapper<>();
        qw.eq(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStatus()), "status", affiliateQueryDTO.getStatus())
                .gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime())
                .lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime())
                .eq(!Objects.isNull(affiliateQueryDTO.getAffiliateId()), "id", affiliateQueryDTO.getAffiliateId())
                .orderByDesc(affiliateQueryDTO.getOrder().equals("DESC"), "create_time");
        Page<Affiliate> affiliatePage = baseMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), qw);
        affiliatePage.convert((Function<Affiliate, AffiliateVO>) affiliate-> {
            AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class);
            affiliateVO.setUsername();
        });
        return affiliatePage;*/
    }

    public AffiliateVO personalAffiliateCenter(){
        QueryWrapper<Affiliate> qw = new QueryWrapper<>();
        Long accountId = UserContext.getUserHolder().getId();
        qw.eq("account_id", accountId);
        Affiliate affiliate = baseMapper.selectOne(qw);
        AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class);
        affiliateVO.setLinkViewCount(getAffiliateLinkViewCount(affiliate.getId()));
        return affiliateVO;
    }

    public double[] getPersonalMonthlyIncome(int year){
        Long accountId = UserContext.getUserHolder().getId();
        List<Map<String, Object>> personalMonthlyIncome = affiliateIncomeMapper.getPersonalMonthlyIncome(accountId, year);
        double[] commissions = new double[12];
        personalMonthlyIncome.forEach(income -> {
            int month = Integer.parseInt(income.get("yearMonth").toString());
            commissions[month-1] = (double)income.get("totalCommission");
        });

        return commissions;
    }

    // 审批申请
    public Boolean applicationApproval(Long id, Boolean isApproved, Float commission){
        Affiliate affiliate = baseMapper.selectById(id);

        // 1、更新db状态
        if (isApproved){
            // 更新状态
            affiliate.setStatus("Active");
            affiliate.setApproved(true);
            affiliate.setLink(CommonConstant.AFFILIATE_LINK + affiliate.getId());
            if (Objects.isNull(commission)) {
                // 未设置佣金比例的情况下，默认25%
                affiliate.setCommissionPercent(25f);
            } else {
                affiliate.setCommissionPercent(commission);
            }
        } else {
            affiliate.setStatus("Refused");
            affiliate.setApproved(false);
        }
        affiliate.setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(affiliate);

        // 2、将批准结果邮件通知用户
        Account account = accountService.getById(affiliate.getAccountId());
        String[] userEmail = {account.getUserEmail()};
        String userName = account.getUserName();
        if (isApproved){
            SendEmailUtil.affiliateEmailReminder(userEmail, new AffiliateEmailParamsDTO(userName), "accepted");
        }else {
            SendEmailUtil.affiliateEmailReminder(userEmail, new AffiliateEmailParamsDTO(userName), "refused");
        }
        return true;
    }

    public void updateCommissionPercentage(Long id, Float commission){
        Affiliate affiliate = baseMapper.selectById(id);
        if (Objects.isNull(affiliate)){
            log.info("未知affiliate id :{}", id);
            throw new BusinessException("unknown affiliate");
        }
        if (!Objects.equals(affiliate.getCommissionPercent(), commission)){
            affiliate.setCommissionPercent(commission);
            affiliate.setUpdateTime(LocalDateTime.now());
            baseMapper.updateById(affiliate);
        }
    }

    // 定时计算佣金
    public void updateAffiliateInfoWithPayment(){
        // id存redis
        String lastTime = redisUtil.getFromString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME);
        String currentTime = LocalDateTime.now().toString();
        // 1、查上次更新之后有无新订单
        QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
        if (!StringUtil.isNullOrEmpty(lastTime)){
            queryWrapper.gt("create_time", lastTime);
        }
        queryWrapper.eq("type","new").eq("trade_state", "paid");

        List<PaymentInfo> paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper);
        if (!paymentInfos.isEmpty()){
            paymentInfos.forEach(paymentInfo -> {
                // 2、根据order_no查付款用户id
                OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(paymentInfo.getOrderNo());
                if (Objects.isNull(orderInfo)){
                    return;
                }
                Long accountId = orderInfo.getAccountId();
                // 3、查该用户之前是否有初次订阅的订单
                QueryWrapper<OrderInfo> qwOrderInfo = new QueryWrapper<>();
                qwOrderInfo.eq("account_id", accountId).eq("is_first_subscription", 1);
                List<OrderInfo> orderInfos = orderInfoService.getBaseMapper().selectList(qwOrderInfo);
                // 该用户首次订阅（非首次订阅，不分配佣金）
                if (orderInfos.isEmpty()){
                    // 查询是否绑定affiliateId
                    Account account = accountService.getById(accountId);
                    if (!Objects.isNull(account.getInvitationCode())){
                        log.info("结算订单id为{}的佣金", orderInfo.getId());
                        // 3、若有, 直接更新affiliate的所得
                        Affiliate affiliate = baseMapper.selectById(account.getInvitationCode());
                        Float payerTotal = paymentInfo.getPayerTotal();

                        if (payerTotal > 0){
                            // 分配新用户首次订阅所付费用 预设的佣金比例 作为佣金
                            BigDecimal commission = BigDecimal.valueOf(payerTotal).multiply(BigDecimal.valueOf(affiliate.getCommissionPercent() / 100));
                            BigDecimal monthlyEarning = BigDecimal.valueOf(affiliate.getMonthlyEarnings()).add(commission);
                            BigDecimal unpaidEarnings = BigDecimal.valueOf(affiliate.getUnpaidEarnings()).add(commission);
                            int visits = affiliate.getVisits() + 1;
                            affiliate.setMonthlyEarnings(monthlyEarning.floatValue());
                            affiliate.setUnpaidEarnings(unpaidEarnings.floatValue());
                            affiliate.setVisits(visits);
                            affiliate.setUpdateTime(LocalDateTime.now());
                            baseMapper.updateById(affiliate);

                            orderInfo.setIsCommissionCalculated((byte)1);

                            // 4、添加到t_affiliate_income
                            AffiliateIncome affiliateIncome = new AffiliateIncome();
                            affiliateIncome.setAffiliateId(affiliate.getId());
                            affiliateIncome.setAffiliateAccountId(affiliate.getAccountId());
                            affiliateIncome.setInviteeAccountId(accountId);
                            affiliateIncome.setAmount(payerTotal);
                            affiliateIncome.setPaymentInfoId(paymentInfo.getId());
                            affiliateIncome.setPaymentTime(paymentInfo.getCreateTime());
                            affiliateIncome.setCommission(commission.floatValue());
                            affiliateIncome.setCreateTime(LocalDateTime.now());
                            affiliateIncomeMapper.insert(affiliateIncome);
                        }
                    }
                    orderInfo.setIsFirstSubscription((byte)1);
                    orderInfo.setUpdateTime(LocalDateTime.now());
                    orderInfoService.updateById(orderInfo);
                }
            });
        }
        redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime);
    }

    public Boolean affiliateLinkViewsIncrease(Long affiliateId) {
        redisUtil.increaseAffiliateLinkViewCount(affiliateId);
        return Boolean.TRUE;
    }

    private Long getAffiliateLinkViewCount(Long affiliateId) {
        return redisUtil.getAffiliateLinkViewCount(affiliateId);
    }

    // 查看每个affiliate带来收入的详情
    @Override
    public IPage<AffiliateInvitationDetailsVO> getEachAffiliateGeneratedRevenue(AffiliateQueryDTO affiliateQueryDTO) {
        if (Objects.isNull(affiliateQueryDTO.getAffiliateId())){
            throw new BusinessException("Please specify the affiliate ID.", ResultEnum.PROMPT.getCode());
        }

        QueryWrapper<AffiliateIncome> affiliateIncomeQueryWrapper = new QueryWrapper<>();
        affiliateIncomeQueryWrapper
                .gt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getStartTime()), "create_time", affiliateQueryDTO.getStartTime())
                .lt(!StringUtils.isNullOrEmpty(affiliateQueryDTO.getEndTime()), "create_time", affiliateQueryDTO.getEndTime())
                .eq(!Objects.isNull(affiliateQueryDTO.getAffiliateId()), "affiliate_id", affiliateQueryDTO.getAffiliateId())
                .orderByDesc(affiliateQueryDTO.getOrder().equals("DESC"), "create_time");
        IPage<AffiliateIncome> affiliateIncomePage = affiliateIncomeMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), affiliateIncomeQueryWrapper);
        return affiliateIncomePage.convert((Function<AffiliateIncome, AffiliateInvitationDetailsVO>) affiliateIncome -> {
            AffiliateInvitationDetailsVO affiliateInvitationDetailsVO = CopyUtil.copyObject(affiliateIncome, AffiliateInvitationDetailsVO.class);
            affiliateInvitationDetailsVO.setAccountId(affiliateIncome.getInviteeAccountId());
            affiliateInvitationDetailsVO.setUsername(accountService.getBaseMapper().selectById(affiliateIncome.getInviteeAccountId()).getUserName());
            affiliateInvitationDetailsVO.setFirstSubscriptionPaymentAmount(affiliateIncome.getAmount());
            affiliateInvitationDetailsVO.setCommission(affiliateIncome.getCommission());
            affiliateInvitationDetailsVO.setTime(affiliateIncome.getPaymentTime());
            return affiliateInvitationDetailsVO;
        });
    }

    public void commissionCalculation(Integer year, Integer month) {
        if (Objects.isNull(year)) {
            year = LocalDateTime.now().getYear();
        }
        if (Objects.isNull(month)) {
            month = LocalDateTime.now().getMonthValue();
        }

        List<Map<String, Object>> monthlyAffiliateIncome = affiliateIncomeMapper.getMonthlyAffiliateIncome(year, month);
        // 1、总收入(近一个月通过affiliate产生的收入),未支付的金额 affiliate表中unpaid的总和
        Double totalAmount = 0.0;
        Double totalCommission = 0.0;
        if (!monthlyAffiliateIncome.isEmpty()){
            Map<String, Object> monthlyIncome = monthlyAffiliateIncome.get(0);
            totalAmount = (Double) monthlyIncome.get("totalAmount");
            totalCommission = (Double) monthlyIncome.get("totalCommission");
        }

        // 2、本月新注册的Affiliate
        Map<String, Long> monthlyApprovedAffiliate = baseMapper.getMonthlyApprovedAffiliate(year, month);
        Long count = monthlyApprovedAffiliate.get("count");

        AffiliateEmailParamsDTO affiliateEmailParamsDTO = new AffiliateEmailParamsDTO();
        affiliateEmailParamsDTO.setTotalProgramRevenue(totalAmount.toString());
        affiliateEmailParamsDTO.setNewApprovedAffiliates(count.toString());
        affiliateEmailParamsDTO.setUnpaidEarnings(totalCommission.toString());
        affiliateEmailParamsDTO.setPaidEarnings("0");

      String merchantEmail = "kimwong@code-create.com.hk";
        String developer = "xupei3360@163.com";
        String[] receiverEmail = {merchantEmail, developer};
        // 邮件通知
        SendEmailUtil.affiliateEmailReminder(receiverEmail, affiliateEmailParamsDTO, "summary");
    }

    @Override
    public Affiliate getByAccountId(Long accountId) {
        QueryWrapper<Affiliate> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("account_id", accountId).orderByDesc("id").last("limit 1");

        return baseMapper.selectOne(queryWrapper);
    }

    public void calcCouponsCommission(){
        // id存redis
        String lastTime = redisUtil.getFromString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME);
        String currentTime = LocalDateTime.now().toString();
        // 1、查上次更新之后有无使用了优惠券的新订单
        QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();
        if (!StringUtil.isNullOrEmpty(lastTime)){
            queryWrapper.gt("create_time", lastTime)
                    .lt("create_time", currentTime)
                    .isNotNull("promotion_code");
        }
        List<PaymentInfo> paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper);

        // key:推广码, value:用户支付的金额
        HashMap<String, Float> codeAmount = new HashMap<>();
        if (!paymentInfos.isEmpty()){
            for (PaymentInfo paymentInfo : paymentInfos){
                String promotionCode = paymentInfo.getPromotionCode();
                Float sum = codeAmount.get(promotionCode);
                if (sum == null || sum == 0.0f){
                    codeAmount.put(promotionCode, paymentInfo.getPayerTotal());
                }else {
                    codeAmount.put(promotionCode, sum + paymentInfo.getPayerTotal());
                }
            }
            for (Map.Entry<String, Float> entry : codeAmount.entrySet()){
                String promotionCode = entry.getKey();
                ProductCoupons productCoupons = stripeService.getProductCoupon(promotionCode, null);
                if (!Objects.isNull(productCoupons)){
                    // 2、计算支付金额的总和，更新totalEarnings，commission，unpaidCommission
                    float sum = productCoupons.getTotalEarnings() + entry.getValue();
                    productCoupons.setTotalEarnings(sum);
                    float commission = sum * productCoupons.getCommissionRate() / 100;
                    productCoupons.setCommission(commission);
                    productCoupons.setUnpaidCommission(commission - productCoupons.getPaidCommission());
                    productCouponsMapper.updateById(productCoupons);
                }
            }
        }
        redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime);
    }


}
