package com.testor.module.iam.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.testor.module.iam.model.domain.IamDepartment;
import com.testor.module.iam.model.domain.IamSysUser;
import com.testor.module.iam.model.dto.IamApp;
import com.testor.module.iam.model.dto.IamDepartmentQueryDto;
import com.testor.module.iam.model.dto.IamLoginDto;
import com.testor.module.iam.model.dto.IamUserQueryDto;
import lombok.Data;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.NumericDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName: RequestIamUtil
 * @Description: TODO
 * @Author: lm
 * @CreateTime: 2023/12/28 10:16
 * @Version: 1.0
 */
@Data
@Component
@ConfigurationProperties(prefix = "iam")
public class RequestIamUtil {

    /**
     * iam应用列表
     */
    private List<IamApp> appList;
    /**
     * iam服务地址
     */
    private String iamUrl;

    /**
     * App的唯一标识
     */
    private String appId;
    /**
     * App调用OpenApi或SDK时需要的Secret
     */
    private String appSecret;

    private Map<String,IamApp> appMap;

    private final static Logger logger = LoggerFactory.getLogger(RequestIamUtil.class);

    private static final int TIME_OUT = 10000;

    private static RequestConfig.Builder requestConfigBuilder = null;

    @PostConstruct
    public void initMethod(){
        appMap = appList.stream().collect(Collectors.toMap(i->i.getType(), i->i));
    }


