package cn.quantgroup.xyqb.service.register.impl;

import cn.quantgroup.user.enums.RecordType;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.entity.Address;
import cn.quantgroup.xyqb.entity.Contact;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.entity.UserDeregisterRecord;
import cn.quantgroup.xyqb.event.RegisterEvent;
import cn.quantgroup.xyqb.exception.UserRegisterLoginException;
import cn.quantgroup.xyqb.model.UserRegisterParam;
import cn.quantgroup.xyqb.service.register.IUserDeregisterService;
import cn.quantgroup.xyqb.service.register.IUserRegisterService;
import cn.quantgroup.xyqb.service.user.ILoginRecordService;
import cn.quantgroup.xyqb.service.user.IUserService;
import cn.quantgroup.xyqb.util.DateUtils;
import cn.quantgroup.xyqb.util.PasswordUtil;
import cn.quantgroup.xyqb.util.TenantUtil;
import cn.quantgroup.xyqb.util.encrypt.Md5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

import static cn.quantgroup.xyqb.Constants.DELETE_USER_AGAIN_REGISTER_INTERVAL;

/**
 * @author liqing
 * @date 2017/12/4 0004
 */
@Service("userRegisterService")
@Slf4j
public class UserRegisterServiceImpl implements IUserRegisterService {

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @Resource
    private IUserService userService;

    @Resource
    private ILoginRecordService loginRecordService;

