package com.izouma.wenlvju.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.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.izouma.wenlvju.config.Constants;
import com.izouma.wenlvju.domain.*;
import com.izouma.wenlvju.dto.ExpertDTO;
import com.izouma.wenlvju.dto.OrganizationRegDTO;
import com.izouma.wenlvju.dto.PageQuery;
import com.izouma.wenlvju.dto.UserRegister;
import com.izouma.wenlvju.enums.AuthorityName;
import com.izouma.wenlvju.exception.BusinessException;
import com.izouma.wenlvju.repo.*;
import com.izouma.wenlvju.security.Authority;
import com.izouma.wenlvju.security.JwtTokenUtil;
import com.izouma.wenlvju.security.JwtUserFactory;
import com.izouma.wenlvju.service.storage.StorageService;
import com.izouma.wenlvju.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 javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.SetJoin;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
@AllArgsConstructor
public class UserService {
    private final UserRepo                userRepo;
    private final WxMaService             wxMaService;
    private final WxMpService             wxMpService;
    private final StorageService          storageService;
    private final JwtTokenUtil            jwtTokenUtil;
    private final CaptchaService          captchaService;
    private final ArtTypeRepo             artTypeRepo;
    private final OrganizationRepo        organizationRepo;
    private final CollaborateRepo         collaborateRepo;
    private final GradingOrganizationRepo gradingOrganizationRepo;
    private final RateRepo                rateRepo;
    private final OrganizationService     organizationService;
    private final SettingRepo             settingRepo;

    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()));
        }
        if (ObjectUtil.isNotNull(userRegister.getPhone())) {
            User byPhone = userRepo.findByPhoneAndDelFalse(userRegister.getPhone());
            if (ObjectUtil.isNotNull(byPhone)) {
                throw new BusinessException("该手机号已绑定");
            }
        }
        return userRepo.save(user);
    }

    public void del(Long id) {
        User user = userRepo.findById(id).orElseThrow(new BusinessException("用户不存在"));
        Set<Authority> authorities = user.getAuthorities();
        if (authorities.contains(Authority.get(AuthorityName.ROLE_EXPERT)) || authorities
                .contains(Authority.get(AuthorityName.ROLE_DISTRICT_STAFF))) {
            // 市政专家
            if (rateRepo.countByExpertId(id) > 0) {
                throw new BusinessException("参与的等级评定的专家不能删除！");
            }
        } else if (authorities.contains(Authority.get(AuthorityName.ROLE_ORGANIZER))) {
            // 承办单位
            organizationRepo.findByUserId(id).ifPresent(organization -> organizationService.del(organization.getId()));
        }

        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) {
        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 Page<ExpertDTO> toArtType(PageQuery pageQuery, List<Authority> authorities) {
        Map<Long, String> artMap = artTypeRepo.findAll()
                .stream()
                .collect(Collectors.toMap(ArtType::getId, ArtType::getName));
        return this.byAuthority(pageQuery, authorities).map(user -> new ExpertDTO(user, artMap.get(user.getArtTypeId())));
    }

    // 等级评定分配专家列表
    public Page<ExpertDTO> rateExpert(Long rateId) {
        PageQuery pageQuery = new PageQuery();
        pageQuery.setSize(1000);
        pageQuery.getQuery().put("del", false);
        pageQuery.setSort("createdAt,desc");

        Map<Long, String> artMap = artTypeRepo.findAll()
                .stream()
                .collect(Collectors.toMap(ArtType::getId, ArtType::getName));

        Set<Long> goId = collaborateRepo.findAllByRateId(rateId)
                .stream()
                .map(Collaborate::getGradingOrganizationId)
                .collect(Collectors.toSet());
        List<String> names = gradingOrganizationRepo.findAllById(goId)
                .stream()
                .map(GradingOrganization::getName)
                .collect(Collectors.toList());

        return userRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = JpaUtils.toPredicates(pageQuery, User.class, root, criteriaQuery, criteriaBuilder);
            // 特定权限
            SetJoin<User, Authority> join = root.join(root.getModel()
                    .getSet("authorities", Authority.class), JoinType.LEFT);

            and.add(join.in(Authority.get(AuthorityName.ROLE_EXPERT), Authority.get(AuthorityName.ROLE_DISTRICT_STAFF)));

            criteriaQuery.distinct(true);
            // 排除一个工作单位的
            names.forEach(name -> and.add(criteriaBuilder.notLike(root.get("work"), "%" + name + "%")));
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        }), JpaUtils.toPageRequest(pageQuery)).map(user -> new ExpertDTO(user, artMap.get(user.getArtTypeId())));
    }


    public Page<User> byAuthority(PageQuery pageQuery, List<Authority> authorities) {
        pageQuery.getQuery().put("del", false);
        pageQuery.setSort("createdAt,desc");
        return userRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = JpaUtils.toPredicates(pageQuery, User.class, root, criteriaQuery, criteriaBuilder);
            // 特定权限
            SetJoin<User, Authority> join = root.join(root.getModel()
                    .getSet("authorities", Authority.class), JoinType.LEFT);
            and.add(join.in(authorities));
            criteriaQuery.distinct(true);
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        }), JpaUtils.toPageRequest(pageQuery));
    }

    /*
    注册承办单位
     */
    public User regOrganization(OrganizationRegDTO dto) {
        User user = new User();
        BeanUtil.copyProperties(dto, user);
        user.setNickname(dto.getUsername());
        User byPhone = userRepo.findByPhoneAndDelFalse(user.getPhone());
        if (ObjectUtil.isNotNull(byPhone)) {
            throw new BusinessException("该手机号已注册");
        }
        User byUsername = userRepo.findByUsernameAndDelFalse(dto.getUsername());
        if (ObjectUtil.isNotNull(byUsername)) {
            throw new BusinessException("该用户名已使用");
        }
        if (StringUtils.isNotBlank(dto.getPassword())) {
            user.setPassword(new BCryptPasswordEncoder().encode(dto.getPassword()));
        }
        user.setAuthorities(Collections.singleton(Authority.get(AuthorityName.ROLE_ORGANIZER)));
        user.setWork(dto.getOrganizationName());
        User save = userRepo.save(user);

        int sort = organizationRepo.nextSort();
        String code = dto.getDistrictCode();
        if (ObjectUtil.isNotEmpty(dto.getGradingCode())) {
            code += dto.getGradingCode();
        }
        code += String.format("%04d", sort);

        Organization organization = Organization.builder()
                .businessLicense(dto.getBusinessLicense())
                .userId(save.getId())
                .name(dto.getOrganizationName())
                .district(dto.getDistrict())
                .gradingOrganizationId(dto.getGradingOrganizationId())
                .sort(sort)
                .code(code)
                .build();

        //艺考联盟
        settingRepo.findByNameLikeAndFlag(dto.getOrganizationName(), 5)
                .ifPresent(setting -> {
                    if (!organizationRepo.findByAllianceId(setting.getId()).isPresent()) {
                        organization.setAllianceId(setting.getId());
                    }
                });

        organizationRepo.save(organization);

        return user;
    }
}
