package org.springblade.wx.service;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.protocol.HTTP;
import org.springblade.common.cache.WechatCache;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.log.logger.BladeLogger;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.wx.config.*;
import org.springblade.wx.constant.MessageTemplate;
import org.springblade.wx.constant.MiniProgramType;
import org.springblade.wx.constant.ScopeTypeEnum;
import org.springblade.wx.constant.WeChatApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author: lianghanqiang
 * @description: 微信服务
 * @since: 9/6/21 -- 8:09 PM
 */
@Service
@Slf4j
public class WeChatService {

	@Autowired
	BladeRedis bladeRedis;

	@Autowired
	private BladeLogger bladeLogger;

	/**
	 * 小程序发送模板消息
	 *
	 * @return
	 */
	public HttpResponse sendTemplateMessage(String openId, MessageTemplate messageTemplate, JSONObject content, String page, MiniProgramType miniProgramType) {

		Assert.notNull(messageTemplate, () -> {
			throw new ServiceException("找不到响应的消息模板");
		});
		String token = fetchAccessToken(miniProgramType);
		String query = "?access_token=" + token;
		HttpResponse execute = HttpRequest.post(WeChatApi.sendTemplateMessage.getApi() + query)
			.body(messageTemplate.buildRequestBody(openId, content, page))
			.execute();
		log.info("微信发送消息模板响应:{}", execute.body());
		return execute;
	}

	/**
	 * 获取小程序的AccessToken
	 */
	public String fetchAccessToken(MiniProgramType type) {

		WeChatConfig config = getConfig(type);
		String accessToken = WechatCache.getAccessToken(config.getAppId());

		//缓存没有请求微信
		if (Objects.isNull(accessToken)) {
			HttpResponse response = HttpRequest.get(WeChatApi.fetchAccessToken.getApi())
				.form("grant_type", "client_credential")
				.form("appid", config.getAppId())
				.form("secret", config.getAppSecret())
				.execute();
			JSONObject jsonObject = JSON.parseObject(response.body());
			log.info("微信获取AccessToken信息:{}", jsonObject);

			accessToken = jsonObject.getString("access_token");

			if (StringUtil.isNotBlank(accessToken)) {
				WechatCache.putAccessToken(config.getAppId(), accessToken);
			} else {
				bladeLogger.error("微信获取AccessToken错误信息", jsonObject.toJSONString());
			}
		}

		return accessToken;
	}

	/**
	 * 获取微信H5授权链接
	 *
	 * @param type
	 * @param redirectUri 重定向地址
	 * @return
	 */
	public String getWeiXinH5Oauth2AuthorizeUrl(MiniProgramType type, String redirectUri, ScopeTypeEnum scopeTypeEnum) throws UnsupportedEncodingException {

		WeChatConfig config = getConfig(type);

		//构建请求链接
		StringBuilder stringBuilder = new StringBuilder(WeChatApi.getWeiXinH5Oauth2AuthorizeUrl.getApi())
			.append("?appid=")
			.append(config.getAppId())
			.append("&redirect_uri=")
			.append(URLEncoder.encode(redirectUri, "UTF-8"))
			.append("&response_type=code&scope=")
			.append(scopeTypeEnum.name())
			.append("&state=STATE#wechat_redirect");

		log.info("微信H5用户授权链接:{}", stringBuilder);

		return stringBuilder.toString();
	}

