package com.inspect.ivs.service;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.inspect.base.core.constant.Color;
import com.inspect.base.core.exception.ServiceException;
import com.inspect.base.redis.service.RedisService;
import com.inspect.ivs.base.feign.domain.IvsResult;
import com.inspect.ivs.constant.IvsConst;
import com.inspect.ivs.domain.ErrorCode;
import com.inspect.ivs.util.SslUtils;
import com.inspect.ivs.util.WebClientUtils;

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec;

import javax.annotation.Resource;
import javax.net.ssl.HttpsURLConnection;

import static com.inspect.ivs.constant.IvsConst.HTTP_JSESSIONID_SESSION_NAME;

@Component
public class IvsCommonService {
    private static final Logger log = LoggerFactory.getLogger(IvsCommonService.class);

    @Value("${ivs.address}")
    private String address;
    private final WebClient webClient = WebClientUtils.create();

    @Resource
    protected RedisService redisService;

    public IvsCommonService() {

    }

    public String getCookie() {
        String cookie = redisService.getCacheObject(IvsConst.IVS_TOKEN);
        log.info(Color.YELLOW + "[IVS] cookie: {}" + Color.END, cookie);
        return cookie;
    }

    public void setCookie(Object cookie) {
        redisService.setCacheObject(IvsConst.IVS_TOKEN, (String) cookie);
    }

    public String getAddress() {
        return this.address;
    }

    public RequestBodySpec http(HttpMethod httpMethod, String uri) {
        log.info(Color.MAGENTA + "[IVS] ivs address: {} " + Color.END, getAddress().concat(uri));
        assert webClient != null;
        return webClient.method(httpMethod).uri(getAddress().concat(uri));
    }

    public RequestBodySpec http(HttpMethod httpMethod, String ipAddr, String uri) {
        assert StringUtils.isNotBlank(ipAddr);
        assert StringUtils.isNotBlank(uri);
        log.info(Color.MAGENTA + "http ivs address: {} " + Color.END, ipAddr.concat(uri));
        assert webClient != null;
        return webClient.method(httpMethod).uri(ipAddr.concat(uri));
    }

    public <T> T get(final String label, String uri, Class<T> clazz) {
        String cookie = this.getCookie();
        String res = HttpUtil.createGet(getAddress().concat(uri))
                //.cookie(new HttpCookie[]{new HttpCookie(IvsConst.HTTP_JSESSIONID_SESSION_NAME, cookie)})
                .cookie(HTTP_JSESSIONID_SESSION_NAME.concat("=").concat(cookie))
                .execute()
                .body();
        log.info("[IVS] GET URI: {}, RES: {}", uri, res);
        return checkResultCodeAndConvertResult(label, uri, JSONObject.parseObject(res), clazz);
    }

    public <T> T get(final String label, final String address, String uri, Class<T> clazz) {
        String cookie = this.getCookie();
        final String url = StringUtils.isEmpty(address) ? getAddress().concat(uri) : address.concat(uri);
        String res = HttpUtil.createGet(url)
                .cookie(HTTP_JSESSIONID_SESSION_NAME.concat("=").concat(cookie))
                .execute()
                .body();
        log.info("[IVS] GET ADDRESS: {} URL: {}, RES: {}", address, url, res);
        return checkResultCodeAndConvertResult(label, uri, JSONObject.parseObject(res), clazz);
    }

    public <T> T getJson(String label, String uri, Class<T> clazz) {
        String cookie = this.getCookie();
        String res = HttpUtil.createGet(getAddress().concat(uri))
                .cookie(new HttpCookie[]{new HttpCookie(HTTP_JSESSIONID_SESSION_NAME, cookie)})
                .execute()
                .body();

        JSONObject t = http(HttpMethod.GET, uri)
                .contentType(MediaType.APPLICATION_JSON)
                .cookie(HTTP_JSESSIONID_SESSION_NAME, cookie).retrieve().bodyToMono(JSONObject.class).block();
        log.info("[CALL IVS] getJson POST, Response:{}", t.toJSONString());
        return this.checkResultCodeAndConvertResult(label, uri, t, clazz);
    }

