package yangtz.cs.liu.dingding.controller;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.aliyun.dingtalkcontact_1_0.models.GetUserResponse;
import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.html.EscapeUtil;
import com.ruoyi.common.utils.http.HttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import yangtz.cs.liu.dingding.config.Constant;
import yangtz.cs.liu.dingding.utils.AccessTokenUtils;
import yangtz.cs.liu.dingding.utils.DingCallbackCrypto;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/dd/user")
public class DdApiController {

    @Value("${dd.appKey}")
    private String appKey;
    @Value("${dd.appSecret}")
    private String appSecret;
    @Value("${dd.redirect-uri}")
    private String redirectUri;
    @Autowired
    private AccessTokenUtils accessTokenUtils;
    @Autowired
    private RedisCache redisCache;

    private Logger log = LoggerFactory.getLogger(DdApiController.class);

    /**
     * 使用 Token 初始化账号Client
     * @return Client
     * @throws Exception
     */
    public static com.aliyun.dingtalkoauth2_1_0.Client createClient() {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        try {
            return new com.aliyun.dingtalkoauth2_1_0.Client(config);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public static com.aliyun.dingtalkcontact_1_0.Client createClient1() {
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
        config.protocol = "https";
        config.regionId = "central";
        try {
            return new com.aliyun.dingtalkcontact_1_0.Client(config);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 1.用户同意授权,获取code
     */
    @GetMapping("/getCode")
    public String getCode(HttpServletResponse response) {
        log.info("调用getCode");
        // state 为开发者填写参数值
        String state = UUID.randomUUID().toString().replaceAll("-", "");
        // 钉钉开放平台
        String url = "https://login.dingtalk.com/oauth2/auth?"
                + "redirect_uri="
                + redirectUri
                + "&response_type=code"
                + "&client_id=dingsuthkncsciwpeaxq"
                + "&scope=openid"
                + "&state=" + state
                + "&prompt=consent";
        return "redirect:" + url;
    }
//https://login.dingtalk.com/oauth2/auth?redirect_uri=http://47.105.176.202:8135/&response_type=code&client_id=dingsuthkncsciwpeaxq&scope=openid&prompt=consent
    /**
     * 2.获取用户openId
     */
    @PostMapping("/getOpenId")
    public AjaxResult getOpenId(String code) {
        AjaxResult ajax = AjaxResult.success();
        com.aliyun.dingtalkoauth2_1_0.Client client = DdApiController.createClient();
        GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
                .setClientId(appKey)
                .setClientSecret(appSecret)
                .setCode(code)
                .setGrantType("authorization_code");
        try {
            GetUserTokenResponse userToken = client.getUserToken(getUserTokenRequest);
            String accessToken = userToken.getBody().getAccessToken();
            if (StringUtils.isNotEmpty(accessToken)){
                String openId = getOpenByToken(accessToken);
                if (StringUtils.isNotEmpty(openId)){
                    ajax.put("openId",openId);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ajax;
    }
    public String getOpenByToken(String token) {
        String openId = "";
        log.info("接收到重定向消息");
        com.aliyun.dingtalkcontact_1_0.Client client = DdApiController.createClient1();
        com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders getUserHeaders = new com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders();
        getUserHeaders.xAcsDingtalkAccessToken = token;
        try {
            GetUserResponse me = client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions());
            String openId1 = me.getBody().getOpenId();
            if (StringUtils.isNotEmpty(openId1)){
                openId = openId1;
                log.info(openId1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return openId;
    }

    /**
     * 获取access_token
     */
    @GetMapping("/getAccessToken")
    @ResponseBody
    public AjaxResult getAccessToken() {
        String token = accessTokenUtils.getToken();
        return AjaxResult.success(token);
    }

    /**
     * 获取签名
     */
    @GetMapping("/getsignature")
    @ResponseBody
    public AjaxResult getsignature(String url){
        //获取accesToken
        String token = accessTokenUtils.getToken();
        url = decodeUrl(url);
        //获取jsapi_ticket
        String ticket = null;
        ticket = redisCache.getCacheObject("ticket");
        if (StringUtils.isEmpty(ticket)){
            String ticketss = HttpUtils.sendGet(
                    "https://oapi.dingtalk.com/get_jsapi_ticket?" + "access_token=" + token
                            + "&type=jsapi");
            JSONObject jsonObject = JSONUtil.parseObj(ticketss);
            ticket = jsonObject.get("ticket").toString();
            redisCache.setCacheObject("ticket", ticket, 7200, TimeUnit.MILLISECONDS);
        }
        //生成JS-SDK权限验证的签名
        String noncester = create_nonce_str();
        String timestamp = create_timestamp();
        String string1;
        String signature = "";
        //3.
        //注意这里参数名必须全部小写，且必须有序
        string1 = "jsapi_ticket=" + ticket +
                "&noncestr=" + noncester +
                "&timestamp=" + timestamp +
                "&url=" + url;
        System.out.println(string1);
        log.info(string1);
        try {
            MessageDigest sha1 = MessageDigest.getInstance("SHA-256");
            sha1.reset();
            sha1.update(string1.getBytes("UTF-8"));
            byteToHex(sha1.digest());
        } catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }
        Map<String,String> map = new HashMap<String,String>();
        map.put("noncestr",noncester);
        map.put("jsapi_ticket",ticket);
        map.put("timestamp",timestamp);
        map.put("url",url);
        map.put("signature",signature);
        map.put("cstoken",token);
        return AjaxResult.success(map);
    }

    private static String decodeUrl(String url) {
        StringBuilder urlBuffer = new StringBuilder();
        try{
            URL urler = new URL(url);
            urlBuffer.append(urler.getProtocol());
            urlBuffer.append(":");
            if (urler.getAuthority() != null && urler.getAuthority().length() > 0) {
                urlBuffer.append("//");
                urlBuffer.append(urler.getAuthority());
            }
            if (urler.getPath() != null) {
                urlBuffer.append(urler.getPath());
            }
            if (urler.getQuery() != null) {
                urlBuffer.append('?');
                urlBuffer.append(URLDecoder.decode(urler.getQuery(), "utf-8"));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return urlBuffer.toString();
    }

    private static String create_nonce_str() {
        return UUID.randomUUID().toString();
    }
    private static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }
    // 字节数组转化成十六进制字符串
    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    /**
     * 事件与回调
     * @param msg_signature
     * @param timeStamp
     * @param nonce
     * @param json
     * @return
     */
    @GetMapping("/callBack")
    public Map<String, String> callBack(
            @RequestParam(value = "msg_signature", required = false) String msg_signature,
            @RequestParam(value = "timestamp", required = false) String timeStamp,
            @RequestParam(value = "nonce", required = false) String nonce,
            @RequestBody(required = false) com.alibaba.fastjson.JSONObject json) {
        try {
            // 1. 从http请求中获取加解密参数

            // 2. 使用加解密类型
            // Constant.OWNER_KEY 说明：
            // 1、开发者后台配置的订阅事件为应用级事件推送，此时OWNER_KEY为应用的APP_KEY。
            // 2、调用订阅事件接口订阅的事件为企业级事件推送，
            //      此时OWNER_KEY为：企业的appkey（企业内部应用）或SUITE_KEY（三方应用）
            DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(Constant.AES_TOKEN, Constant.AES_KEY, Constant.OWNER_KEY);
            String encryptMsg = json.getString("encrypt");
            String decryptMsg = callbackCrypto.getDecryptMsg(msg_signature, timeStamp, nonce, encryptMsg);

            // 3. 反序列化回调事件json数据
            com.alibaba.fastjson.JSONObject eventJson = JSON.parseObject(decryptMsg);
            String eventType = eventJson.getString("EventType");

            // 4. 根据EventType分类处理
            if ("check_url".equals(eventType)) {
                // 测试回调url的正确性
                log.info("测试回调url的正确性");
            } else if ("user_add_org".equals(eventType)) {
                // 处理通讯录用户增加事件
                log.info("发生了：" + eventType + "事件");
            } else {
                // 添加其他已注册的
                log.info("发生了：" + eventType + "事件");
            }

            // 5. 返回success的加密数据
            Map<String, String> successMap = callbackCrypto.getEncryptedMap("success");
            return successMap;

        } catch (DingCallbackCrypto.DingTalkEncryptException e) {
            e.printStackTrace();
        }
        return null;
    }
}
