package com.izouma.jmrh.service;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import com.izouma.jmrh.config.Constants;
import com.izouma.jmrh.domain.User;
import com.izouma.jmrh.exception.BusinessException;
import com.izouma.jmrh.repo.UserRepo;
import com.izouma.jmrh.security.Authority;
import com.izouma.jmrh.security.JwtTokenUtil;
import com.izouma.jmrh.security.JwtUserFactory;
import com.izouma.jmrh.service.sms.SmsService;
import com.izouma.jmrh.service.storage.StorageService;
import com.izouma.jmrh.utils.SecurityUtils;
import com.warrenstrange.googleauth.GoogleAuthenticator;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;

@Service
@Slf4j
@AllArgsConstructor
public class UserService {
    private UserRepo        userRepo;
    private WxMaService     wxMaService;
    private WxMpService     wxMpService;
    private SmsService      smsService;
    private StorageService  storageService;
    private JwtTokenUtil    jwtTokenUtil;
    private MailCodeService mailCodeService;
    private CaptchaService  captchaService;

    public User loginByPhone(String phone, String code, String password) {
        User user = userRepo.findByPhone(phone);
        if (user == null) {
            if (StringUtils.isEmpty(password)) {
                throw new BusinessException("该手机未注册");
            }
            user = userRepo.save(User.builder()
                                     .avatar(Constants.DEFAULT_AVATAR)
                                     .password(new BCryptPasswordEncoder().encode(password))
                                     .enabled(true)
                                     .phone(phone)
                                     .username(phone)
                                     .nickname(phone)
                                     .authorities(Collections.singleton(new Authority(Authority.NAMES.ROLE_USER.name())))
                                     .build());
        }
        return user;
    }

