package com.crunii.micro.service.sysmgr.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.crunii.micro.common.dto.Result;
import com.crunii.micro.common.exception.BusinessException;
import com.crunii.micro.domain.dto.Worker;
import com.crunii.micro.domain.entity.main.TsmWorker;
import com.crunii.micro.domain.entity.main.WechatJsConfigVO;
import com.crunii.micro.domain.repo.main.TsmWorkerRepo;
import com.crunii.micro.feign.client.dto.WechatLoginDto;
import com.crunii.micro.feign.client.dto.WechatLoginVo;
import com.crunii.micro.service.sysmgr.config.WechatProperties;
import com.crunii.micro.service.sysmgr.constants.SysmgrConstants;
import com.crunii.micro.service.sysmgr.enums.WechatLoginEnum;
import com.crunii.micro.service.sysmgr.service.LoginService;
import com.crunii.micro.service.sysmgr.service.WechatService;
import com.crunii.micro.service.sysmgr.utils.DigestUtil;
import com.crunii.micro.service.sysmgr.utils.WeChatUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * @author 15294
 */
@Slf4j
@Service
public class WechatServiceImpl implements WechatService {

    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    private static final Random random = new Random();

    @Resource
    private WechatProperties wechatProperties;

    @Value("${wechat.appid:wx6b28d63e2e294fd6}")
    private String appId;

    @Resource
    private WeChatUtil weChatUtil;

    @Autowired
    private TsmWorkerRepo tsmWorkerRepo;

    @Autowired
    private LoginService loginService;

    /**
     * 获取授权地址
     * @return
     * @throws UnsupportedEncodingException
     */
    @Override
    public String authorization(String url) throws UnsupportedEncodingException {
        if (StringUtils.isBlank(url)){
            throw new BusinessException("invalid param");
        }
        StringBuilder builder = new StringBuilder(SysmgrConstants.OPEN_WEI_XIN_AUTH_URL);
        builder.append("/connect/oauth2/authorize?appid=")
               .append(wechatProperties.getAppId())
               .append("&redirect_uri=")
               .append(URLEncoder.encode(url, "UTF-8"))
               .append("&response_type=code")
               .append("&scope=snsapi_base")
               .append("#wechat_redirect");
        String s = builder.toString();
        log.info("授权重定向地址：{}", s);
        return s;
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        StringBuilder builder = new StringBuilder(SysmgrConstants.OPEN_WEI_XIN_AUTH_URL);
        builder.append("/connect/oauth2/authorize?appid=")
                .append("wx0592cf14c40bbca1")
                .append("&redirect_uri=")
                .append(URLEncoder.encode("https://test.wechat.youth.cq.cqyl.org.cn/", "UTF-8"))
                .append("&response_type=code")
                .append("&scope=snsapi_base")
                .append("#wechat_redirect");
        System.out.println(builder.toString());
    }

