package com.izouma.zhirongip.service;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.hutool.core.util.ObjectUtil;
import com.izouma.zhirongip.config.Constants;
import com.izouma.zhirongip.domain.User;
import com.izouma.zhirongip.dto.PageQuery;
import com.izouma.zhirongip.dto.UserRegister;
import com.izouma.zhirongip.enums.AuthorityName;
import com.izouma.zhirongip.exception.BusinessException;
import com.izouma.zhirongip.repo.InstitutionRepo;
import com.izouma.zhirongip.repo.PersonalRepo;
import com.izouma.zhirongip.repo.UserRepo;
import com.izouma.zhirongip.security.Authority;
import com.izouma.zhirongip.security.JwtTokenUtil;
import com.izouma.zhirongip.security.JwtUserFactory;
import com.izouma.zhirongip.service.sms.SmsService;
import com.izouma.zhirongip.service.storage.StorageService;
import com.izouma.zhirongip.utils.JpaUtils;
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.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Set;
import java.util.UUID;

@Service
@Slf4j
@AllArgsConstructor
public class UserService {
    private final UserRepo        userRepo;
    private final WxMaService     wxMaService;
    private final WxMpService     wxMpService;
    private final SmsService      smsService;
    private final StorageService  storageService;
    private final JwtTokenUtil    jwtTokenUtil;
    private final CaptchaService  captchaService;
    private final PersonalRepo    personalRepo;
    private final InstitutionRepo institutionRepo;

    public Page<User> all(PageQuery pageQuery) {
        return userRepo.findAll(JpaUtils.toSpecification(pageQuery, User.class), JpaUtils.toPageRequest(pageQuery));
    }

    public User create(UserRegister userRegister) {
        User user = new User();
        BeanUtils.copyProperties(userRegister, user);
        if (StringUtils.isNotBlank(userRegister.getPassword())) {
            user.setPassword(new BCryptPasswordEncoder().encode(userRegister.getPassword()));
        }
        return userRepo.save(user);
    }

    public void del(Long id) {
        User user = userRepo.findById(id).orElseThrow(new BusinessException("用户不存在"));
        user.setDel(true);
        if (StringUtils.isNoneEmpty(user.getOpenId())) {
            user.setOpenId(user.getOpenId() + "###" + RandomStringUtils.randomAlphabetic(8));
        }
        if (StringUtils.isNoneEmpty(user.getPhone())) {
            user.setPhone(user.getPhone() + "###" + RandomStringUtils.randomAlphabetic(8));
        }
        userRepo.save(user);
        Set<Authority> authorities = user.getAuthorities();
        if (authorities.contains(Authority.get(AuthorityName.ROLE_INSTITUTION))) {
            institutionRepo.softDeleteByUserId(id);
        } else if (authorities.contains(Authority.get(AuthorityName.ROLE_PERSONAL))) {
            personalRepo.softDeleteByUserId(id);
        }
    }

    public User loginByPhone(String phone) {
        return userRepo.findByPhoneAndDelFalse(phone);
    }

    public User loginMp(String code) throws WxErrorException {
        WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);
        WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(accessToken, null);
        User user = userRepo.findByOpenIdAndDelFalse(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())
                    .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.findByOpenIdAndDelFalse(openId);
            if (userInfo != null) {
                return userInfo;
            }
            userInfo = User.builder()
                    .username(UUID.randomUUID().toString())
                    .nickname("用户" + RandomStringUtils.randomAlphabetic(6))
                    .openId(openId)
                    .avatar(Constants.DEFAULT_AVATAR)
                    .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.findByOpenIdAndDelFalse(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())
                    .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 = userRepo.save(user);
        return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
    }

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

    public User register(String phone, String code, String password) throws SmsService.SmsVerifyException {
        User byPhone = userRepo.findByPhoneAndDelFalse(phone);
        if (ObjectUtil.isNotNull(byPhone)) {
            throw new BusinessException("该手机号已注册");
        }
//        smsService.verify(phone, code);

        String phone1 = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
        User user = User.builder()
                .phone(phone)
                .username(phone)
                .nickname("用户" + phone1)
                .password(new BCryptPasswordEncoder().encode(password))
                .authorities(Collections.singleton(Authority.get(AuthorityName.ROLE_USER)))
                .build();
        return userRepo.save(user);
    }

    public String changePassword(Long userId, String oldPassword, String newPassword) {
        User user = userRepo.findById(userId).orElseThrow(new BusinessException("无用户"));
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if (!encoder.matches(oldPassword, user.getPassword())) {
            throw new BusinessException("密码错误");
        }
        user.setPassword(encoder.encode(newPassword));
        user = userRepo.save(user);
        return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
    }

    public String forgotPassword(String phone, String password) {
        User user = userRepo.findByPhoneAndDelFalse(phone);
        if (ObjectUtil.isNull(user)) {
            throw new BusinessException("此手机号未注册");
        }
        user.setPassword(new BCryptPasswordEncoder().encode(password));
        user = userRepo.save(user);
        return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
    }

    public User changePhone(String phone, Long id) {
        User user = userRepo.findById(id).orElseThrow(new BusinessException("无用户"));
        User byPhone = userRepo.findByPhoneAndDelFalse(phone);
        if (ObjectUtil.isNotNull(byPhone) && !byPhone.getId().equals(id)) {
            throw new BusinessException("该手机号已注册");
        }
        user.setUsername(phone);
        user.setPhone(phone);
        String phone1 = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
        user.setNickname(phone1);
        return userRepo.save(user);
    }

}