    public User loginMp(String code) throws WxErrorException {
        WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);
        WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(accessToken, null);
        User user = userRepo.findByOpenId(wxMpUser.getOpenId());
        if (user == null) {
            user = User.builder()
                       .username(UUID.randomUUID().toString())
                       .nickname(wxMpUser.getNickname())
                       .avatar(wxMpUser.getHeadImgUrl())
                       .sex(wxMpUser.getSexDesc())
                       .country(wxMpUser.getCountry())
                       .province(wxMpUser.getProvince())
                       .city(wxMpUser.getCity())
                       .openId(wxMpUser.getOpenId())
                       .language(wxMpUser.getLanguage())
                       .enabled(true)
                       .authorities(Collections.singleton(Authority.builder().name("ROLE_USER").build()))
                       .build();
            userRepo.save(user);
        }
        return user;
    }

    public User loginMa(String code) {
        try {
            WxMaJscode2SessionResult result = wxMaService.jsCode2SessionInfo(code);
            String openId = result.getOpenid();
            String sessionKey = result.getSessionKey();
            User userInfo = userRepo.findByOpenId(openId);
            if (userInfo != null) {
                return userInfo;
            }
            userInfo = User.builder()
                           .username(UUID.randomUUID().toString())
                           .nickname("用户" + RandomStringUtils.randomAlphabetic(6))
                           .openId(openId)
                           .avatar(Constants.DEFAULT_AVATAR)
                           .enabled(true)
                           .authorities(Collections.singleton(Authority.builder().name("ROLE_USER").build()))
                           .build();
            userInfo = userRepo.save(userInfo);
            return userInfo;
        } catch (WxErrorException e) {
            e.printStackTrace();
        }
        throw new BusinessException("登录失败");
    }

    public User getMaUserInfo(String sessionKey, String rawData, String signature,
                              String encryptedData, String iv) {
        // 用户信息校验
        if (!wxMaService.getUserService().checkUserInfo(sessionKey, rawData, signature)) {
            throw new BusinessException("获取用户信息失败");
        }

        // 解密用户信息
        WxMaUserInfo wxUserInfo = wxMaService.getUserService().getUserInfo(sessionKey, encryptedData, iv);
        User user = userRepo.findByOpenId(wxUserInfo.getOpenId());

        String avatarUrl = Constants.DEFAULT_AVATAR;
        try {
            String path = "image/avatar/" +
                    new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) +
                    RandomStringUtils.randomAlphabetic(8) +
                    ".jpg";
            avatarUrl = storageService.uploadFromUrl(wxUserInfo.getAvatarUrl(), path);
        } catch (Exception e) {
            log.error("获取头像失败", e);
        }

        if (user == null) {

            user = User.builder()
                       .username(UUID.randomUUID().toString())
                       .nickname(wxUserInfo.getNickName())
                       .openId(wxUserInfo.getOpenId())
                       .avatar(avatarUrl)
                       .sex(wxUserInfo.getGender())
                       .country(wxUserInfo.getCountry())
                       .province(wxUserInfo.getProvince())
                       .city(wxUserInfo.getCity())
                       .enabled(true)
                       .authorities(Collections.singleton(Authority.builder().name("ROLE_USER").build()))
                       .build();
            user = userRepo.save(user);

        } else {
            user.setAvatar(avatarUrl);
            user.setNickname(wxUserInfo.getNickName());
            user.setSex(wxUserInfo.getGender());
            user.setCountry(wxUserInfo.getCountry());
            user.setProvince(wxUserInfo.getProvince());
            user.setCity(wxUserInfo.getCity());
            user = userRepo.save(user);
        }

        return user;
    }

    public String setPassword(Long userId, String password) {
        User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
        user.setPassword(new BCryptPasswordEncoder().encode(password));
        user.setPwdUpdateAt(LocalDateTime.now());
        user = userRepo.save(user);
        return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
    }

    public void updateProfile(Long userId, String orgName, String occupation, String contactName,
                              String contactPhone, String email, String idNo, String avatar) {
        User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
        if (StringUtils.isNotEmpty(orgName)) {
            user.setOrgName(orgName);
        }
        if (StringUtils.isNotEmpty(occupation)) {
            user.setOccupation(occupation);
        }
        if (StringUtils.isNotEmpty(contactName)) {
            user.setContactName(contactName);
        }
        if (StringUtils.isNotEmpty(contactPhone)) {
            user.setContactPhone(contactPhone);
        }
        if (StringUtils.isNotEmpty(email)) {
            user.setEmail(email);
        }
        if (StringUtils.isNotEmpty(idNo)) {
            user.setIdNo(idNo);
        }
        if (StringUtils.isNotEmpty(avatar)) {
            user.setAvatar(avatar);
        }
        userRepo.save(user);
    }

    public User registerByMail(String username, String mail, String password) {
        User user = userRepo.findByEmail(mail);
        if (user != null) {
            throw new BusinessException("此邮箱已注册");
        }
        user = userRepo.findByUsername(username);
        if (user != null) {
            throw new BusinessException("此用户名已注册");
        }
        user = userRepo.save(User.builder()
                                 .avatar(Constants.DEFAULT_AVATAR)
                                 .password(new BCryptPasswordEncoder().encode(password))
                                 .enabled(true)
                                 .username(username)
                                 .nickname(username)
                                 .email(mail)
                                 .authorities(Collections.singleton(new Authority(Authority.NAMES.ROLE_USER.name())))
                                 .build());
        return user;
    }

    public void updatePassWord(String mail, String code, String password) {
        mailCodeService.verify(mail, code);

        User user = userRepo.findByEmail(mail);
        if (user == null) {
            throw new BusinessException("此邮箱未注册");
        }
        user.setPassword(new BCryptPasswordEncoder().encode(password));
        user.setPwdUpdateAt(LocalDateTime.now());
        userRepo.save(user);
    }

    public String setPasswordCaptcha(Long userId, String key, String code, String password) {
        if (!captchaService.verify(key, code)) {
            throw new BusinessException("验证码错误");
        }
        return setPassword(userId, password);
    }

    public void bindTFA(String password, String key, String code) {
        if (!new BCryptPasswordEncoder().matches(password, SecurityUtils.getAuthenticatedUser().getPassword())) {
            throw new BusinessException("密码错误");
        }
        GoogleAuthenticator gAuth = new GoogleAuthenticator();
        if (!gAuth.authorize(key, Integer.parseInt(code))) {
            throw new BusinessException("谷歌验证码错误");
        }
        User user = SecurityUtils.getAuthenticatedUser();
        user.setGAuthKey(key);
        userRepo.save(user);
    }

    public void verifyTAF(String code) {
        GoogleAuthenticator gAuth = new GoogleAuthenticator();
        String key = SecurityUtils.getAuthenticatedUser().getGAuthKey();
        if (!gAuth.authorize(key, Integer.parseInt(code))) {
            throw new BusinessException("谷歌验证码错误");
        }
    }
}