    /**
     * 创建https协议的client
     *
     * @return CloseableHttpClient
     */
    public static CloseableHttpClient createSSLClientDefault() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                //信任所有
                @Override
                public boolean isTrusted(X509Certificate[] chain,
                                         String authType) throws CertificateException {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
            return HttpClients.custom().setDefaultRequestConfig(getDefaultRequestConfig()).setSSLSocketFactory(sslsf).build();
        } catch (KeyManagementException e) {
            logger.error("创建SSLClientDefault失败:{}", ExceptionUtils.getFullStackTrace(e));
        } catch (NoSuchAlgorithmException e) {
            logger.error("创建SSLClientDefault失败:{}" + ExceptionUtils.getFullStackTrace(e));
        } catch (KeyStoreException e) {
            logger.error("创建SSLClientDefault失败:{}" + ExceptionUtils.getFullStackTrace(e));
        }
        return createClientDefault();
    }

    /**
     * 创建https协议的client
     *
     * @return CloseableHttpClient
     */
    public static CloseableHttpClient createClientDefault() {
        return HttpClients.custom().setDefaultRequestConfig(getDefaultRequestConfig()).build();
    }

    /**
     * 获取默认Request配置
     *
     * @return
     */
    private static RequestConfig getDefaultRequestConfig() {
        if (requestConfigBuilder == null) {
            requestConfigBuilder = RequestConfig.custom().setConnectTimeout(TIME_OUT).setSocketTimeout(TIME_OUT)
                    .setConnectionRequestTimeout(TIME_OUT).setStaleConnectionCheckEnabled(true);
        }
        return requestConfigBuilder.build();
    }

    /**
     * 获取iam token
     * @param iamLoginDto
     * @return
     * {
     *     "access_token":"aa950a353cfb17b24eade856072f2193",
     *     "refresh_token":"",
     *     "id_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6IkY2NzNGOTA1MkRBNTREQzY5QzlFRjNDQTI5ODVDQjkxIn0.eyJqdGkiOiJMLUZJNmRzcnZFLTJZMjVBWFJWNHFRIiwiaWF0IjoxNzA0MzYzNjQzLCJleHAiOjE3MDQ0NTAwNDMsIm5iZiI6MTcwNDM2MzU4MywiaXNzIjoiaHR0cHM6Ly95ZGJnLmNvZmNvLmNvbTo4MDAyL2F1dGgvb2lkYy9GNjczRjkwNTJEQTU0REM2OUM5RUYzQ0EyOTg1Q0I5MSIsInN1YiI6ImxpdW1hby5teSIsImF1ZCI6IlpXSTRZemt5TURJeFlqUXpOR05tTVRrMU9XSXhaak0zWXpVeU1tWmlOamMiLCJyb2xlcyI6W10sIm5hbWUiOm51bGwsImF1dGhUeXBlIjoiU01TIiwidXNlcmlkIjoibGl1bWFvLm15IiwidXNlcklkIjoidGVtcHF5d3h3d2ZhYmNlOGVmYzZmZWMyN2ZfbGl1bWFvLm15Iiwic3ViamVjdElkIjoiODM5MDA2ZGMtYzgzMC00M2JiLWIxY2QtYWExNWMyMzhhNzJjIiwiZW1haWwiOm51bGx9.bbix8YhA6Imr8WrQEXtrJt-Ih9zozQrdjHx9tCV4YywV_5HCfpibVkTij1e5F1CLMMN0pL2e8zRxSne-F1ftQuCIuxr8Cr0LydOXxO-pS3_0VfuZ9tdp2225fNLVea6kF_PFx3Is39Uv-2UhYmqUFiOn0ksuekyx4XGr1TvXb4IgdFAAEFEYby8Bi3ymYaRJG3TFgjBHtgkFqx8i953D1GR-3k5BNtd_qzorr5nRWh-XJ5uP7Bn33uKWxd5TeObYECSrj1wXOhHjIonohWkrzNpr57atMXDnNJPe9cxJVbl_9FU6PfbN1Mtt0hhh9k9U2KmN224s6WjWfQU4txKFAA",
     *     "expires_in":60
     * }
     */
    public JSONObject getToken(IamLoginDto iamLoginDto){
        String url = iamUrl + "/auth/oauth2/token";
        Map<String,Object> param = new HashMap<>();
        String type = iamLoginDto.getType();
        param.put("client_id",appMap.get(type).getClientid());
        param.put("client_secret",appMap.get(type).getClientsecret());
        param.put("redirect_uri",appMap.get(type).getRedirecturl());
        param.put("grant_type","authorization_code");
        param.put("code",iamLoginDto.getCode());
        JSONObject result = doHttpGet(url,param,null);
        logger.info("调用中粮接口------getToken----结果:{}，参数：{}，地址：{}",JSON.toJSONString(result),JSON.toJSONString(param),url);
        return result;
    }

    public List<IamSysUser> getUser(IamUserQueryDto queryDto){
        List<IamSysUser> resultList = null;
        String url = iamUrl +  "/openapi_v2/qywx_user/get";
        Map<String,Object> header = generateSdkHeader();
        Map<String,Object> param;
        param = Objects.isNull(queryDto)?null:JSON.parseObject(JSON.toJSONString(queryDto),Map.class);
        JSONObject result = doHttpGet(url,param,header);
        logger.info("调用中粮接口------getUser----结果:{}，参数：{}，地址：{}",JSON.toJSONString(result),JSON.toJSONString(param),url);
        if (Objects.nonNull(result)&&Objects.nonNull(result.get("userlist"))){
            JSONArray list = (JSONArray) result.get("userlist");
            resultList = list.toJavaList(IamSysUser.class);
        }
        return resultList;
    }

    public List<IamDepartment> getDepartmentList(IamDepartmentQueryDto dto){
        List<IamDepartment> resultList = null;
        String url = iamUrl +  "/openapi_v2/qywx_department/list";
        Map<String,Object> header = generateSdkHeader();
        Map<String,Object> param = new HashMap<>();
        if (StringUtils.isNotBlank(dto.getId()))
            param.put("id",dto.getId());
        if (StringUtils.isNotBlank(dto.getDepartmentId()))
            param.put("department_id",dto.getDepartmentId());
        if (StringUtils.isNotBlank(dto.getDepartmentName()))
            param.put("department_name",dto.getDepartmentName());
        if (StringUtils.isNotBlank(dto.getNoFetchChild()))
            param.put("no_fetch_child",dto.getNoFetchChild());
        if (StringUtils.isNotBlank(dto.getOrgType()))
            param.put("org_type",dto.getOrgType());
        JSONObject result = doHttpGet(url,param,header);
        logger.info("调用中粮接口------getDepartmentList----结果:{}，参数：{}，地址：{}",JSON.toJSONString(result),JSON.toJSONString(param),url);

        if (Objects.nonNull(result)&&Objects.nonNull(result.get("department"))){
            JSONArray list = (JSONArray) result.get("department");
            resultList = list.toJavaList(IamDepartment.class);
        }
        return resultList;
    }

    /**
     * 校验id_token是否合法
     * @param type
     * @param jwtTicket
     * @return
     * @throws Exception
     */
    public JwtClaims validateToken(String type,String jwtTicket) throws Exception {
        /**
         * JwtClaims信息示例
         * {
         *     "jti":"Sy6hvONyfWq9AteFrGt0Fg",
         *     "iat":1704436808,
         *     "exp":1704523208,
         *     "nbf":1704436748,
         *     "iss":"https://ydbg.cofco.com:8002/auth/oidc/F673F9052DA54DC69C9EF3CA2985CB91",
         *     "sub":"liumao.my",
         *     "aud":"ZWI4YzkyMDIxYjQzNGNmMTk1OWIxZjM3YzUyMmZiNjc",
         *     "roles":[
         *
         *     ],
         *     "name":null,
         *     "authType":"WECOM_QR",
         *     "userid":"liumao.my",
         *     "userId":"tempqywxwwfabce8efc6fec27f_liumao.my",
         *     "subjectId":"839006dc-c830-43bb-b1cd-aa15c238a72c",
         *     "email":null
         * }
         */
        JsonWebSignature jws = new JsonWebSignature();
        jws.setCompactSerialization(jwtTicket);
        jws.setKey(JsonWebKey.Factory.newJwk(appMap.get(type).getPublickey()).getKey());

        boolean verifySignature = jws.verifySignature();
        if (!verifySignature) {
            logger.error("Invalid signature! jwtTicket:{} ", jwtTicket);
            throw new Exception("Invalid signature!");
        }

        String payload = jws.getPayload();
        JwtClaims claims = JwtClaims.parse(payload);
        logger.info("iam用户信息：{}",claims.toJson());
        NumericDate expirationTime = claims.getExpirationTime();
        if (expirationTime == null) {
            logger.error("Invalid signature! jwtTicket:{}", jwtTicket);
            throw new Exception("Invalid signature!");
        }
        if (!expirationTime.isAfter(NumericDate.now())) {
            logger.error("Jwt ticket expired! jwtTicket:{}", jwtTicket);
            throw new Exception("Jwt ticket expired!");
        }
        return claims;
    }


    public Map<String,Object> generateSdkHeader(){
        Map<String,Object> header = new HashMap<>();
        String timestamp = "" + System.currentTimeMillis();
        String appToken = generateAppToken(appId+appSecret+timestamp);
        header.put("X-App-Id",appId);
        header.put("X-Timestamp",timestamp);
        header.put("Content-Type","application/json");
        header.put("X-App-Token",appToken);
        return header;
    }

    /**
     * 生成T-App-Token
     * @param str
     * @return
     */
    public String generateAppToken(String str) {
        MessageDigest messageDigest;
        String encodeStr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str.getBytes("UTF-8"));
            encodeStr = Hex.encodeHexString(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            logger.error(e.getMessage(), e);
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage(), e);
        }
        return encodeStr;
    }


    /**
     * post请求
     *
     * @param strUrl
     * @param paramsJson json参数
     * @param header     请求头
     * @return
     * @throws IOException
     */
    public static Object doHttpPost(String strUrl, String paramsJson, Map<String, Object> header) throws IOException {
        CloseableHttpClient httpClient = createClientDefault();
        Object data = null;
        HttpPost post = buildPost(strUrl, paramsJson, header);
        data = buildResult(httpClient, post);
        return data;
    }

    /**
     * get 带参数请求
     *
     * @param strUrl
     * @param params
     * @return
     * @throws IOException
     */
    public static JSONObject doHttpGet(String strUrl, Map<String, Object> params, Map<String, Object> header){
        CloseableHttpClient httpClient = createClientDefault();
        JSONObject data = null;
        HttpGet get = null;
        try {
            get = buildGet(strUrl, params, header);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        data = buildResult(httpClient, get);
        return data;
    }


    private static HttpPost buildPost(String strUrl, String paramsJson, Map<String, Object> header){
        HttpPost post = new HttpPost(strUrl);
        if (null != header) {
            for (Map.Entry<String, Object> entry : header.entrySet()) {
                post.setHeader(entry.getKey(), entry.getValue().toString());
            }
        }
        //构建参数
        HttpEntity httpEntity = new StringEntity(paramsJson, ContentType.create(HTTP.CONTENT_ENCODING, "UTF-8"));
        post.setEntity(httpEntity);
        return post;
    }

    private static HttpGet buildGet(String strUrl, Map<String, Object> params, Map<String, Object> header) throws URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder(strUrl);
        //设置参数
        if (params!=null && params.keySet().size() > 0) {
            params.entrySet().stream().forEach(m -> {
                uriBuilder.addParameter(m.getKey(), String.valueOf(m.getValue()));
            });
        }
        HttpGet get = new HttpGet(uriBuilder.build());
        //设置请求头
        if (null != header) {
            for (Map.Entry<String, Object> entry : header.entrySet()) {
                get.setHeader(entry.getKey(), entry.getValue().toString());
            }
        }
        return get;
    }

    /**
     * 获取返回值
     *
     * @param httpClient
     * @param request
     * @return
     */
    private static JSONObject buildResult(CloseableHttpClient httpClient, HttpRequestBase request) {
        String content = null;
        JSONObject result = null;
        try {
            CloseableHttpResponse response = httpClient.execute(request);
            // 获取entity
            HttpEntity httpEntity = response.getEntity();
            // 获取返回内容
            Charset charset = ContentType.getOrDefault(httpEntity).getCharset();
            content = EntityUtils.toString(httpEntity, charset);
            result = JSONObject.parseObject(content);
        } catch (Exception e) {
            logger.info("IAM接口调用失败,url:{},error{}",request.getURI().toString(),e.getMessage());
        }finally {
            return result;
        }
    }

    public final static void convert(Object json) {
        if (json instanceof JSONArray) {
            JSONArray arr = (JSONArray) json;
            for (Object obj : arr) {
                convert(obj);
            }
        } else if (json instanceof JSONObject) {
            JSONObject jo = (JSONObject) json;
            Set<String> keys = jo.keySet();
            String[] array = keys.toArray(new String[keys.size()]);
            for (String key : array) {
                Object value = jo.get(key);
                String[] key_strs = key.split("_");
                if (key_strs.length > 1) {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < key_strs.length; i++) {
                        String ks = key_strs[i];
                        if (!"".equals(ks)) {
                            if (i == 0) {
                                sb.append(ks);
                            } else {
                                int c = ks.charAt(0);
                                if (c >= 97 && c <= 122) {
                                    int v = c - 32;
                                    sb.append((char) v);
                                    if (ks.length() > 1) {
                                        sb.append(ks.substring(1));
                                    }
                                } else {
                                    sb.append(ks);
                                }
                            }
                        }
                    }
                    jo.remove(key);
                    jo.put(sb.toString(), value);
                }
                convert(value);
            }
        }
    }

    public final static Object convert(String json) {
        Object obj = JSON.parse(json);
        convert(obj);
        return obj;
    }

}
