package com.inkasso.factoring.system.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.inkasso.adapter.bean.AbstractUser;
import com.inkasso.adapter.enums.ClientTypeEnum;
import com.inkasso.adapter.exception.BusinessException;
import com.inkasso.adapter.exception.ServiceException;
import com.inkasso.adapter.exception.enums.ExceptionEnum;
import com.inkasso.adapter.util.*;
import com.inkasso.adapter.web.LoginUserHolder;
import com.inkasso.factoring.common.entity.ValidatorEntity;
import com.inkasso.factoring.common.service.CommonService;
import com.inkasso.factoring.common.service.impl.CommonServiceImpl;
import com.inkasso.factoring.common.util.VerifyCodeUtil;
import com.inkasso.factoring.common.util.aliyunMail.ShortMessageConstant;
import com.inkasso.factoring.common.util.aliyunMail.ShortMessageUtil;
import com.inkasso.factoring.file.entity.FileStorageDO;
import com.inkasso.factoring.file.service.FileService;
import com.inkasso.factoring.system.mail.service.MailService;
import com.inkasso.factoring.system.user.dao.UserDAO;
import com.inkasso.factoring.system.user.entity.UserDO;
import com.inkasso.factoring.system.user.enums.CodeBusinessTypeEnum;
import com.inkasso.factoring.system.user.model.dto.UserDTO;
import com.inkasso.factoring.system.user.model.request.*;
import com.inkasso.factoring.system.user.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author 张超
 * @date 2023-08-15
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserDAO, UserDO> implements UserService {

    @Autowired
    private FileService fileService;
    @Autowired
    private MailService mailService;
    @Autowired
    private CommonService commonService;
    @Autowired
    private RedisTemplate redisTemplate;


    @Autowired
    private TokenAndSessionCacheUtil tokenAndSessionCacheUtil;


    private static final String PHONE_BIND_PREFIX = "phoneBind:";

    private static final String PASSWORD_EDIT_PREFIX = "passwordEdit:";

    private static final String EMAIL_BIND_PREFIX = "emailBind:";

    private static final String REDIS_MARK = ":";

    private static final String SMS_CODE_PREFIX = "SMS_CODE_PHONE:";


    @Override
    public void addUser(AddUserRequest addUserRequest) {
        UserDO user = BeanUtilsProxy.copyPropertiesIgnoreNull(addUserRequest, UserDO.class);
        this.baseMapper.insert(user);
    }

    @Override
    public void editUser(EditUserRequest editUserRequest) {

        UserDO user = super.getById(editUserRequest.getId());
        if (user == null) {
            throw new ServiceException(400, ExceptionEnum.SYSTEM_DATE_ERROR);
        }
        BeanUtilsProxy.copyPropertiesNotIgnoreNull(editUserRequest, user);
        this.updateById(user);
    }

    @Override
    public UserDTO getUserDtoById(Long id) {
        UserDO user = super.getById(id);
        UserDTO userDto = BeanUtilsProxy.copyProperties(user, UserDTO.class);
//        WxAccountDO byId = wxAccountService.lambdaQuery().eq(WxAccountDO::getUserId,id).one();
//        userDto.setOpenId(byId.getOpenId());
//        userDto.setTenantId(LoginUserHolder.get().getTenantId());
        return userDto;
    }

    @Override
    public void deleteUser(Long id) {

        this.removeById(id);
    }

    @Override
    public UserDO getByPhone(String phone) {
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(UserDO::getMobile, phone);
        UserDO userDO = this.baseMapper.selectOne(queryWrapper);
        return userDO;
    }

    /**
     * 通过登录名字 获取 用户信息
     *
     * @param username
     * @return
     */
    @Override
    public UserDTO getUserByUsername(String username) {
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(UserDO::getLoginName, username);
        UserDO userDO = this.baseMapper.selectOne(queryWrapper);
        if (userDO != null) {
            UserDTO userDTO = BeanUtilsProxy.copyProperties(userDO, UserDTO.class);
            userDTO.setPhone(userDO.getMobile());
            return userDTO;
        }
        return null;
    }


    /**
     * 发送用户系统下  各种类型的   验证码   手机绑定（phoneBind）/密码修改（passwordEdit）/
     *
     * @param type
     */
    @Override
    public void sendCode(String phone, String type) {
        String key = null;
        if (CodeBusinessTypeEnum.PHONE_BIND.getCode().equals(type)) {
            //说明用户是要发送手机验证码
            key = PHONE_BIND_PREFIX + LoginUserHolder.get().getId() + REDIS_MARK + phone;
        }
        //密码修改 短信
        if (CodeBusinessTypeEnum.PASSWORD_EDIT.getCode().equals(type)) {
            //如果是 密码修改的短信 则手机号从系统中获取
            phone = LoginUserHolder.get().getPhone();
            key = PASSWORD_EDIT_PREFIX + LoginUserHolder.get().getId() + REDIS_MARK + phone;
        }
        //绑定邮箱
        if (CodeBusinessTypeEnum.EMAIL_BIND.getCode().equals(type)) {
            phone = LoginUserHolder.get().getPhone();
            key = EMAIL_BIND_PREFIX + LoginUserHolder.get().getId() + REDIS_MARK + phone;
        }
        String code = VerifyCodeUtil.createCode(6);
        redisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", code);
        try {
            ShortMessageUtil.sendMessage(phone,
                    ShortMessageConstant.SEND_PHONE_BIND_TEMPLATE, jsonObject, "长安银科");
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("500", "当天发送短信次数已超出");
        }
    }

    /**
     * 发送email code
     *
     * @param email
     */
    @Override
    public void sendEmailCode(String email) {
        String code = VerifyCodeUtil.createCode(6);
        String key = EMAIL_BIND_PREFIX + LoginUserHolder.get().getId() + REDIS_MARK + email;
        redisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);
        String content = "您正在使用绑定邮箱操作,您的验证码为: " + code;
        mailService.sendSimpleMail(email, "绑定邮箱验证码", content);
    }


    @Override
    public void bindPhone(BindPhoneRequest bindPhoneRequest, String token) {
        boolean b = checkMobile(bindPhoneRequest.getPhone());
        if(!b){
            throw new ServiceException(500,"手机号已被占用");
        }
        String key = PHONE_BIND_PREFIX + LoginUserHolder.get().getId() + REDIS_MARK
                + bindPhoneRequest.getPhone();
        Object o = redisTemplate.opsForValue().get(key);
        if (o != null) {
            String code = o.toString();
            if (bindPhoneRequest.getCode().equals(code)) {
                UserDO userDO = this.getById(LoginUserHolder.get().getId());
                userDO.setMobile(bindPhoneRequest.getPhone());
                this.updateById(userDO);
                redisTemplate.delete(key);
                // String tokenKey = tokenAndSessionCacheUtil.tokenKey(ClientTypeEnum.WEB.getCode(), token);
                AbstractUser loginUserByToken = tokenAndSessionCacheUtil.getLoginUserByToken(token);
                loginUserByToken.setPhone(bindPhoneRequest.getPhone());
                tokenAndSessionCacheUtil.token2User(ClientTypeEnum.WEB.getCode(), token, loginUserByToken);
                return;
            } else {
                throw new ServiceException("500", "验证码输入错误");
            }
        } else {
            throw new ServiceException("500", "验证码已过期");
        }

    }

    /**
     * 修改密码
     *
     * @param editPasswordRequest
     */
    @Override
    public void editPassword(EditPasswordRequest editPasswordRequest) {
        //修改密码
        String phone = LoginUserHolder.get().getPhone();
        String key = PASSWORD_EDIT_PREFIX + LoginUserHolder.get().getId() + REDIS_MARK + phone;
        Object o = redisTemplate.opsForValue().get(key);
        if (o == null) {
            throw new ServiceException("500", "验证码已过期");
        }
        String code = o.toString();
        if (!code.equals(editPasswordRequest.getCode())) {
            throw new ServiceException("500", "验证码输入错误");
        }
        //TODO 对比密码
        // LoginUserHolder.get().getPassword()
        UserDO byId = this.getById(LoginUserHolder.get().getId());
        try {
            byId.setPassword(MD5Util.getEncryptedPwd(editPasswordRequest.getNewPassword()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(500, "密码修改失败");
        }
        this.updateById(byId);
        redisTemplate.delete(key);
    }


    /**
     * @param bindEmailRequest
     */
    @Override
    public void bindEmail(BindEmailRequest bindEmailRequest) {
        String phone = LoginUserHolder.get().getPhone();
        String key1 = EMAIL_BIND_PREFIX + LoginUserHolder.get().getId() + REDIS_MARK + phone;
        String key2 = EMAIL_BIND_PREFIX + LoginUserHolder.get().getId() + REDIS_MARK + bindEmailRequest.getEmail();
        Object o1 = redisTemplate.opsForValue().get(key1);
        Object o2 = redisTemplate.opsForValue().get(key2);
        if (o1 == null) {
            throw new ServiceException("500", "手机验证码已过期");
        }
        if (o2 == null) {
            throw new ServiceException("500", "邮箱验证码已过期");
        }
        String phoneCode = o1.toString();
        String emailCode = o2.toString();
        if (!phoneCode.equals(bindEmailRequest.getCode())) {
            throw new ServiceException("500", "手机验证码输入错误");
        }
        if (!emailCode.equals(bindEmailRequest.getEmailCode())) {
            throw new ServiceException("500", "邮箱验证码输入错误");
        }
        UserDO byId = this.getById(LoginUserHolder.get().getId());
        byId.setEmail(bindEmailRequest.getEmail());
        this.updateById(byId);
        redisTemplate.delete(key1);
        redisTemplate.delete(key2);
    }

    @Override
    public void updateHeadImg(Long fileId) {
        Long id = LoginUserHolder.get().getId();
        UserDO userDO = this.baseMapper.selectById(id);
        if (userDO != null) {
            FileStorageDO byId = fileService.getById(fileId);
            if (byId != null) {
                userDO.setHeadImgUrl(byId.getPath());
            }
            this.baseMapper.updateById(userDO);
        }
    }

    @Override
    public void reSetPassword(ReSetPasswordRequest reSetPasswordRequest) {

        UserDO byPhone = this.getByPhone(reSetPasswordRequest.getPhone());
        if (byPhone == null) {
            throw new ServiceException("500", "用户信息不存在");
        }
        String key = CommonServiceImpl.PASSWORD_RESET_PREFIX + reSetPasswordRequest.getPhone();
        Object o = redisTemplate.opsForValue().get(key);
        if (o == null) {
            throw new ServiceException("500", "验证码已失效");
        }
        String code = o.toString();
        if (!reSetPasswordRequest.getCode().equals(code)) {
            throw new ServiceException("500", "验证码错误");
        }
        try {
            byPhone.setPassword(MD5Util.getEncryptedPwd(reSetPasswordRequest.getNewPassword()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("500", "找回密码失败");
        }

        this.updateById(byPhone);
        redisTemplate.delete(key);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(UserDO userDO) {
        if (userDO == null) {
            throw new ServiceException("500", "用户信息不能为空");
        }
        ValidatorUtil.validator(ValidatorEntity.ValidatorType.ADD, userDO);

        if (!redisTemplate.hasKey(SMS_CODE_PREFIX + userDO.getMobile())) {
            throw new ServiceException("500", "验证码已失效");
        }
        if (!commonService.verifySMSCode(userDO.getMobile(), userDO.getCode())) {
            throw new ServiceException("500", "验证码输入错误，请检查");
        }
        if (!this.checkLoginName(userDO.getLoginName())) {
            throw new ServiceException("500", "用户已存在,请直接登录");
        }
        if (!this.checkMobile(userDO.getMobile())) {
            throw new ServiceException("500", "手机号已注册，请检查");
        }
        try {
            userDO.setPassword(MD5Util.getEncryptedPwd(userDO.getPassword()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        List<Long> provinceCityIds = userDO.getProvinceCityIds();
        if(CollectionUtil.isNotEmpty(provinceCityIds)){
            userDO.setProvinceId(provinceCityIds.get(0));
            if(provinceCityIds.size()>1){
                userDO.setCityId(provinceCityIds.get(1));
            }
        }
        userDO.setUserName(userDO.getLoginName());
        userDO.setRegisterDate(new Date());
        if (userDO.getReferralCode() != null) {
            //如果推荐码有值，去B端查询对应的客户经理
            Long backendUserId = baseMapper.getBackendUserIdByReferralCode(userDO.getReferralCode());
            userDO.setProjectManager(backendUserId);
        }
        baseMapper.insert(userDO);
        return true;
    }

    @Override
    public UserDO updateUsername(String username,String token) {

        //更新用户名字
        List<UserDO> list = this.lambdaQuery().eq(UserDO::getLoginName, username).list();
        if(CollectionUtil.isNotEmpty(list)){
             throw new ServiceException("500","用户名重复");
        }
        UserDO byId = this.getById(LoginUserHolder.get().getId());
       // byId.setUserName(username);
        byId.setLoginName(username);
        this.updateById(byId);
        UserDTO loginUserByToken = (UserDTO)tokenAndSessionCacheUtil.getLoginUserByToken(token);
        loginUserByToken.setLoginName(username);
        tokenAndSessionCacheUtil.token2User(ClientTypeEnum.WEB.getCode(), token, loginUserByToken);

        return byId;
    }

    /**
     * 校验手机号是否重复
     *
     * @param mobile
     * @return
     */
    private boolean checkMobile(String mobile) {
        if (StringUtils.isBlank(mobile)) {
            throw new ServiceException("500", "手机号不能为空");
        }
        UserDO userDO = baseMapper.selectOne(new QueryWrapper<UserDO>().eq("mobile", mobile));
        return userDO == null;
    }

    /**
     * 校验登录名是否重复
     *
     * @param loginName
     * @return
     */
    private boolean checkLoginName(String loginName) {
        if (StringUtils.isBlank(loginName)) {
            throw new ServiceException("500", "手机号不能为空");
        }
        UserDO userDO = baseMapper.selectOne(new QueryWrapper<UserDO>().eq("login_name", loginName));
        return userDO == null;
    }
}

