package com.izouma.dingdong.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 cn.hutool.core.util.StrUtil;
import com.izouma.dingdong.config.Constants;
import com.izouma.dingdong.domain.User;
import com.izouma.dingdong.dto.MerchantDTO;
import com.izouma.dingdong.enums.Identity;
import com.izouma.dingdong.exception.BusinessException;
import com.izouma.dingdong.repo.UserRepo;
import com.izouma.dingdong.security.Authority;
import com.izouma.dingdong.security.JwtTokenUtil;
import com.izouma.dingdong.security.JwtUserFactory;
import com.izouma.dingdong.service.sms.SmsService;
import com.izouma.dingdong.service.storage.StorageService;
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.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.izouma.dingdong.enums.Identity.*;

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


    /*
    创建User
     */
    public User register(String phone, String password, Identity identity) {
        User user = userRepo.findByPhoneAndIdentity(phone, identity);
        if (ObjectUtil.isNull(user)) {
            if (RIDER.equals(identity)){
                throw new BusinessException("未注册");
            }
            user = User.builder()
                    .username(phone)
                    .nickname(UUID.randomUUID().toString())
                    .blacklist(false)
                    .enabled(true)
                    .phone(phone)
                    .authorities(Collections.singleton(Authority.getInstance(Authority.NAMES.ROLE_USER)))
                    .identity(identity)
                    .money(BigDecimal.ZERO)
                    .enabled(true)
                    .build();
            if (StrUtil.isNotBlank(password)) {
                user.setPassword(new BCryptPasswordEncoder().encode(password));
            }
            userRepo.save(user);
        }
        return user;
    }

    public User merUser(MerchantDTO merchantDTO) {
        User user = userRepo.findByPhoneAndIdentity(merchantDTO.getPhone(), MERCHANT);
        if (ObjectUtil.isNotEmpty(user)) {
            throw new BusinessException("此手机号已注册");
        }

        user = User.builder()
                .avatar(merchantDTO.getLogo())
                .username(merchantDTO.getPhone())
                .identity(MERCHANT)
                .phone(merchantDTO.getPhone())
                .nickname(merchantDTO.getShowName())
                .password(new BCryptPasswordEncoder().encode(merchantDTO.getPassword()))
                .authorities(Collections.singleton(Authority.getInstance(Authority.NAMES.ROLE_USER)))
                .blacklist(false)
                .enabled(true)
                .money(BigDecimal.ZERO)
                .build();

        return userRepo.save(user);
    }

    public User loginByPhone(String phone) {
        User user = userRepo.findByPhone(phone);
        if (user == null) {
            user = User.builder()
                    .username(UUID.randomUUID().toString())
                    .nickname(phone)
                    .phone(phone)
                    .identity(USER)
                    .enabled(true)
                    .avatar(Constants.DEFAULT_AVATAR)
                    .build();
            userRepo.save(user);
        }
        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 = userRepo.save(user);
        return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
    }

}
