package com.zhiqiyun.open.service.impl;

import com.zhiqiyun.open.enmus.AccountType;
import com.zhiqiyun.open.enmus.PasswordType;
import com.zhiqiyun.open.enmus.UserState;
import com.zhiqiyun.open.exception.ServiceException;
import com.zhiqiyun.open.mapper.UserBaseInfoMapper;
import com.zhiqiyun.open.mapper.UserLoginAccountMapper;
import com.zhiqiyun.open.mapper.UserPasswordMapper;
import com.zhiqiyun.open.models.*;
import com.zhiqiyun.open.mybatis.paginator.domain.PageBounds;
import com.zhiqiyun.open.mybatis.paginator.domain.PageResult;
import com.zhiqiyun.open.service.Ip2RegionService;
import com.zhiqiyun.open.service.OauthService;
import com.zhiqiyun.open.service.SequenceService;
import com.zhiqiyun.open.service.UserBaseInfoService;
import com.zhiqiyun.open.utils.DateUtil;
import com.zhiqiyun.open.utils.RandomUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

/**
 * @author jtoms
 */
@Service
public class UserBaseInfoServiceImpl implements UserBaseInfoService {

    @Autowired
    private UserBaseInfoMapper userBaseInfoMapper;

    @Autowired
    private UserLoginAccountMapper userLoginAccountMapper;

    @Autowired
    private UserPasswordMapper userPasswordMapper;

    @Autowired
    private OauthService oauthService;

    @Autowired
    private SequenceService sequenceService;

    @Autowired
    private Ip2RegionService ip2RegionService;

    @Override
    public UserBaseInfo findById(Long id) {
        return this.userBaseInfoMapper.findById(id);
    }

    @Override
    public PageResult<UserBaseInfo> findPage(PageBounds bounds, Map<String, Object> queryWrapper) {
        List<UserBaseInfo> listData = this.userBaseInfoMapper.findPage(bounds, queryWrapper);

        return bounds.newPageResult(listData);
    }

    @Override
    public void updateById(UserBaseInfo userInfo) {
        this.userBaseInfoMapper.updateById(userInfo);
    }

    @Override
    public UserLoginAccount findLoginAccount(String loginAccount, AccountType accountType) {
        return this.userLoginAccountMapper.findLoginAccount(loginAccount, accountType);
    }

    @Override
    public UserPassword findPassword(Long uid, PasswordType passwordType) {
        return this.userPasswordMapper.findPassword(uid, passwordType);
    }

    @Override
    public List<String> findUserAuthorities(Long uid) {
        return this.userBaseInfoMapper.findUserAuthorities(uid);
    }

    @Override
    public List<RoleInfo> findRoles(Long uid) {
        return this.userBaseInfoMapper.findRoles(uid);
    }

    @Override
    public void updatePassword(Long uid, String password, String salt, PasswordType passwordType) {
        UserPassword userPassword = new UserPassword(uid, password, salt, passwordType);
        this.userPasswordMapper.insertOrUpdatePassword(userPassword);
    }

    @Override
    public void updateBaseInfo(Long uid, UserBaseInfo userBaseInfo) {
        userBaseInfo.setId(uid);
        this.userBaseInfoMapper.updateById(userBaseInfo);
    }

    @Override
    public void updateLoginAccount(Long uid, String account, AccountType accountType) {
        UserLoginAccount loginAccount = new UserLoginAccount(uid, account, accountType);
        userLoginAccountMapper.insertOrUpdate(loginAccount);
    }

    @Override
    public List<Map<String, Object>> findSelectAccounts(String searchKey) {
        return this.userBaseInfoMapper.findSelectAccounts(searchKey);
    }

    @Override
    public List<UserLoginAccount> findLoginAccounts(Long uid) {
        return this.userLoginAccountMapper.findLoginAccounts(uid);
    }

    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public String insertOrUpdate(UserBaseInfo userInfo, List<UserLoginAccount> loginAccounts) throws ServiceException {
        try {
            String loginPassword = null;
            if (userInfo.getId() == null) {
                Long uid = this.sequenceService.nextId();
                userInfo.setId(uid);
                userInfo.setState(UserState.normal);
                userInfo.setRegisterTime(DateUtil.current());
                if (StringUtils.isBlank(userInfo.getAvatar())) {
                    userInfo.setAvatar("");
                }
                loginPassword = RandomUtil.get(6);
                this.userBaseInfoMapper.insert(userInfo);
            } else {
                this.updateById(userInfo);
            }

            userLoginAccountMapper.deleteByUid(userInfo.getId());
            for (UserLoginAccount loginAccount : loginAccounts) {
                loginAccount.setUid(userInfo.getId());
                userLoginAccountMapper.insertOrUpdate(loginAccount);
            }

            if (StringUtils.isNotBlank(loginPassword)) {
                String salt = RandomUtil.getSalt();
                String secretPassword = this.oauthService.generatePassword(loginPassword, salt);

                UserPassword userPassword = new UserPassword(userInfo.getId(), secretPassword, salt, PasswordType.login);
                this.userPasswordMapper.insertOrUpdatePassword(userPassword);
            }
            return loginPassword;
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public void saveRoles(Long uid, Long[] roles) throws ServiceException {
        try {
            userBaseInfoMapper.deleteRoles(uid);
            if (roles != null && roles.length > 0) {
                userBaseInfoMapper.insertRoles(uid, roles);
            }
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    @Override
    public void insertOperateLog(UserOperateLog userOperateLog) {
        this.userBaseInfoMapper.insertOperateLog(userOperateLog);
    }

    @Override
    public void updateOperateLogById(UserOperateLog userOperateLog) {
        this.userBaseInfoMapper.updateOperateLogById(userOperateLog);
    }

    @Override
    public void insertLoginLog(UserLoginLog userLoginLog) {
        try {
            userLoginLog.setLoginLocal(this.ip2RegionService.getRegion(userLoginLog.getLoginIp()));
        } finally {
            userBaseInfoMapper.insertLoginLog(userLoginLog);
        }
    }

    @Override
    public PageResult<UserLoginLog> findLoginLogPage(PageBounds bounds, Long uid) {
        List<UserLoginLog> listData = userBaseInfoMapper.findLoginLogPage(bounds, uid);
        return bounds.newPageResult(listData);
    }
}