    public JSONObject getResultJson(String label, String uri) {
        String cookie = this.getCookie();
        JSONObject result = this.http(HttpMethod.GET, uri)
                .cookie(HTTP_JSESSIONID_SESSION_NAME, cookie)
                .retrieve()
                .bodyToMono(JSONObject.class)
                .block();
        String resultCode = result.getString(IvsConst.RESULT_CODE);
        if (IvsConst.HTTP_STATUS_LOGIN_EXPIRE.equals(resultCode)) {
            (new Thread(() -> {
                this.get(label, IvsConst.LOCAL_LOGIN_URL);
            })).start();
        }

        if (!IvsConst.HTTP_STATUS_OK.equals(resultCode)) {
            throw new ServiceException(String.format("IVS SERVER FAIL: %s!", ErrorCode.map.get(resultCode).toString()));
        }

        return result;
    }

    public IvsResult delete(String label, String uri) {
        String cookie = getCookie();
        JSONObject jsonObject
                = http(HttpMethod.DELETE, uri)
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .cookie(HTTP_JSESSIONID_SESSION_NAME, cookie)
                .retrieve()
                .bodyToMono(JSONObject.class)
                .block();
        return checkResultCodeAndConvertResult(label, uri, jsonObject, IvsResult.class);
    }

    public IvsResult get(String label, String uri) {
        return this.get(label, uri, IvsResult.class);
    }

    private <T> T checkResultCodeAndConvertResult(final String label, final String uri, JSONObject result, Class<T> clazz) {
        log.info("CHECK_IVS_RET LABEL: {}, URI: {}, RES: {}", label, uri, result.toJSONString());
        String resultCode = result.getString(IvsConst.RESULT_CODE);
        if (IvsConst.HTTP_STATUS_LOGIN_EXPIRE.equals(resultCode)) {
            (new Thread(() -> HttpUtil.get(IvsConst.LOCAL_LOGIN_URL))).start();
        }

        if (!IvsConst.HTTP_STATUS_OK.equals(resultCode)) {
            log.error("IVS SERVER FAIL resultCode: {}", resultCode);
        }

        return result.toJavaObject(clazz);
    }

    public <T> T putJson(String label, Object requestParam, String uri, Class<T> clazz) {
        String cookie = this.getCookie();
        JSONObject t = http(HttpMethod.PUT, uri)
                .contentType(MediaType.APPLICATION_JSON)
                .cookie(HTTP_JSESSIONID_SESSION_NAME, cookie)
                .bodyValue(requestParam)
                .retrieve()
                .bodyToMono(JSONObject.class)
                .block();

        log.debug("[IVS] PUT PARAM: {}\n, RES: {}", JSONObject.toJSONString(requestParam), t.toJSONString());
        return checkResultCodeAndConvertResult(label, uri, t, clazz);
    }

//    public <T> T postJson(Object requestParam, String uri, Class<T> clazz) {
//        String cookie = getCookie();
//        JSONObject t = http(HttpMethod.POST, uri)
//                .contentType(MediaType.APPLICATION_JSON)
//                .cookie(IvsConst.HTTP_JSESSIONID_SESSION_NAME, cookie)
//                .bodyValue(requestParam)
//                .retrieve()
//                .bodyToMono(JSONObject.class)
//                .block();
//        assert t != null;
//        //log.debug("[IVS] POST PARAM: {}, RES: {}", JSONObject.toJSONString(requestParam), t.toJSONString());
//        return checkResultCodeAndConvertResult(t, clazz);
//    }

    public <T> T postJson(String label, Object requestParam, String uri, Class<T> clazz) {
        JSONObject t = this.http(HttpMethod.POST, uri)
                .contentType(MediaType.APPLICATION_JSON)
                .cookie(HTTP_JSESSIONID_SESSION_NAME, this.getCookie())
                .bodyValue(requestParam).retrieve()
                .bodyToMono(JSONObject.class)
                .block();
        log.info("IVS postJson, Parameter:{}, Response:{}", JSONObject.toJSONString(requestParam), t.toJSONString());
        return this.checkResultCodeAndConvertResult(label, uri, t, clazz);
    }

