package com.iformall.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.iformall.common.ErrorCode;
import com.iformall.common.IdWorker;
import com.iformall.common.ResultData;
import com.iformall.domain.po.*;
import com.iformall.domain.vo.WxCreditHistoryVo;
import com.iformall.enums.*;
import com.iformall.exception.MallinkException;
import com.iformall.mapper.*;
import com.iformall.service.ExcelService;
import com.iformall.service.WxCreditHistoryService;
import com.iformall.service.WxScoreRulesService;
import com.iformall.utils.CreditUtil;
import com.iformall.utils.RedisLock;
import com.iformall.utils.UserUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WxCreditHistoryServiceImpl implements WxCreditHistoryService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    WxCreditHistoryMapper wxCreditHistoryMapper;

    @Autowired
    WxCUserMapper wxCUserMapper;

    @Autowired
    WxCUserBasicInfoMapper wxCUserBasicInfoMapper;

    @Autowired
    WxMerchantBUserMapper wxMerchantBUserMapper;

    @Autowired
    MallUserInfoMapper mallUserInfoMapper;

    @Autowired
    WxScoreRulesService wxScoreRulesService;

    @Autowired
    WxMerchantMapper wxMerchantMapper;

    @Autowired
    WxCouponMapper wxCouponMapper;

    @Autowired
    RedisLock redisLock;

    @Autowired
    ExcelService excelService;

    @Autowired
    WxLevelConfigMapper wxLevelConfigMapper;

    @Override
    public void clearCreditByYear() {
        WxScoreRules wxScoreRules = new WxScoreRules();
        wxScoreRules.setType(EnumScoreRules.CREDIT.getCode());
        List<WxScoreRules> wxScoreRulesList = wxScoreRulesService.findList(wxScoreRules);
        List<String> tenantIds = wxScoreRulesList.stream().map(WxScoreRules::getTenantId).distinct().collect(Collectors.toList());

    }

    @Override
    public void exportData(HttpServletRequest request, HttpServletResponse response, WxCreditHistory wxCreditHistory) {
        List<WxCreditHistoryVo> wxCreditHistoryVoList = wxCreditHistoryMapper.findListMore(wxCreditHistory);
        excelService.exportExcel(wxCreditHistoryVoList, null, "积分变更记录", WxCreditHistoryVo.class, "积分变更记录.xlsx", response, false);

    }

    @Override
    public ResultData getCreditSummary(WxCreditHistory wxCreditHistory) {
        long totalCreditAmount = wxCreditHistoryMapper.getTotalCreditAmount(wxCreditHistory);
        long incrementCreditAmount = wxCreditHistoryMapper.getIncrementCreditAmount(wxCreditHistory);
        long decrementCreditAmount = wxCreditHistoryMapper.getDecrementCreditAmount(wxCreditHistory);
        long peopleCount = wxCreditHistoryMapper.getPeopleCount(wxCreditHistory);
        Map<String, Long> result = new HashMap<>(4);
        result.put("totalCreditAmount", totalCreditAmount);
        result.put("incrementCreditAmount", incrementCreditAmount);
        result.put("decrementCreditAmount", decrementCreditAmount);
        result.put("peopleCount", peopleCount);
        return new ResultData(result);
    }

    @Override
    public PageInfo<WxCreditHistory> listAsPage(WxCreditHistory record, Integer pageIndex, Integer pageSize) {
        return PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCreditHistoryMapper.findList(record));
    }

    @Override
    public PageInfo<WxCreditHistoryVo> listAsPageMore(WxCreditHistory record, Integer pageIndex, Integer pageSize) {
        PageHelper.startPage(pageIndex, pageSize);
        List<WxCreditHistoryVo> wxCreditHistoryVoList = wxCreditHistoryMapper.findListMore(record);
        return new PageInfo<>(wxCreditHistoryVoList);
    }

    public void supplyCreditInfo(List<WxCreditHistoryVo> wxCreditHistoryVoList) {
        List<Long> cUserIds = wxCreditHistoryVoList.stream().filter(a -> a.getOperatorType().equals(EnumUserType.CUSER.getCode())).map(WxCreditHistoryVo::getOperatorId).collect(Collectors.toList());
        List<Long> cUserBasicIds = wxCreditHistoryVoList.stream().filter(a -> a.getOperatorType().equals(EnumUserType.CUSERBASIC.getCode())).map(WxCreditHistoryVo::getOperatorId).collect(Collectors.toList());
        List<Long> bUserIds = wxCreditHistoryVoList.stream().filter(a -> a.getOperatorType().equals(EnumUserType.BUSER.getCode())).map(WxCreditHistoryVo::getOperatorId).collect(Collectors.toList());
        List<Long> mallUserIds = wxCreditHistoryVoList.stream().filter(a -> a.getOperatorType().equals(EnumUserType.MALLUSER.getCode())).map(WxCreditHistoryVo::getOperatorId).collect(Collectors.toList());
        List<WxCUser> wxCUserList = Lists.newArrayList();
        List<WxCUserBasicInfo> wxCUserBasicInfoList = Lists.newArrayList();
        List<WxMerchantBUser> wxMerchantBUserList = Lists.newArrayList();
        List<MallUserInfo> mallUserInfoList = Lists.newArrayList();
        if (cUserIds != null && cUserIds.size() > 0) {
            WxCUser wxUser = new WxCUser();
            wxUser.setIds(cUserIds);
            wxCUserList = wxCUserMapper.findList(wxUser);
        }
        if (cUserBasicIds != null && cUserBasicIds.size() > 0) {
            WxCUserBasicInfo wxCUserBasicInfo = new WxCUserBasicInfo();
            wxCUserBasicInfo.setIds(cUserBasicIds);
            wxCUserBasicInfoList = wxCUserBasicInfoMapper.findList(wxCUserBasicInfo);
        }
        if (bUserIds != null && bUserIds.size() > 0) {
            WxMerchantBUser wxMerchantBUser = new WxMerchantBUser();
            wxMerchantBUser.setIds(bUserIds);
            wxMerchantBUserList = wxMerchantBUserMapper.findList(wxMerchantBUser);
        }
        if (mallUserIds != null && mallUserIds.size() > 0) {
            MallUserInfo mallUserInfo = new MallUserInfo();
            mallUserInfo.setIds(mallUserIds);
            mallUserInfoList = mallUserInfoMapper.findList(mallUserInfo);
        }
        for (WxCreditHistoryVo credit : wxCreditHistoryVoList) {
            if (wxCUserList != null && wxCUserList.size() > 0) {
                wxCUserList.stream().forEach(cUser -> {
                    if (cUser.getId().longValue() == credit.getOperatorId().longValue()) {
                        credit.setOperator(cUser.getNickName());
                    }
                });
            }
            if (wxCUserBasicInfoList != null && wxCUserBasicInfoList.size() > 0) {
                wxCUserBasicInfoList.stream().forEach(cUserBasicInfo -> {
                    if (cUserBasicInfo.getId().longValue() == credit.getOperatorId().longValue()) {
                        credit.setOperator(cUserBasicInfo.getNickName());
                    }
                });
            }
            if (wxMerchantBUserList != null && wxMerchantBUserList.size() > 0) {
                wxMerchantBUserList.stream().forEach(bUser -> {
                    if (bUser.getId().longValue() == credit.getOperatorId().longValue()) {
                        credit.setOperator(bUser.getName());
                    }
                });
            }
            if (mallUserInfoList != null && mallUserInfoList.size() > 0) {
                mallUserInfoList.stream().forEach(mallUser -> {
                    if (mallUser.getId().longValue() == credit.getOperatorId().longValue()) {
                        credit.setOperator(mallUser.getName());
                    }
                });
            }
            if (Long.valueOf(EnumMerchantType.MERCHANT_MORE.getCode()).equals(credit.getMerchantId())) {
                credit.setMerchantName(EnumMerchantType.MERCHANT_MORE.getMessage());
            }
        }
    }

    @Override
    public WxCreditHistory getById(Long id) {
        return wxCreditHistoryMapper.selectById(id);
    }

    @Override
    public void creditUsercheck(Long cUserId) {
        WxCUser wxCUser = wxCUserMapper.selectById(cUserId);
        WxCUserBasicInfo wxCUserBasicInfo = wxCUserBasicInfoMapper.selectById(cUserId);
        UserUtil.creditUserCheck(wxCUser, wxCUserBasicInfo);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public WxCreditHistory saveOrUpdate(WxCreditHistory record) {
        WxCUser wxCUser = wxCUserMapper.selectById(record.getCUserId());
        WxCUserBasicInfo wxCUserBasicInfo = wxCUserBasicInfoMapper.selectById(record.getCUserId());
        if (wxCUser == null && wxCUserBasicInfo == null) {
            //验证此用户是否存在
            throw new MallinkException(ErrorCode.USER_NOT_AUTH_PHONE);
        }
        if (wxCUserBasicInfo != null && EnumCreditLockedStatus.CLOSE.getCode().equals(wxCUserBasicInfo.getStatus())) {
            logger.info(ErrorCode.MEMBER_IS_LOCKED.getMessage());
            return record;
        }
        if (StringUtils.isNotEmpty(record.getSpendStr())) {
            record.setSpend(new BigDecimal(record.getSpendStr()).multiply(new BigDecimal(100)).intValue());
        }
        int currentCreditAmount = 0;
        //获取当前用户总积分规则：优先获取wxCUser中数据 若不存在 再获取wxCUserBasicInfo表中数据
        if (wxCUser != null && wxCUser.getCredit() != null && wxCUser.getCredit() > 0) {
            currentCreditAmount = wxCUser.getCredit();
        } else if (wxCUserBasicInfo != null && wxCUserBasicInfo.getCredit() != null && wxCUserBasicInfo.getCredit() > 0) {
            currentCreditAmount = wxCUserBasicInfo.getCredit();
        }
        if (record.getCreditAmount() == null) {
            record.setCreditAmount(currentCreditAmount);
        }

        int creditChangeNumOrigin = creditIncrement(record);
        int creditChangeNum = creditChangeNumOrigin ;
        //是否参与了生日积分倍率
        boolean hasBirthDateCredit = false;
        if (record.getCreditType().equals(EnumScoreType.CONSUMPTION.getCode()) || record.getCreditType().equals(EnumScoreType.SPEND_CREDIT.getCode())) {
            //等级积分配置
            try {
                creditChangeNum = CreditUtil.calUserCredit(creditChangeNumOrigin, wxCUser, wxCUserBasicInfo, wxScoreRulesService,wxLevelConfigMapper);
                if (Objects.nonNull(CreditUtil.getIsBirthDayScale())) {
                    hasBirthDateCredit = true;
                }
            } catch (Exception e) {
                logger.error("积分倍率计算异常",e);
            } finally {
                CreditUtil.clear();
            }
        }

        if (creditChangeNum != 0) {
            Integer total = currentCreditAmount + creditChangeNum;
//            if (total > Integer.MAX_VALUE) {
//                throw new MallinkException(ErrorCode.OUT_OF_CREDIT);
//            }
            if (total < 0) {
                throw new MallinkException(ErrorCode.CREDIT_NOT_ENOUGH);
            }
            //如果为导入的会员 则直接覆盖总积分和操作积分明细
            if (!Objects.equals(record.getCreditType(),EnumScoreType.MEM_IMPORT.getCode())) {
                record.setCreditNum(creditChangeNum);
                record.setCreditAmount(total);
            }
            if (record.getId() == null) {
                final IdWorker idWorker = IdWorker.get();
                record.setId(idWorker.nextId());
                record.setCreateDate(new Date());
                wxCreditHistoryMapper.insert(record);
                //将计算出来新的总积分 更新到两张用户表里
                if (wxCUser != null) {
                    WxCUser wxCUserNew = new WxCUser();
                    wxCUserNew.setId(wxCUser.getId());
                    wxCUserNew.setCredit(record.getCreditAmount());
                    wxCUserMapper.updateById(wxCUserNew);
                }
                if (wxCUserBasicInfo != null) {
                    WxCUserBasicInfo wxCUserBasicInfoNew = new WxCUserBasicInfo();
                    wxCUserBasicInfoNew.setId(wxCUserBasicInfo.getId());
                    wxCUserBasicInfoNew.setCredit(record.getCreditAmount());
                    //如果享受了生日积分倍率，记录日期
                    if (hasBirthDateCredit) {
                        wxCUserBasicInfoNew.setScoreDate(new Date());
                    }
                    wxCUserBasicInfoMapper.updateById(wxCUserBasicInfoNew);
                }

            } else {
                //wxCreditHistoryMapper.updateById(record);
            }
        }
        return record;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public int cancelCredit(WxCreditHistory record) {
        int num = 0;
        WxCreditHistory wxCreditHistory = null;
        // 1. clear history
        List<WxCreditHistory> creditHistories = wxCreditHistoryMapper.findList(record);
        if (creditHistories.size() >= 1) {
            wxCreditHistory = creditHistories.get(0);
        }
        if (wxCreditHistory != null) {
            num = wxCreditHistoryMapper.deleteById(wxCreditHistory.getId());
            if (num != 1) {
                String errMessage = "删除积分历史出错";
                logger.error(errMessage + wxCreditHistory.toString());
                throw new MallinkException(ErrorCode.DB_FAIL.getCode(), errMessage);
            }
        } else {
            logger.info("未找到积分历史");
            return 0;
        }
        // 2. user
        WxCUser wxCUser = wxCUserMapper.selectById(record.getCUserId());
        WxCUserBasicInfo wxCUserBasicInfo = wxCUserBasicInfoMapper.selectById(record.getCUserId());
        if (wxCUser != null) {
            WxCUser wxCUserNew = new WxCUser();
            wxCUserNew.setId(wxCUser.getId());
            wxCUserNew.setCredit(wxCUser.getCredit() - wxCreditHistory.getCreditNum());
            wxCUserNew.setUpdateDate(new Date());
            wxCUserMapper.updateById(wxCUserNew);
        }
        if (wxCUserBasicInfo != null) {
            WxCUserBasicInfo wxCUserBasicInfoNew = new WxCUserBasicInfo();
            wxCUserBasicInfoNew.setId(wxCUserBasicInfo.getId());
            wxCUserBasicInfoNew.setCredit(wxCUserBasicInfo.getCredit() - wxCreditHistory.getCreditNum());
            wxCUserBasicInfoNew.setUpdateDate(new Date());
            wxCUserBasicInfoMapper.updateById(wxCUserBasicInfoNew);
        }
        return num;
    }

    @Override
    public void deleteById(Long id) {
        wxCreditHistoryMapper.deleteById(id);
    }

    @Override
    public Map<String, Integer> findByMerchantIdAndSpend(Long merchantId, String spendStr, Long userId,String tenantId) {
        WxMerchant wxMerchant = wxMerchantMapper.selectById(merchantId);
        Map<String, Integer> creditMap = Maps.newHashMap();
        if (wxMerchant != null && wxMerchant.getBusinessId() != null) {
            WxCreditHistory wxCreditHistory = new WxCreditHistory();
            wxCreditHistory.setBusinessId(wxMerchant.getBusinessId());
            wxCreditHistory.setTenantId(tenantId);
            int credit;
            int creditNew = 0 ;
            if (StringUtils.isNotBlank(spendStr)) {
                wxCreditHistory.setSpend(new BigDecimal(spendStr).multiply(new BigDecimal(100)).intValue());
                credit = payAddCredit(wxCreditHistory);
                if (Objects.nonNull(userId)) {
                    WxCUser wxCUser = wxCUserMapper.selectById(userId);
                    WxCUserBasicInfo wxCUserBasicInfo = wxCUserBasicInfoMapper.selectById(userId);
                    creditNew = CreditUtil.calUserCredit(credit, wxCUser, wxCUserBasicInfo, wxScoreRulesService,wxLevelConfigMapper);
                } else {
                    creditNew = credit;
                }
            }
            creditMap.put("credit", creditNew);
        }
        return creditMap;
    }


    //根据积分类型计算"增加积分"
    private int creditIncrement(WxCreditHistory record) {
        //查找C端用户的成长值
        if (record.getCreditType() == EnumScoreType.LOGIN.getCode()) {
            return loginAddCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.BIND_CAR.getCode()) {
            return bindCarAddCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.WECHAT_PERSION.getCode()) {
            return personAddCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.WECHAT_PHONE.getCode()) {
            return phoneAddCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.COMPLETE_INFO.getCode()) {
            return infoAddCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.SPEND_CREDIT.getCode()) {
            return spendCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.CHANGE_CREDIT.getCode()) {
            return changeCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.CONSUMPTION.getCode()) {
            return payAddCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.REDUCE_CREDIT.getCode()) {
            return reduceCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.MEM_IMPORT.getCode()) {
            return record.getCreditNum();
        }
        if (record.getCreditType() == EnumScoreType.ACTIVITY_JOIN.getCode()) {
            return reduceCredit(record);
        }

        return 0;
    }

    private int reduceCredit(WxCreditHistory record) {
        int reduceCredit = record.getCreditNum();
        return -reduceCredit;
    }

    private int loginAddCredit(WxCreditHistory record) {
        //如果当天已经登录过则跳过
        if (wxCreditHistoryMapper.loginCount(record) > 0) {
            return 0;
        }
        // 1. 获取当前租户下积分的增加规则
        WxScoreRules wxScoreRules = wxScoreRulesService.getCreditRules(record.getTenantId());
        // 2. 增长的积分
        return wxScoreRules.getRule(EnumScoreType.LOGIN, WxScoreRules.SCORE);
    }

    private int bindCarAddCredit(WxCreditHistory record) {
        // 1. 获取score rules
        WxScoreRules wxScoreRules = wxScoreRulesService.getCreditRules(record.getTenantId());
        // 2. 增长的积分
        return wxScoreRules.getRule(EnumScoreType.BIND_CAR, WxScoreRules.SCORE);
    }

    private int personAddCredit(WxCreditHistory record) {
        // 1. 获取score rules
        WxScoreRules wxScoreRules = wxScoreRulesService.getCreditRules(record.getTenantId());
        // 2. 增长的积分
        return wxScoreRules.getRule(EnumScoreType.WECHAT_PERSION, WxScoreRules.SCORE);
    }

    private int phoneAddCredit(WxCreditHistory record) {
        // 1. 获取score rules
        WxScoreRules wxScoreRules = wxScoreRulesService.getCreditRules(record.getTenantId());
        // 2. 获取成长值
        return wxScoreRules.getRule(EnumScoreType.WECHAT_PHONE, WxScoreRules.SCORE);
    }

    private int checkCompleteInfoScoreCount(WxCUser cUser) {
        WxCreditHistory wxCreditHistory = new WxCreditHistory();
        wxCreditHistory.setTenantId(cUser.getTenantId());
        wxCreditHistory.setCUserId(cUser.getId());
        wxCreditHistory.setCreditType(EnumScoreType.COMPLETE_INFO.getCode());
        return wxCreditHistoryMapper.countList(wxCreditHistory);
    }

    private int infoAddCredit(WxCreditHistory record) {
        WxCUser user = wxCUserMapper.selectById(record.getCUserId());
        if (user == null || checkCompleteInfoScoreCount(user) > 0)
            return 0;
        // 1. 获取score rules
        WxScoreRules scoreRules = wxScoreRulesService.getCreditRules(user.getTenantId());
        // 2. 获取成长值
        return scoreRules.getRule(EnumScoreType.COMPLETE_INFO, WxScoreRules.SCORE);
    }

    private int spendCredit(WxCreditHistory record) {
        WxMerchant wxMerchant = new WxMerchant();
        wxMerchant.setStatus(1);
        wxMerchant.setId(record.getMerchantId());
        List<WxMerchant> wxMerchantList = wxMerchantMapper.findList(wxMerchant);
        if (wxMerchantList == null || wxMerchantList.size() == 0) {
            throw new MallinkException(ErrorCode.MERCHANT_INFO_NOT_FOUND);
        }
        //根据商户id和花费金额计算需要新增的积分
        Map<String, Integer> map = findByMerchantIdAndSpend(record.getMerchantId(), record.getSpendStr(), null,record.getTenantId());
        return map.get("credit");
    }

    private int changeCredit(WxCreditHistory record) {
        int changeCredit = record.getCreditNum();
        return -changeCredit;
    }

    private int payAddCredit(WxCreditHistory record) {
        int addCreditNumber = 0;
        // 订单金额为0时不计入
        if (record.getCreditNum() != null && record.getCreditNum() == 0)
            return 0;
        // 1. 获取score rules
        WxScoreRules scoreRules = wxScoreRulesService.getCreditRules(record.getTenantId());

        // 2. 获取成长值 by id
        JSONObject payRule = scoreRules.getRuleObj(EnumScoreType.CONSUMPTION);
        // 3. 获取成长值 by 业态
        JSONObject busRule = null;
        JSONObject busRuleOther = null;
        JSONArray busRules = payRule.getJSONArray("childs");
        if (busRules != null) {
            for (int i = 0; i < busRules.size(); i++) {
                JSONObject bus = (JSONObject) busRules.get(i);
                Integer businessId1 = bus.getInteger("businessId");
                if (record.getBusinessId().equals(businessId1)) {
                    busRule = bus;
                }
                if (businessId1.equals(EnumBusiness.BUSINESS_ID6.getCode())) {
                    busRuleOther = bus;
                }
            }
            if (busRule == null) {
                busRule = busRuleOther;
            }
        }
        if (busRule == null) {
            busRule = payRule;
        }
        Double priceCondition = busRule.getDouble(WxScoreRules.STEP) * 100;  // 元->分
        int baseScore = busRule.getInteger(WxScoreRules.SCORE); //   消费增长积分

        // 4. 计算积分
        if (record.getSpend() >= priceCondition.intValue()) {
            Double dScoreT = Math.floor(record.getSpend() * 1.0D / priceCondition * baseScore);
            addCreditNumber = dScoreT.intValue();
            log.info("CREDIT:" + busRule.toJSONString() + "\nAddCredit: " + addCreditNumber);
        } else {
            log.info("CREDIT:" + busRule.toJSONString() + "\nAddCredit: 0, not touch credit limit");
            return 0;
        }
        return addCreditNumber;
    }

}
