package com.songhua.framework.sso;

import com.dtflys.forest.Forest;
import com.songhua.framework.sso.domain.SysLoginUser;
import com.songhua.framework.sso.utli.AjaxJson;
import com.songhua.framework.sso.utli.JsonUtil;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.Map;
import java.util.Random;

/**
 * 封装一些 sso 共用方法 
 * 
 * @author kong
 * @since: 2022-4-30
 */
@Service
public class SsoRequestUtil {


    public String host = "http://127.0.0.1:17005";

	/**
	 * SSO-Server端 统一认证地址 
	 */
	public String authUrl;

	public String getAuthUrl() {
		return authUrl = host + "/sso/auth";
	}
	/**
	 * 使用 Http 请求校验ticket 
	 */
//	public static boolean isHttp = true;
	
	/**
	 * SSO-Server端 ticket校验地址 
	 */
	public String checkTicketUrl;

	public String getCheckTicketUrl() {
		return checkTicketUrl = host + "/sso/checkTicket";
	}
	
	/**
	 * 打开单点注销功能 
	 */
	public boolean isSlo = true;
	
	/**
	 * 单点注销地址 
	 */
	public String sloUrl;

	public String getSloUrl() {
		return sloUrl = host + "/sso/signout";
	}
	/**
	 * 接口调用秘钥 
	 */
	public String secretkey = "YQfyZtAmDbYHTBaHPSx3GZeX7x2ip7ik";
	
	/**
	 * SSO-Server端 注销后跳转地址
	 */
	public String logoutRedirectUrl;

	public String getLogoutRedirectUrl() {
		return logoutRedirectUrl = host + "/home";
	}
    /**
     * SSO-Server端 查询userinfo地址
     */
    public String userinfoUrl;
	public String getUserinfoUrl() {
		return userinfoUrl = host + "/sso/userinfo";
	}
	/**
	 * 当前 client 的标识，可为 null
	 */
	public String client = "";


	// -------------------------- 封装方法 
	
	/**
     * 拼接 sso 授权地址
     * @return
     */
    public String buildServerAuthUrl(String clientLoginUrl) {
        String serverAuthUrl = getAuthUrl();
        if(isNotEmpty(client)) {
            serverAuthUrl = joinParam(serverAuthUrl, "client=" + client);
        }
        serverAuthUrl = joinParam(serverAuthUrl, "redirect=" + clientLoginUrl);
        return serverAuthUrl;
    }
	
	/**
	 * 校验 ticket，返回 userId 
	 * @param ticket ticket 码
	 * @param request 请求对象 
	 * @param currPath 当前接口的访问path 
	 * @return
	 */
	public Object checkTicket(String ticket, HttpServletRequest request, String currPath) {
		// 校验 ticket 的地址
		String checkUrl = getCheckTicketUrl() + "?ticket=" + ticket;
		
		// 如果锁定 client 的标识 
		if(isNotEmpty(client)) {
			checkUrl += "&client=" + client;
		}
		// 如果打开了单点注销 
		if(isSlo) {
			String ssoLogoutCall = request.getRequestURL().toString().replace(currPath, "/sso/logoutCall");
			checkUrl += "&ssoLogoutCall=" + ssoLogoutCall;
		}
		// 发起请求
		AjaxJson result = request(checkUrl);
		
		// 200 代表校验成功 
		if(result.getCode() == 200 && SsoRequestUtil.isEmpty(result.getData()) == false) {
			// 登录上 
			Object loginId = result.getData();
			return loginId;
		} else {
			// 将 sso-server 回应的消息作为异常抛出 
			throw new RuntimeException(result.getMsg());
		}
	}
	
