package com.izouma.jiashanxia.service;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.hutool.core.util.ObjectUtil;
import com.izouma.jiashanxia.config.Constants;
import com.izouma.jiashanxia.domain.User;
import com.izouma.jiashanxia.dto.PageQuery;
import com.izouma.jiashanxia.dto.UserRegister;
import com.izouma.jiashanxia.enums.AuthorityName;
import com.izouma.jiashanxia.enums.TransactionType;
import com.izouma.jiashanxia.exception.BusinessException;
import com.izouma.jiashanxia.repo.CommissionRecordRepo;
import com.izouma.jiashanxia.repo.OrderInfoRepo;
import com.izouma.jiashanxia.repo.UserRepo;
import com.izouma.jiashanxia.security.Authority;
import com.izouma.jiashanxia.security.JwtTokenUtil;
import com.izouma.jiashanxia.security.JwtUserFactory;
import com.izouma.jiashanxia.service.sms.SmsService;
import com.izouma.jiashanxia.service.storage.StorageService;
import com.izouma.jiashanxia.utils.JpaUtils;
import com.izouma.jiashanxia.utils.SecurityUtils;
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 javax.persistence.criteria.Predicate;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@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 CaptchaService       captchaService;
    private OrderInfoRepo        orderInfoRepo;
    private CommissionRecordRepo commissionRecordRepo;

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

    public User create(UserRegister userRegister) {
        User user = new User();
        BeanUtils.copyProperties(userRegister, user);
        user.setVip(false);
        user.setWxAuthorized(false);
        user.setAmount(BigDecimal.ZERO);
        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);
    }

    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, Long parent) {
        try {
            WxMaJscode2SessionResult result = wxMaService.jsCode2SessionInfo(code);
            String openId = result.getOpenid();
            String sessionKey = result.getSessionKey();
            User userInfo = userRepo.findByOpenIdAndDelFalse(openId);
            if (userInfo != null) {
                // 上级不为空 && 用户上级为空
                if (ObjectUtil.isNotEmpty(parent) && ObjectUtil.isNull(userInfo.getParent())) {
                    userInfo.setParent(parent);
                    userRepo.saveAndFlush(userInfo);
                }
                userInfo.setSessionKey(sessionKey);
                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()))
                    .parent(parent)
                    .amount(BigDecimal.ZERO)
                    .teamFounder(false)
                    .sessionKey(sessionKey)
                    .build();
            userInfo = userRepo.saveAndFlush(userInfo);
            log.info("loginMa {}", userInfo.getCreatedAt());
            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()))
                    .amount(BigDecimal.ZERO)
                    .teamFounder(false)
                    .wxAuthorized(true)
                    .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.setWxAuthorized(true);
            user = userRepo.save(user);
        }

        return user;
    }

    public User getMaPhone(String sessionKey, String encryptedData, String iv) {
        // 解密用户信息
        WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
        User user = userRepo.findById(SecurityUtils.getAuthenticatedUser().getId())
                .orElseThrow(new BusinessException("用户不存在"));
        user.setPhone(phoneNoInfo.getPhoneNumber());
        return userRepo.save(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 void updateUserInfo(Map<String, Object> data) {
        User user = userRepo.findById(SecurityUtils.getAuthenticatedUser().getId())
                .orElseThrow(new BusinessException("用户不存在"));
        try {
            org.apache.commons.beanutils.BeanUtils.populate(user, data);
        } catch (IllegalAccessException | InvocationTargetException e) {
            log.error("updateUserInfo", e);
        }
        userRepo.save(user);
    }

    /*
    我的推广
    */
    public List<User> myPromotion(Long userId) {
        User user = userRepo.findById(userId).orElseThrow(new BusinessException("无用户"));
        List<User> users = new ArrayList<>();
        // 如果是企业创始人，查出下级所有推广
        if (user.getTeamFounder()) {
            List<User> employees = userRepo.findAllByCompanyIdAndDelFalse(user.getCompanyId());
            List<Long> collect = employees.stream().map(User::getId).collect(Collectors.toList());
            users.addAll(userRepo.findAllByParentInAndDelFalse(collect));
        }
        users.addAll(userRepo.findAllByParentAndDelFalse(userId));
        return users;
    }

    /*
    我的推广
    移除员工：推广列表下员工的推广就会没有
    */
    public Page<User> myPromotion(PageQuery pageQuery, Long userId) {
        User user = userRepo.findById(userId).orElseThrow(new BusinessException("无用户"));

        return userRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = JpaUtils.toPredicates(pageQuery, User.class, root, criteriaQuery, criteriaBuilder);
            if (user.getTeamFounder()) {
                List<User> employees = userRepo.findAllByCompanyIdAndDelFalse(user.getCompanyId());
                List<Long> collect = employees.stream().map(User::getId).collect(Collectors.toList());
                and.add(root.get("parent").in(collect));
            } else {
                and.add(criteriaBuilder.equal(root.get("parent"), userId));
            }
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        }), JpaUtils.toPageRequest(pageQuery));
    }

    /*
    我的员工
    */
    public Page<User> myEmployee(PageQuery pageQuery, User user) {
//        User user = userRepo.findById(id).orElseThrow(new BusinessException("无用户"));
        Set<Authority> authorities = user.getAuthorities();
        // 管理员返回所有
        if (authorities.contains(Authority.get(AuthorityName.ROLE_ADMIN))) {
            Map<String, Object> query = pageQuery.getQuery();
            Object companyId = query.get("companyId");
            return userRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
                List<Predicate> and = JpaUtils
                        .toPredicates(pageQuery, User.class, root, criteriaQuery, criteriaBuilder);
                if (ObjectUtil.isNotEmpty(companyId)) {
                    long id = Long.parseLong(String.valueOf(companyId));
                    and.add(criteriaBuilder.equal(root.get("companyId"), id));
                } else {
                    and.add(root.get("companyId").isNotNull());
                }

                return criteriaBuilder.and(and.toArray(new Predicate[0]));
            }), JpaUtils.toPageRequest(pageQuery));
        }

        if (ObjectUtil.isNull(user.getCompanyId())) {
            throw new BusinessException("无企业");
        }
        // 返回企业员工
        return userRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = JpaUtils.toPredicates(pageQuery, User.class, root, criteriaQuery, criteriaBuilder);
            and.add(criteriaBuilder.equal(root.get("companyId"), user.getCompanyId()));
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        }), JpaUtils.toPageRequest(pageQuery));
    }

    public Map<String, Object> overview(Long userId) {
        Map<String, Object> map = new HashMap<>();
        map.put("orderCount", orderInfoRepo.countByUserId(userId));
        BigDecimal empPromote = commissionRecordRepo.sumByUserId(userId, TransactionType.EMPLOYEES_PROMOTE);
        BigDecimal promote = commissionRecordRepo.sumByUserId(userId, TransactionType.PROMOTE);
        if (ObjectUtil.isNull(empPromote)) {
            empPromote = BigDecimal.ZERO;
        }
        if (ObjectUtil.isNull(promote)) {
            promote = BigDecimal.ZERO;
        }
        map.put("commission", empPromote.add(promote));
        map.put("withdraw", commissionRecordRepo.sumByUserId(userId, TransactionType.WITHDRAW));
        map.put("promote", userRepo.countByParentAndDelFalse(userId));
        return map;
    }
}