	/**
	 * 获取微信H5 AccessToken
	 *
	 * @param type
	 * @param code
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public String getWeiXinH5AccessToken(MiniProgramType type, String code) {

		WeChatConfig config = getConfig(type);

		//请求微信
		HttpResponse response = HttpRequest.get(WeChatApi.getWeiXinH5AccessToken.getApi())
			.form("appid", config.getAppId())
			.form("secret", config.getAppSecret())
			.form("code", code)
			.form("grant_type", "authorization_code")
			.execute();

		JSONObject jsonObject = JSON.parseObject(response.body());
		log.info("微信获取AccessToken信息:{}", jsonObject);

		String access_token = jsonObject.getString("access_token");
		if (StringUtils.isEmpty(access_token)) {
			bladeLogger.error("微信获取AccessToken信息错误", response.body());
		}

		return access_token;
	}

	/**
	 * 获取微信H5用户信息
	 *
	 * @param accessToken
	 * @return
	 */
	public JSONObject getWeiXinH5UserInfo(String accessToken) {

		//请求微信
		HttpResponse response = HttpRequest.get(WeChatApi.getWeiXinH5UserInfo.getApi())
			.form("access_token", accessToken)
			.form("openid", "OPENID")
			.form("lang", "zh_CN")
			.execute();

		JSONObject jsonObject = JSON.parseObject(response.body());
		log.info("通过AccessToken获取微信用户信息:{}", jsonObject);

		if (StringUtils.isNotEmpty(jsonObject.getString("errcode"))) {
			bladeLogger.error("微信获取用户信息错误", response.body());
			return null;
		}

		return jsonObject;
	}

	/**
	 * 获取openId
	 *
	 * @return
	 */
	public JSONObject getOpenId(String code, MiniProgramType type) {
		WeChatConfig config = getConfig(type);
		String appId = config.getAppId();
		String appSecret = config.getAppSecret();

		StringBuilder sb = new StringBuilder("https://api.weixin.qq.com/sns/jscode2session?appid=");
		sb.append(appId)
			.append("&secret=")
			.append(appSecret)
			.append("&js_code=")
			.append(code)
			.append("&grant_type=authorization_code");
		String url = sb.toString();
		String s = HttpUtil.get(url);
		JSONObject jsonObject = JSON.parseObject(s);
		return jsonObject;
	}

	/**
	 * OCR-营业执照
	 *
	 * @return
	 */
	public HttpResponse ocrBizilicense(String imgUrl, MiniProgramType type) {
		String token = fetchAccessToken(type);
		HttpResponse response = HttpRequest.post(WeChatApi.OCR_BIZLICENSE.buildUrl(imgUrl, token)).execute();
		return response;
	}

	/**
	 * OCR-身份证
	 *
	 * @return
	 */
	public HttpResponse ocrIdcard(String imgUrl, MiniProgramType type) {
		String token = fetchAccessToken(type);
		HttpResponse response = HttpRequest.post(WeChatApi.OCR_ID_CARD.buildUrl(imgUrl, token)).execute();
		return response;
	}

	/**
	 * OCR-银行卡
	 *
	 * @return
	 */
	public HttpResponse ocrBank(String imgUrl, MiniProgramType type) {
		String token = fetchAccessToken(type);
		HttpResponse response = HttpRequest.post(WeChatApi.OCR_BANK.buildUrl(imgUrl, token)).execute();
		return response;
	}

	/**
	 * 生成小程序码--无数量限制
	 *
	 * @return
	 */
	public HttpResponse getUnlimitCode(String queryData, MiniProgramType type, String page) {
		String token = fetchAccessToken(type);
		Map<String, String> params = new HashMap() {{
			put("page", page);
			put("scene", queryData);
		}};
		HttpResponse response = HttpRequest.post(WeChatApi.getUnLimit.buildUrl(token)).header(HTTP.CONTENT_TYPE, "application/json").body(JSON.toJSONString(params)).execute();

		return response;
	}


	/**
	 * 获取对应小程序的配置文件
	 */
	private WeChatConfig getConfig(MiniProgramType type) {
		switch (type) {
			case MALL:
				return SpringUtil.getBean(ConfigForMall.class);
			case SHOP:
				return SpringUtil.getBean(ConfigForShop.class);
			case CLIENT:
				return SpringUtil.getBean(ConfigForClient.class);
			case OFFICIAL:
				return SpringUtil.getBean(ConfigForOfficial.class);
			default:
				return null;
		}
	}
}