    @Resource
    private IUserDeregisterService userDeregisterService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public User register(Long registerFrom, String phoneNo) {

        /* 用户销户检查 */
        deregisterCheck(phoneNo);

        UserRegisterParam userRegisterParam = UserRegisterParam.builder()
                .registerFrom(registerFrom)
                .phoneNo(phoneNo)
                .channelId(registerFrom)
                .sendSuccessSms(true)
                .sendAppSms(true)
                .sendSuccessMq(true)
                .build();
        User user = saveUser(userRegisterParam);
        applicationEventPublisher.publishEvent(new RegisterEvent(this, userRegisterParam));
        return user;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public User register(Long registerFrom, String phoneNo, Integer tenantId) {

        deregisterCheck(phoneNo);

        UserRegisterParam userRegisterParam = UserRegisterParam.builder()
                .registerFrom(registerFrom)
                .phoneNo(phoneNo)
                .channelId(registerFrom)
                .sendSuccessSms(true)
                .sendAppSms(true)
                .sendSuccessMq(true)
                .build();
        User user = saveUser(userRegisterParam);
        if (tenantId == null || tenantId.equals(TenantUtil.TENANT_DEFAULT)) {
            applicationEventPublisher.publishEvent(new RegisterEvent(this, userRegisterParam));
        }
        return user;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public User register(Long registerFrom, String phoneNo, String idNo, String name, Long channelId, Long btRegisterChannelId) {

        deregisterCheck(phoneNo);

        UserRegisterParam userRegisterParam = UserRegisterParam.builder()
                .registerFrom(registerFrom)
                .phoneNo(phoneNo)
                .idNo(idNo)
                .name(name)
                .channelId(channelId)
                .btRegisterChannelId(btRegisterChannelId)
                .sendSuccessSms(true)
                .sendAppSms(true)
                .sendSuccessMq(true)
                .build();
        User user = saveUser(userRegisterParam);
        applicationEventPublisher.publishEvent(new RegisterEvent(this, userRegisterParam));
        return user;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public User register(Long registerFrom, String phoneNo, String idNo, String name, Long channelId, Long btRegisterChannelId, Integer tenantId) {

        deregisterCheck(phoneNo);

        UserRegisterParam userRegisterParam = UserRegisterParam.builder()
                .registerFrom(registerFrom)
                .phoneNo(phoneNo)
                .idNo(idNo)
                .name(name)
                .channelId(channelId)
                .btRegisterChannelId(btRegisterChannelId)
                .sendSuccessSms(true)
                .sendAppSms(true)
                .sendSuccessMq(true)
                .build();
        User user = saveUser(userRegisterParam);
        if (tenantId == null || tenantId.equals(TenantUtil.TENANT_DEFAULT)) {
            applicationEventPublisher.publishEvent(new RegisterEvent(this, userRegisterParam));
        }
        return user;
    }

    private User saveUser(UserRegisterParam userRegisterParam) {
        String uuid = UUID.randomUUID().toString();
        User user = new User();
        user.setUuid(uuid);
        user.setEnable(true);
        user.setPhoneNo(userRegisterParam.getPhoneNo());
        user.setEncryptedPhoneNo(userRegisterParam.getPhoneNo());
        Long registerFrom = userRegisterParam.getRegisterFrom();
        if (Objects.isNull(registerFrom)) {
            registerFrom = 1L;
        }
        Long channelId = userRegisterParam.getChannelId();
        if (Objects.equals(channelId, Constants.Channel.BAITIAO)) {
            user.setRegisteredFrom(channelId);
        } else {
            user.setRegisteredFrom(registerFrom);
        }
        String password = StringUtils.defaultString(userRegisterParam.getPassword(), "");
        // 如果需要生成随机密码
        if (userRegisterParam.isGenerateRandomPwd()) {
            password = PasswordUtil.generateRandomPwd(Constants.RANDOM_PWD_LEN);
        }
        if (StringUtils.isNotBlank(password)) {
            user.setPassword(PasswordUtil.MD5WithSalt(password));
        }
        user = userService.saveUser(user);

        // 注册添加日志
        loginRecordService.saveLoginRecord(user.getId(), RecordType.REGISTERRECORD.getName());
        userRegisterParam.setUser(user);
        return user;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean register(String phoneNo, String password, Long registerFrom, String ip, Long channelId, Long btRegisterChannelId, String dimension) {

        deregisterCheck(phoneNo);

        UserRegisterParam userRegisterParam = UserRegisterParam.builder()
                .registerFrom(registerFrom)
                .phoneNo(phoneNo).password(password)
                .channelId(channelId)
                .btRegisterChannelId(btRegisterChannelId)
                .dimension(dimension)
                .sendSuccessSms(true)
                .sendAppSms(true)
                .sendSuccessMq(true)
                .build();
        User user = saveUser(userRegisterParam);
        applicationEventPublisher.publishEvent(new RegisterEvent(this, userRegisterParam));
        return user != null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public User register(String phoneNo, Long channelId, Long registerFrom, String appChannel, Long btRegisterChannelId, String dimension) {

        deregisterCheck(phoneNo);

        UserRegisterParam userRegisterParam = UserRegisterParam.builder()
                .registerFrom(registerFrom)
                .phoneNo(phoneNo)
                .channelId(channelId)
                .btRegisterChannelId(btRegisterChannelId)
                .dimension(dimension)
                .sendSuccessSms(true)
                .sendAppSms(true)
                .sendSuccessMq(true)
                .build();
        User user = saveUser(userRegisterParam);
        applicationEventPublisher.publishEvent(new RegisterEvent(this, userRegisterParam));
        return user;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public User register(String phoneNo, Long channelId, Long registerFrom, String appChannel, Long btRegisterChannelId, String dimension, Integer tenantId) {

        deregisterCheck(phoneNo);

        UserRegisterParam userRegisterParam = UserRegisterParam.builder()
                .registerFrom(registerFrom)
                .phoneNo(phoneNo)
                .channelId(channelId)
                .btRegisterChannelId(btRegisterChannelId)
                .dimension(dimension)
                .sendSuccessSms(true)
                .sendAppSms(true)
                .sendSuccessMq(true)
                .build();
        User user = saveUser(userRegisterParam);
        if (tenantId == null || tenantId.equals(TenantUtil.TENANT_DEFAULT)) {
            applicationEventPublisher.publishEvent(new RegisterEvent(this, userRegisterParam));
        }
        return user;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public User register(Long registeredFrom, Long channelId, String phoneNo, String name, String idNo, Address addressObj, String contacts, List<Contact> contactList, Long btRegisterChannelId) {

        deregisterCheck(phoneNo);

        UserRegisterParam userRegisterParam = UserRegisterParam.builder()
                .registerFrom(registeredFrom)
                .phoneNo(phoneNo)
                .idNo(idNo)
                .name(name)
                .channelId(channelId)
                .btRegisterChannelId(btRegisterChannelId)
                .address(addressObj)
                .contacts(contacts)
                .contactList(contactList)
                .sendSuccessSms(true)
                .sendAppSms(true)
                .sendSuccessMq(true)
                .build();
        User user = saveUser(userRegisterParam);
        applicationEventPublisher.publishEvent(new RegisterEvent(this, userRegisterParam));
        return user;
    }


    /**
     * 注册校验-销户校验
     *
     * @param phoneNo 手机号
     */
    private void deregisterCheck(String phoneNo) {

        List<UserDeregisterRecord> deregisterRecords = userDeregisterService.queryByPhoneNo(Md5Util.build(phoneNo));

        /* 获取最新销户记录 */
        Optional<UserDeregisterRecord> lastOne = deregisterRecords.stream()
                .max(Comparator.comparing(UserDeregisterRecord::getDeregisterTime));

        if (!lastOne.isPresent()) {
            return;
        }

        /* 销户时间小于90天不允许再次注册 */
        long diffDay = DateUtils.dayDiff(new Date(), lastOne.get().getDeregisterTime());
        if (DELETE_USER_AGAIN_REGISTER_INTERVAL >= diffDay) {
            throw new UserRegisterLoginException("距离上次销户时间小于90天，无法再次注册");
        }
    }
}