	/**
	 * 获取指定用户id的详细资料  (调用此接口的前提是 sso-server 端开放了 /sso/userinfo 路由)
	 */
	public SysLoginUser getUserInfo(Object loginId) {

        // 组织 url 参数 
		String timestamp = String.valueOf(System.currentTimeMillis());	// 时间戳 
		String nonce = getRandomString(20);		// 随机字符串
		String sign = getSign(loginId, timestamp, nonce, secretkey);	// 参数签名
		
		// 请求 
        String url = getUserinfoUrl() +
        		"?loginId=" + loginId +
        		"&timestamp=" + timestamp +
        		"&nonce=" + nonce +
        		"&sign=" + sign;
        AjaxJson result = request(url);
        
        // 如果返回值的 code 不是200，代表请求失败 
        if(result.getCode() == null || result.getCode() != 200) {
        	throw new RuntimeException(result.getMsg());
        }
        
        // 解析出 user 
        SysLoginUser user = JsonUtil.parseObjectToModel(result.getData(), SysLoginUser.class);
        return user;
	}
	
	
	// -------------------------- 工具方法 
	
	/**
	 * 发出请求，并返回 SaResult 结果 
	 * @param url 请求地址 
	 * @return 返回的结果 
	 */
	public static AjaxJson request(String url) {
		System.out.println("------ 发起请求：" + url);
		Map<String, Object> map = Forest.post(url).executeAsMap();
		return new AjaxJson(map);
	}

	/**
	 * 根据参数计算签名 
	 * @param loginId 账号id
	 * @param timestamp 当前时间戳，13位
	 * @param nonce 随机字符串 
	 * @param secretkey 账号id
	 * @return 签名 
	 */
	public static String getSign(Object loginId, String timestamp, String nonce, String secretkey) {
		return md5("loginId=" + loginId + "&nonce=" + nonce + "&timestamp=" + timestamp + "&key=" + secretkey);
	}

	/**
	 * 指定元素是否为null或者空字符串
	 * @param str 指定元素 
	 * @return 是否为null或者空字符串
	 */
	public static boolean isEmpty(Object str) {
		return str == null || "".equals(str);
	}

	/**
	 * 指定元素是否不为null或者空字符串
	 * @param str 指定元素 
	 * @return 是否为null或者空字符串
	 */
	public static boolean isNotEmpty(Object str) {
		return !isEmpty(str);
	}
	
	/**
	 * md5加密 
	 * @param str 指定字符串
	 * @return 加密后的字符串
	 */
	public static String md5(String str) {
		str = (str == null ? "" : str);
		char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
		try {
			byte[] btInput = str.getBytes();
			MessageDigest mdInst = MessageDigest.getInstance("MD5");
			mdInst.update(btInput);
			byte[] md = mdInst.digest();
			int j = md.length;
			char[] strA = new char[j * 2];
			int k = 0;
			for (byte byte0 : md) {
				strA[k++] = hexDigits[byte0 >>> 4 & 0xf];
				strA[k++] = hexDigits[byte0 & 0xf];
			}
			return new String(strA);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 生成指定长度的随机字符串
	 * 
	 * @param length 字符串的长度
	 * @return 一个随机字符串
	 */
	public static String getRandomString(int length) {
		String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		Random random = new Random();
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < length; i++) {
			int number = random.nextInt(62);
			sb.append(str.charAt(number));
		}
		return sb.toString();
	}

	/**
	 * URL编码 
	 * @param url see note 
	 * @return see note 
	 */
	public static String encodeUrl(String url) {
		try {
			return URLEncoder.encode(url, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 在url上拼接上kv参数并返回 
	 * @param url url
	 * @param parameStr 参数, 例如 id=1001
	 * @return 拼接后的url字符串 
	 */
	public static String joinParam(String url, String parameStr) {
		// 如果参数为空, 直接返回 
		if(parameStr == null || parameStr.length() == 0) {
			return url;
		}
		if(url == null) {
			url = "";
		}
		int index = url.lastIndexOf('?');
		// ? 不存在
		if(index == -1) {
			return url + '?' + parameStr;
		}
		// ? 是最后一位
		if(index == url.length() - 1) {
			return url + parameStr;
		}
		// ? 是其中一位
		if(index > -1 && index < url.length() - 1) {
			String separatorChar = "&";
			// 如果最后一位是 不是&, 且 parameStr 第一位不是 &, 就增送一个 &
			if(url.lastIndexOf(separatorChar) != url.length() - 1 && parameStr.indexOf(separatorChar) != 0) {
				return url + separatorChar + parameStr;
			} else {
				return url + parameStr;
			}
		}
		// 正常情况下, 代码不可能执行到此 
		return url;
	}
}