    public <T> T postJson(String label, String ipAddr, String uri, Object requestParam, Class<T> clazz) {
        final String destAddr = StringUtils.isEmpty(ipAddr) ? getAddress() : ipAddr;
        log.info("IVS postJsonEx, Parameter:{}", JSONObject.toJSONString(requestParam));
        JSONObject t = this.http(HttpMethod.POST, destAddr, uri)
                .contentType(MediaType.APPLICATION_JSON)
                .cookie(HTTP_JSESSIONID_SESSION_NAME, this.getCookie())
                .bodyValue(requestParam).retrieve()
                .bodyToMono(JSONObject.class)
                .block();
        log.info("IVS postJsonEx, Parameter:{}, Response:{}", JSONObject.toJSONString(requestParam), t.toJSONString());
        return this.checkResultCodeAndConvertResult(label, uri, t, clazz);
    }

    public IvsResult postJson(String label, Object requestParam, String uri) {
        return this.postJson(label, requestParam, uri, IvsResult.class);
    }

    public byte[] sendSSLPost(String url, String param) throws Exception {
        OutputStream out = null;
//        log.info("执行sendssl方法");
        String Cookie = this.getCookie();
        StringBuilder result = new StringBuilder();
        String urlNameString = url;
        // 定义一个通用的连接对象
        HttpURLConnection conn = null;
        try {
            URL console = new URL(urlNameString);
            SslUtils.ignoreSsl();
            conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            conn.setRequestProperty("Cookie", "JSESSIONID=" + Cookie);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();
            out = conn.getOutputStream();
            out.write(param.getBytes());
            out.flush();
            try (InputStream is = conn.getInputStream()) {
//                log.info("返回sendssl方法");
                return readStream(is);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return null;
    }


    /**
     * post请求
     *
     * @param url
     * @param param
     * @param Cookie
     * @return
     */
    public String sendSSLPostString(String url, String param) {
        String Cookie = this.getCookie();
        OutputStream out = null;
        StringBuilder result = new StringBuilder();
        String urlNameString = url;
        // 定义一个通用的连接对象
        HttpURLConnection conn;
        try {
            URL console = new URL(urlNameString);
            SslUtils.ignoreSsl();
            conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            conn.setRequestProperty("Cookie", "JSESSIONID=" + Cookie);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();
            out = conn.getOutputStream();
            out.write(param.getBytes());
            out.flush();
            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String ret = "";
            while ((ret = br.readLine()) != null) {
                if (ret != null && !"".equals(ret.trim())) {
                    result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
                }
            }
            conn.disconnect();
            br.close();
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
        }
        return result.toString();
    }


    public byte[] readStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, len);
        }
        outStream.close();
        inStream.close();
//        log.info("返回readStream方法{}" , outStream.toByteArray());
        return outStream.toByteArray();
    }

    /**
     * 向指定https URL 发送GET方法的请求
     *
     * @param url         发送请求的 URL
     * @param param       请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @param contentType 编码类型
     * @return 所代表远程资源的响应结果
     */
    public String sendsslGetCookie(String url, String param, String contentType) {
        String Cookie = this.getCookie();
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {
            String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
            URL realUrl = new URL(urlNameString);
            SslUtils.ignoreSsl();
//            URL realUrl = new URL(urlNameString);
//            SslUtils.ignoreSsl();
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.setRequestProperty("Cookie", "JSESSIONID=" + Cookie);
            //添加自定义请求头
            // 设置请求头
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }


    public static void main(String[] args) {
        JSONObject jsonObject = new JSONObject();
        jsonObject
                .fluentPut("cameraCode", "00517884726383520101#d562fd94f9d24ee38cc23052ded532df")
                .fluentPut("mediaAddrDst",
                        (new JSONObject())
                                .fluentPut("audioPort", Integer.valueOf(30500))
                                .fluentPut("videoPort", Integer.valueOf(30251))
                                .fluentPut("dstIP",
                                        (new JSONObject())
                                                .fluentPut("ip", "172.21.101.112")
                                                .fluentPut("ipType", Integer.valueOf(0))
                                )
                )
                .fluentPut("playbackParam",
                        (new JSONObject())
                                .fluentPut("NVRCode", "038f958f47ec4ec2959cd28ffd1c22fe")
                                .fluentPut("protocolType", "1")
                                .fluentPut("speed", "1")
                                .fluentPut("recordTime",
                                        (new JSONObject()).
                                                fluentPut("endTime", "20220709122057")
                                                .fluentPut("startTime", "20220709122025"))
                );
        log.info("jsonObject.toJSONString: {}", jsonObject.toJSONString());
    }
}