    @Override
    public String serverAuth(Map<String, String> map) throws NoSuchAlgorithmException {
        String nonce = map.get("nonce");
        String signature = map.get("signature");
        String timestamp = map.get("timestamp");
        String echostr = map.get("echostr");
        String join = Stream.of(nonce, timestamp, wechatProperties.getToken()).sorted().collect(Collectors.joining(""));
        MessageDigest digest = MessageDigest.getInstance("SHA-1");
        digest.update(join.getBytes(StandardCharsets.UTF_8));
        byte[] digest1 = digest.digest();
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : digest1) {
            String s = Integer.toHexString(0xFF & b);
            if (s.length() == 1){
                stringBuilder.append('0');
            }
            stringBuilder.append(s);
        }
        String s = stringBuilder.toString();
        if (Objects.equals(s, signature)){
            return echostr;
        }
        return null;
    }

    @Override
    public String getOpenId(String code){
        return weChatUtil.getUserInfo(code);
    }

    @Override
    public Result<WechatLoginVo> wechatLogin(HttpServletRequest request, WechatLoginDto dto) {
        log.info("微信跳转登录请求入参：{}", JSONObject.toJSONString(dto));
        String openid = dto.getOpenid();
        String code = dto.getCode();
        String mobile = dto.getMobile();
        WechatLoginVo result = new WechatLoginVo();
        if (StringUtils.isBlank(openid) && StringUtils.isBlank(code)){
            return Result.ofFail("invalid param");
        }
        if (StringUtils.isBlank(openid)){
            // openid为空 代表需要授权获取openid并且尝试登录
            openid = weChatUtil.getUserInfo(code);
            dto.setOpenid(openid);
            tryLogin(request, dto, result, false);
        }else if (StringUtils.isBlank(mobile) && StringUtils.isNotBlank(openid)){
            tryLogin(request, dto, result, false);
        }else if (StringUtils.isNotBlank(mobile) && StringUtils.isNotBlank(openid)){
            if(StringUtils.isBlank(dto.getSmsCode())){
                return Result.ofFail("验证码不能为空");
            }
            tryLogin(request, dto, result, true);
        }else {
            return Result.ofFail("信息错误，无法登录");
        }
        return Result.ofSuccess(result);
    }

    private void tryLogin(HttpServletRequest request, WechatLoginDto dto, WechatLoginVo result, boolean manualLogin){
        // 根据openid查询用户信息
        String openid = dto.getOpenid();

        HashMap<String, Object> queryMap = new HashMap<>();
        if (manualLogin){
            String mobile = dto.getMobile();
            queryMap.put("mobile", mobile);
            List<TsmWorker> tsmWorkers = tsmWorkerRepo.findByProps(queryMap);
            if (tsmWorkers != null && tsmWorkers.size() > 1){
                log.error("用户信息异常，openid：{}，mobile：{}", openid, mobile);
                throw new BusinessException("用户信息异常，请联系管理员");
            }
            Worker worker;
            if (CollectionUtils.isEmpty(tsmWorkers)){
                worker = loginService.wechatDoLogin(request, dto, null, true);
            }else {
                worker = loginService.wechatDoLogin(request, dto, tsmWorkers.get(0), true);
            }
            BeanUtils.copyProperties(worker, result);
            result.setType(WechatLoginEnum.AUTH.getType());
            result.setOpenid(openid);
        }else {
            queryMap.put("openid", openid);
            List<TsmWorker> tsmWorkers = tsmWorkerRepo.findByProps(queryMap);
            if (CollectionUtils.isEmpty(tsmWorkers)){
                result.setOpenid(openid);
                result.setType(WechatLoginEnum.UN_AUTH.getType());
            }else if (tsmWorkers.size() == 1){
                // 登录 生成token 并返回
                dto.setMobile(tsmWorkers.get(0).getMobile());
                Worker worker = loginService.wechatDoLogin(request, dto, tsmWorkers.get(0), false);
                BeanUtils.copyProperties(worker, result);
                result.setType(WechatLoginEnum.AUTH.getType());
                result.setOpenid(openid);
            }else if (tsmWorkers.size() > 1){
                // 返回所有用户信息
                TsmWorker tsmWorker = tsmWorkers.stream().max(Comparator.comparing(TsmWorker::getLastLoginTime)).get();
                dto.setMobile(tsmWorker.getMobile());
                Worker worker = loginService.wechatDoLogin(request, dto, tsmWorker, false);
                BeanUtils.copyProperties(worker, result);
                result.setType(WechatLoginEnum.AUTH.getType());
                result.setOpenid(openid);
            }else {
                log.error("登录信息异常，请联系系统管理员！");
                throw new RuntimeException("登录信息异常，请联系系统管理员！");
            }
        }
    }

    @Override
    public Result<WechatJsConfigVO> getJsConfig(String url){
        if (StringUtils.isBlank(url)){
            throw new BusinessException("invalid param");
        }
        WechatJsConfigVO wechatJsConfigVO = new WechatJsConfigVO();
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = random.ints(12, 0, CHARACTERS.length())
                .mapToObj(CHARACTERS::charAt)
                .map(Object::toString)
                .collect(Collectors.joining());
        String ticket = weChatUtil.getTicket();
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("jsapi_ticket=").append(ticket)
                .append("&noncestr=").append(nonceStr)
                .append("&timestamp=").append(timestamp)
                .append("&url=").append(url);
        String s = stringBuilder.toString();
        String signature = DigestUtil.sha1(s);
        wechatJsConfigVO.setSignature(signature);
        wechatJsConfigVO.setTimestamp(timestamp);
        wechatJsConfigVO.setAppId(appId);
        wechatJsConfigVO.setNonceStr(nonceStr);
        return Result.ofSuccess(wechatJsConfigVO);
    }
}
