package com.ruoyi.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.code.DictConstant;
import com.ruoyi.code.PublicCommon;
import com.ruoyi.code.Token;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.enums.CoreExceptionEnum;
import com.ruoyi.enums.sms.SmsEnums;
import com.ruoyi.enums.user.UserEnums;
import com.ruoyi.frequency.customer.entity.Customer;
import com.ruoyi.frequency.customer.mapper.CustomerMapper;
import com.ruoyi.frequency.customer.service.CustomerService;
import com.ruoyi.frequency.customer.service.UserService;
import com.ruoyi.frequency.enterprise.service.EnterpriseService;
import com.ruoyi.frequency.illegaldeductionrecord.model.result.OrderInfoResp;
import com.ruoyi.frequency.illegaldeductionrecord.service.IllegalDeductionRecordService;
import com.ruoyi.frequency.sale.entity.Sale;
import com.ruoyi.frequency.sale.service.SaleService;
import com.ruoyi.frequency.sensitiveword.entity.SensitiveWord;
import com.ruoyi.frequency.sensitiveword.service.SensitiveWordService;
import com.ruoyi.frequency.store.entity.Store;
import com.ruoyi.frequency.store.service.StoreService;
import com.ruoyi.pay.enums.PayEnums;
import com.ruoyi.response.ResponseData;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.utils.pwd.MD5EncryptionPwdUtil;
import com.ruoyi.vo.UserInfo;
import com.ruoyi.vo.UserVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author 刘耀
 * @Date 2020/9/16 14:24
 */
@Slf4j
@Component
public class BusinessUtil {

    @Autowired
    private StringRedisTemplate redisTemplate;


    @Resource
    private CustomerService customerService;
    @Autowired
    private CustomerMapper customerMapper;

    @Resource
    private StoreService storeService;

    @Autowired
    private ISysDictDataService iSysDictDataService;

    public OrderInfoResp getOrderInfo(Long orderId){
        return customerMapper.getOrderInfo(orderId);
    }

    /**
     * 获取字典
     *
     * @param code
     * @return
     */
    public SysDictData getDict(String code) {
        SysDictData sysDictData = new SysDictData();
        sysDictData.setCode(code);
        List<SysDictData> list = iSysDictDataService.selectDictDataList(sysDictData);
        return list.isEmpty() ? null : list.get(0);
    }

    public List<SysDictData> getDictList(String code) {
        SysDictData sysDictData = new SysDictData();
        sysDictData.setDictType(code);
        List<SysDictData> list = iSysDictDataService.selectDictDataList(sysDictData);
        return list;
    }

    public void setOutTradeNo(String outTradeNo) {
        redisTemplate.opsForValue().set("PaySuccess" + outTradeNo, DateUtil.formatDateTime(new Date()), 10, TimeUnit.MINUTES);
    }

    public Boolean isPaySuccess(String outTradeNo) {
        return redisTemplate.hasKey("PaySuccess" + outTradeNo);
    }


    /**
     * 判断支付密码是否错误
     *
     * @param userVo
     * @param pwd
     * @return
     */
    public Boolean payPwdTrue(UserVo userVo, String pwd) {

        //查询用户支付密码
        String userPwd = (String) getUserFieldValue(userVo, "payPwd");

        if (StrUtil.isBlank(userPwd)) {
            throw new CustomException(CoreExceptionEnum.NO_PWD);
        }

        //验证密码
        String md5Code = MD5EncryptionPwdUtil.encryptionMd5(pwd);
        if (!userPwd.equals(md5Code)) {
            return false;
        }

        return true;
    }

    /**
     * 验证支付密码
     *
     * @param userVo
     * @param pwd
     * @return
     */
    public void verifyPayPwd(UserVo userVo, Integer payType, String pwd) {

        if (ObjectUtil.equal(PayEnums.balance.getCode(), payType)) {
            //支付密码验证
            Boolean isTrue = payPwdTrue(userVo, pwd);
            if (!isTrue) {
                throw new CustomException(CoreExceptionEnum.PWD_ERROR);
            }
        }
    }

    /**
     * 判断登录密码是否错误
     *
     * @param userVo
     * @param pwd
     * @return
     */
    public Boolean loginPwdTrue(UserVo userVo, String pwd) {

        //查询用户登入密码
        String loginPwd = (String) getUserFieldValue(userVo, "loginPwd");

        if (StrUtil.isBlank(loginPwd)) {
            throw new CustomException(540, "您还未设置密码，请选择其他登录方式");
        }

        String md5Code = MD5EncryptionPwdUtil.encryptionMd5(pwd);
        if (!loginPwd.equals(md5Code)) {
            return false;
        }
        return true;
    }

