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.domain.po.*;
import com.iformall.domain.vo.WxCreditHistoryVo;
import com.iformall.enums.EnumBusiness;
import com.iformall.enums.EnumScoreRules;
import com.iformall.enums.EnumScoreType;
import com.iformall.enums.EnumUserType;
import com.iformall.exception.MallinkException;
import com.iformall.mapper.*;
import com.iformall.service.WxCreditHistoryService;
import com.iformall.service.WxScoreRulesService;
import com.iformall.utils.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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 java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WxCreditHistoryServiceImpl implements WxCreditHistoryService {

    @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;

    @Override
    public void clearCreditByYear() {
        WxScoreRules wxScoreRules = new WxScoreRules();
        wxScoreRules.setType(EnumScoreRules.CREDIT.getCode());


    }

    @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);
        if (wxCreditHistoryVoList == null || wxCreditHistoryVoList.size() == 0) {
            return new PageInfo<>(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());
                    }
                });
            }
        }
        return new PageInfo<>(wxCreditHistoryVoList);
    }

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

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

        //计算出需要新增或减少的积分
        int creditChangeNum = creditIncrement(record);
        if (creditChangeNum != 0) {
            Long total = Long.valueOf(currentCreditAmount) + Long.valueOf(creditChangeNum);
            if (total > Integer.MAX_VALUE) {
                throw new MallinkException(ErrorCode.OUT_OF_CREDIT);
            }
            if (total < 0) {
                throw new MallinkException(ErrorCode.CREDIT_NOT_ENOUGH);
            }
            //如果为导入的会员 则直接覆盖总积分和操作积分明细
            if (record.getCreditType() != EnumScoreType.MEM_IMPORT.getCode()) {
                record.setCreditNum(creditChangeNum);
                record.setCreditAmount(currentCreditAmount + creditChangeNum);
            }
            if (record.getId() == null) {
                final IdWorker idWorker = IdWorker.get();
                record.setId(idWorker.nextId());
                record.setCreateDate(new Date());
                wxCreditHistoryMapper.insertSelective(record);
                //将计算出来新的总积分 更新到两张用户表里
                if (wxCUser != null) {
                    WxCUser wxCUserNew = new WxCUser();
                    wxCUserNew.setId(wxCUser.getId());
                    wxCUserNew.setCredit(record.getCreditAmount());
                    wxCUserMapper.updateByPrimaryKeySelective(wxCUserNew);
                }
                if (wxCUserBasicInfo != null) {
                    WxCUserBasicInfo wxCUserBasicInfoNew = new WxCUserBasicInfo();
                    wxCUserBasicInfoNew.setId(wxCUserBasicInfo.getId());
                    wxCUserBasicInfoNew.setCredit(record.getCreditAmount());
                    wxCUserBasicInfoMapper.updateByPrimaryKeySelective(wxCUserBasicInfoNew);
                }
            } else {
                //wxCreditHistoryMapper.updateByPrimaryKeySelective(record);
            }
        }
        return record;
    }

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

    @Override
    public Map<String, Integer> findByMerchantIdAndSpend(Long merchantId, String spendStr, String tenantId) {
        WxMerchant wxMerchant = wxMerchantMapper.selectByPrimaryKey(merchantId);
        Map<String, Integer> creditMap = Maps.newHashMap();
        if (wxMerchant != null && wxMerchant.getBusinessId() != null) {
            WxCreditHistory wxCreditHistory = new WxCreditHistory();
            wxCreditHistory.setBusinessId(Long.valueOf(wxMerchant.getBusinessId()));
            wxCreditHistory.setTenantId(tenantId);
            if (StringUtils.isNotEmpty(spendStr)) {
                wxCreditHistory.setSpend(new BigDecimal(spendStr).multiply(new BigDecimal(100)).intValue());
            }
            int credit = payAddCredit(wxCreditHistory);
            creditMap.put("credit", credit);
        }
        return creditMap;
    }


    //根据积分类型计算"增加积分"
    private int creditIncrement(WxCreditHistory record) {
        if (record.getCreditType() == EnumScoreType.LOGIN.getCode()) {
            return loginAddCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.BIND_CAR.getCode()) {
            return bindCarAddCredit(record);
        }
        if (record.getCreditType() == EnumScoreType.WIFI_CONNECTION.getCode()) {
            return wifiAddCredit();
        }
        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();
        }

        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 wifiAddCredit() {
        int addCreditNumber = 0;
        return addCreditNumber;
    }

    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_PERSION, 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.selectByPrimaryKey(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) {
        //根据商户id和花费金额计算需要新增的积分
        Map<String,Integer> map = findByMerchantIdAndSpend(record.getMerchantId(),record.getSpendStr(),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. 计算积分
        Double dScoreT = Math.floor(record.getSpend() * 1.0D / priceCondition * baseScore);
        addCreditNumber = dScoreT.intValue();
        log.info("CREDIT:" + busRule.toJSONString() + "\nAddCredit: " + addCreditNumber);

        return addCreditNumber;
    }

}
