package org.springblade.wx.controller;

import cn.hutool.core.lang.Assert;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.wx.config.ConfigForClient;
import org.springblade.wx.utils.Sign;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
@RequestMapping("/wx")
@Api(tags = "登录模块")
public class WXController {
	@Resource
	private ConfigForClient configForClient;
	@Resource
	private StringRedisTemplate stringRedisTemplate;
	private static final String ACCESS_TOKEN = "access_token:";
	private static final String JsAPI_TICKET = "jsApi_ticket:";

	/**
	 * @param userCode 前端返回的用户code
	 * 	1、通过code获取用户的accessToken，openId等信息
	 * 	2、判断accessToken是否有效
	 * 	3、通过accessToken获取用户信息
	 * */
	@GetMapping("/getUserInfo")
	@ApiOperation("获取用户信息")
	public R getUserInfo(String userCode){
		//获取用户accessToken
		JSONObject ac = getLoginAccessToken(userCode);
		String accessToken =  Optional.ofNullable(ac.getString("access_token")).orElseThrow(() -> new RuntimeException(ac.getString("errmsg")));
		String openId =  Optional.ofNullable(ac.getString("openid")).orElseThrow(() -> new RuntimeException(ac.getString("errmsg")));
		String refreshToken =  Optional.ofNullable(ac.getString("refresh_token")).orElseThrow(() -> new RuntimeException(ac.getString("errmsg")));
		//校验Token是否有效
		if(!verifyAc(openId,accessToken)){
			accessToken = refreshToken(refreshToken);
		}
		//返回用户信息
		return R.data(userInfo(accessToken,openId));
	}

	@GetMapping("/getShareData")
	@ApiOperation("获取分享所需参数")
	public R getShareData(String url){
		//获取app的accessToken
		String accessToken = getAccessToken();
		Assert.notNull(accessToken, "获取程序accessToken失败");
		String jsApiTicket = getJsApiTicket(accessToken);
		Assert.notNull(jsApiTicket, "获取jsApiTicket失败");
		Map<String, String> ret = Sign.getSign(jsApiTicket, url);
		ret.put("appId", configForClient.getAppId());
		return R.data(ret);
	}
	/**
	 *
	 * @pram accessToken
	 * @return
	 */
	private String getJsApiTicket(String accessToken){
		String ticket = stringRedisTemplate.opsForValue().get(JsAPI_TICKET + accessToken);
		if (StringUtil.isBlank(ticket)) {
			String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ accessToken +"&type=jsapi";//这个url链接和参数不能变
			String s = HttpUtil.get(url);
			JSONObject jsonObject = JSON.parseObject(s);
			ticket = jsonObject.getString("ticket");
			if (StringUtil.isNotBlank(ticket)) {
				stringRedisTemplate.opsForValue().set(JsAPI_TICKET+accessToken,ticket,7000,TimeUnit.SECONDS);
			}
		}
		return ticket;
	}

	/**
	 * 获取accessToken
	 * 跟下面的登录accessToken是不一样的
	 * @return
	 */
	private String getAccessToken() {
		String appId = configForClient.getAppId();
		String appSecret = configForClient.getAppSecret();
		String accessTokenKey = ACCESS_TOKEN + appId;
		//从缓存中获取
		String accessToken = stringRedisTemplate.opsForValue().get(accessTokenKey);
		if (StringUtil.isBlank(accessToken)) {
			String acUrl =  "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appId+"&secret="+ appSecret;
			String s = HttpUtil.get(acUrl);
			JSONObject jsonObject = JSON.parseObject(s);
			accessToken = jsonObject.getString("access_token");
			if (StringUtil.isNotBlank(accessToken)) {
				stringRedisTemplate.opsForValue().set(accessTokenKey, accessToken, 7000, TimeUnit.SECONDS);
			}
		}
		return accessToken;
	}

	/**
	 * 获取登录的用户的accessToken
	 * @param code
	 * @return
	 */
	private JSONObject getLoginAccessToken(String code){
		String acUrl =  "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
			+ configForClient.getAppId()+"&secret="
			+ configForClient.getAppSecret()+"&code="
			+code+"&grant_type=authorization_code";
		String acString= HttpUtil.get(acUrl);
		return JSONObject.parseObject(acString);
	}
	//获取用户信息
	private JSONObject userInfo(String ac, String openId){
		String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + ac + "&openid=" + openId;
		 String res= HttpUtil.get(url);
		return JSONObject.parseObject(res);
	}
	//刷新Token
	private String refreshToken(String refreshToken){
		String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+ configForClient.getAppId()+"&grant_type=refresh_token&refresh_token="+refreshToken;
		String res = HttpUtil.get(url);
		JSONObject resObj = JSONObject.parseObject(res);
		return  Optional.ofNullable(resObj.getString("access_token")).orElseThrow(() -> new RuntimeException(resObj.getString("errmsg")));
	}
	//校验Token
	private boolean verifyAc(String openId,String accessToken){
		String url = "https://api.weixin.qq.com/sns/auth?access_token="+accessToken+"&openid="+accessToken;
		String res = HttpUtil.get(url);
		JSONObject resObj = JSONObject.parseObject(res);
		String status = resObj.getString("errmsg");
		return "ok".equals(status);
	}
}