    /**
     * 功能描述:写出文本
     *
     * @param response response
     * @param data
     * @author yangliu  2013-8-26 上午09:08:36
     */
    public void writeText(HttpServletResponse response, Object data) {
        PrintWriter out = null;
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/text");
            out = response.getWriter();
            out.print(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeOut(out);
        }
    }

    private void closeOut(PrintWriter out) {
        if (out != null) {
            out.flush();
            out.close();
        }
    }

    /**
     * 校验支付密码
     *
     * @param pwd
     * @param userVo
     * @return
     */
    public ResponseData checkPayPwd(String pwd, UserVo userVo) {

        Boolean isTrue = payPwdTrue(userVo, pwd);

        if (!isTrue) {
            return ResponseData.error(CoreExceptionEnum.PWD_ERROR);
        }
        return ResponseData.success();
    }


    /**
     * 生成token
     *
     * @param userId 用户id
     * @return
     */
    public ResponseData getToken(Long userId, Integer userType) {

//        if (UserEnums.store.getCode().equals(userType)){
//            Store store = storeService.getById(userId);
//            if (ObjectUtil.isNotEmpty(store.getBindEnterpriseId())){
//                userId = store.getBindEnterpriseId();
//                userType = UserEnums.enterprise.getCode();
//            }
//        }

        //组装token
        String token = assembleToken(userType);

        redisTemplate.opsForValue().set(token, userId.toString(), Token.EXPIRE_SECOND, TimeUnit.DAYS);

        //存储token信息
//        redisTemplate.opsForHash().put(Token.TOKEN_MAP, userId + "", token);

        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        return ResponseData.success(result);
    }

    /**
     * 组装token
     *
     * @param userType
     * @return
     */
    private String assembleToken(Integer userType) {

        String token = "";

        switch (userType) {
            case 1:
                token = Token.customer + UUID.randomUUID();
                break;
            case 2:
                token = Token.store + UUID.randomUUID();
                break;
            case 3:
                token = Token.sale + UUID.randomUUID();
                break;
            case 4:
                token = Token.enterprise + UUID.randomUUID();
                break;
            default:
                throw new CustomException(CoreExceptionEnum.ERROR_PARAM);
        }
        return token;
    }


    /**
     * 获取用户余额
     *
     * @param userVo
     * @return
     */
    public BigDecimal getUserBalance(UserVo userVo) {
        return new BigDecimal(getUserFieldValue(userVo, "balance").toString());
    }


    /**
     * 获取用户抵用金余额
     *
     * @param userVo
     * @return
     */
    public BigDecimal getUserGiveBalance(UserVo userVo) {
        return new BigDecimal(getUserFieldValue(userVo, "giveBalance").toString());
    }

    /**
     * 获取用户积分
     *
     * @param userVo
     * @return
     */
    public BigDecimal getUserIntegral(UserVo userVo) {
        return new BigDecimal(getUserFieldValue(userVo, "integral").toString());
    }

    /**
     * 获取用户其中一个字段
     *
     * @param userVo
     * @param fieldName
     * @return
     */
    public Object getUserFieldValue(UserVo userVo, String fieldName) {

        //获取用户对象
        Object object = getUserObject(userVo);

        Object fieldValue = getFieldValue(object, fieldName);

        return fieldValue;
    }

    @Autowired
    private SaleService saleService;

    @Autowired
    private EnterpriseService enterpriseService;

    /**
     * 获取用户对象
     *
     * @param userVo
     * @return
     */
    private Object getUserObject(UserVo userVo) {
        switch (userVo.getUserType()) {
            case 1:
                return customerService.getById(userVo.getUserId());
            case 2:
                return storeService.getById(userVo.getUserId());
            case 3:
                return saleService.getById(userVo.getUserId());
            case 4:
                return enterpriseService.getById(userVo.getUserId());
            default:
                throw new CustomException(CoreExceptionEnum.ERROR_PARAM);
        }
    }

    /**
     * 根据手机号查询用户对象
     *
     * @return
     */
    public Object getUserObject(UserEnums userEnums, String areaCode, String mobile) {
        switch (userEnums) {
            case customer:
                Customer customer = customerService.getOne(new QueryWrapper<Customer>().lambda()
                        .eq(ObjectUtil.isNotEmpty(areaCode), Customer::getAreaCode, areaCode)
                        .eq(Customer::getMobile, mobile)
                        .ne(Customer::getDelFlag, PublicCommon.删除));
                return customer;
            case store:
                Store store = storeService.getOne(new QueryWrapper<Store>().lambda()
                        .eq(Store::getMobile, mobile)
                        .ne(Store::getDelFlag, PublicCommon.删除));
                return store;
            case sale:
                Sale sale = saleService.getOne(new QueryWrapper<Sale>().lambda()
                        .eq(Sale::getMobile, mobile)
                        .ne(Sale::getDelFlag, PublicCommon.删除));
                return sale;
        }

        return null;
    }

    /**
     * 根据用户账号查询用户对象
     *
     * @return
     */
    public Object getUserObject(UserEnums userEnums, String account) {
        switch (userEnums) {
            case customer:
                Customer customer = customerService.getOne(new QueryWrapper<Customer>().lambda()
                        .eq(Customer::getAccount, account)
                        .ne(Customer::getDelFlag, PublicCommon.删除));
                return customer;
            case store:
                Store store = storeService.getOne(new QueryWrapper<Store>().lambda()
                        .eq(Store::getAccount, account)
                        .ne(Store::getDelFlag, PublicCommon.删除));
                return store;
            case sale:
                Sale sale = saleService.getOne(new QueryWrapper<Sale>().lambda()
                        .eq(Sale::getMobile, account)
                        .ne(Sale::getDelFlag, PublicCommon.删除));
                return sale;
            default:
                return null;
        }
    }


    /**
     * 根据字段名称，获取字段值
     */
    public Object getFieldValue(Object object, String fieldName) {

        //根据对象反射
        Class<?> clazz = object.getClass();

        //根据反射机制获取对象的所有字段，包含public、private和proteced
        Field[] declaredFields = clazz.getDeclaredFields();

        Object fieldValue = null;

        for (Field field : Arrays.asList(declaredFields)) {
            //设置属性可访问
            field.setAccessible(true);

            if (ObjectUtil.notEqual(field.getName(), fieldName)) {
                continue;
            }

            try {
                //得到属性值
                fieldValue = field.get(object);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return fieldValue;
    }


    /**
     * 验证码验证
     *
     * @param code     验证码
     * @param smsEnums 短信类型 SmsEnums枚举
     * @param mobile   手机号
     * @param userType 用户类型 1 用户
     * @return
     */
    public void checkCode(String code, SmsEnums smsEnums, String areaCode, String mobile, Integer userType) {
        String s = redisTemplate.opsForValue().get(code + smsEnums.getCode() + areaCode + mobile + userType);
        if (StrUtil.isBlank(s)) {
            throw new CustomException("验证码错误");
        }
        String[] split = s.split(",");
        Long time = System.currentTimeMillis() - Long.valueOf(split[0].trim());
        if (time > 10 * 60 * 1000) {
            throw new CustomException("验证码超时");
        }
        if (!split[1].trim().equals(smsEnums.getCode().toString())) {
            throw new CustomException("操作冲突");
        }
        if (!split[2].trim().equals(code)) {
            throw new CustomException("验证码错误");
        }
        if (!split[3].trim().equals(mobile)) {
            throw new CustomException("手机号错误");
        }

        //删除 验证码
        redisTemplate.delete(code + smsEnums.getCode() + areaCode + mobile + userType);
    }

    /**
     * 判断余额支付
     *
     * @param userVo
     * @param amount
     */
    public void judgeBalancePay(UserVo userVo, BigDecimal amount, String pwd) {
        //支付密码验证
        Boolean isTrue = payPwdTrue(userVo, pwd);
        if (!isTrue) {
            throw new CustomException(CoreExceptionEnum.PWD_ERROR);
        }
        //判断余额
        BigDecimal userBalance = getUserBalance(userVo);
        if (userBalance.compareTo(amount) == -1) {
            throw new CustomException(CoreExceptionEnum.BALANCE_INSUFFICIENT);
        }
    }

    /**
     * 判断积分余额支付
     *
     * @param userVo
     * @param amount
     */
    public void judgeIntegralPay(UserVo userVo, BigDecimal amount, String pwd) {

        //判断积分余额
        BigDecimal userIntegral = getUserIntegral(userVo);
        if (userIntegral.compareTo(amount) == -1) {
            throw new CustomException(CoreExceptionEnum.INTEGRAL_INSUFFICIENT);
        }

        //支付密码验证
        Boolean isTrue = payPwdTrue(userVo, pwd);
        if (!isTrue) {
            throw new CustomException(CoreExceptionEnum.PWD_ERROR);
        }
    }

    /**
     * 修改在线时长
     *
     * @param userVo
     * @param totalTime
     */
    public void updateOnlineDuration(UserVo userVo, Long totalTime, Integer onlineStatus) {

        switch (userVo.getUserType()) {
            case 1:
                customerService.update().setSql("online_duration = " + totalTime / 60 / 1000 + ",online_status = " + onlineStatus).eq("id", userVo.getUserId()).update();
                break;
            case 2:
                storeService.update().setSql("online_duration = " + totalTime / 60 / 1000 + ",online_status = " + onlineStatus).eq("id", userVo.getUserId()).update();
                break;
            case 3:
//                saleService.update().setSql("online_duration = " + totalTime/60/1000 + ",online_status = "+ onlineStatus).eq("id", userVo.getUserId()).update();
                break;
            case 4:
                enterpriseService.update().setSql("online_duration = " + totalTime / 60 / 1000 + ",online_status = " + onlineStatus).eq("id", userVo.getUserId()).update();
                break;
            default:
                throw new CustomException(CoreExceptionEnum.ERROR_PARAM);
        }
    }


    /**
     * 修改在线状态
     *
     * @param userVo
     */
    public void updateOnlineStatus(UserVo userVo) {

        switch (userVo.getUserType()) {
            case 1:
                customerService.update().setSql("online_status = 2").eq("id", userVo.getUserId()).update();
                break;
            case 2:
                storeService.update().setSql("online_status = 2").eq("id", userVo.getUserId()).update();
                break;
            case 3:
                saleService.update().setSql("online_status = 2").eq("id", userVo.getUserId()).update();
                break;
            default:
                throw new CustomException(CoreExceptionEnum.ERROR_PARAM);
        }
    }

    public UserInfo getUserInfo(UserVo userVo) {
        //用户id
        Set<UserVo> userVoSet = new HashSet<>();
        userVoSet.add(userVo);

        //获取用户信息
        Map<UserVo, UserInfo> userMap = userService.selectUserInfoMap(userVoSet, userVo);
        UserInfo userInfo = userMap.get(userVo);
        return userInfo;
    }

    /**
     * 计算用户可使用赠送的金额
     *
     * @param userVo
     * @param price
     * @return
     */
    public BigDecimal userMaxGivePrice(UserVo userVo, BigDecimal price) {

        //只有设计师有赠送金额
        if (ObjectUtil.notEqual(UserEnums.customer.getCode(), userVo.getUserType())) {
            return BigDecimal.ZERO;
        }

        //查询赠送金额最大支付比例
        SysDictData dict = this.getDict(DictConstant.OrderDict.赠送金额支付占比);
        BigDecimal givePriceRatio = BigDecimal.ZERO;
        if (dict != null && dict.getDictValue() != null) {
            givePriceRatio = new BigDecimal(dict.getDictValue());
        }

        //获取用户赠送金额
        Customer customer = customerService.getById(userVo.getUserId());
        BigDecimal givePrice = customer.getGiveBalance();

        BigDecimal maxGivePrice = price.multiply(givePriceRatio).setScale(2, RoundingMode.HALF_UP);
        return maxGivePrice.compareTo(givePrice) == 1 ? givePrice : maxGivePrice;
    }

    /**
     * 获取用户当前在线状态
     *
     * @param id
     * @param lastTimeMapKey
     * @return
     */
    public Integer getOnlineStatus(Long id, String lastTimeMapKey) {

        //在线状态;1:在线,2离线
        Integer onlineStatus = 2;

        if (redisTemplate.opsForHash().hasKey(lastTimeMapKey, id.toString())) {
            long lastTimes = Long.valueOf(redisTemplate.opsForHash().get(lastTimeMapKey, id.toString()).toString());

            //判断当前时间和上次时间是否超过6分钟(考虑到网络延时和程序卡顿,多1分钟)
            if (System.currentTimeMillis() - lastTimes < (6 * 60 * 1000)) {
                onlineStatus = 1;
            }
        }

        return onlineStatus;
    }

    /**
     * 判断是否是自己
     */
    public static Boolean isSelf(UserVo sourceUser, UserVo targetUser) {
        if (sourceUser != null && sourceUser.getUserId() != null && sourceUser.getUserType() != null
                && targetUser != null && targetUser.getUserId() != null && targetUser.getUserType() != null) {
            return sourceUser.getUserId().equals(targetUser.getUserId()) && sourceUser.getUserType().equals(targetUser.getUserType());
        }
        return false;
    }

    @Autowired
    private UserService userService;

    /**
     * 是否可以发布互动消息
     */
    public Boolean canPushMessage(UserVo selfUserVo, UserVo otherUserVo) {
        List<UserVo> userVos = userService.blockUserList(selfUserVo);
        for (UserVo userVo : userVos) {
            Boolean self = isSelf(userVo, otherUserVo);
            if (self) {
                return false;
            }
        }
        userVos = userService.shieldUserList(otherUserVo);

        for (UserVo userVo : userVos) {
            Boolean self = isSelf(userVo, selfUserVo);
            if (self) {
                return false;
            }
        }
        return true;
    }

    @Autowired
    private SensitiveWordService sensitiveWordService;

    public void checkSensitiveWord(String word) {
        List<SensitiveWord> list = sensitiveWordService.list(new QueryWrapper<SensitiveWord>().lambda().eq(SensitiveWord::getDelFlag, PublicCommon.启用));
        for (SensitiveWord sensitiveWord : list) {
            if (StrUtil.isNotBlank(sensitiveWord.getName()) && word.contains(sensitiveWord.getName())) {
                throw new ServiceException("禁止使用敏感词汇");
            }
        }
    }
}