package com.cloudcc.core;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

public class CCService {

    // -------- Service name constants --------
    public static final String SVC_CQUERY                    = "cquery";
    public static final String SVC_CQUERY_WITH_ROLE_RIGHT    = "cqueryWithRoleRight";
    public static final String SVC_PAGE_QUERY                = "pageQuery";
    public static final String SVC_PAGE_QUERY_WITH_ROLE_RIGHT = "pageQueryWithRoleRight";
    public static final String SVC_GET_QUERY_PERMISSION      = "getQueryPermission";
    public static final String SVC_CQL_QUERY_WITH_LOG_INFO   = "cqlQueryWithLogInfo";
    public static final String SVC_CQL_QUERY_WITH_STATIC     = "cqlQueryWithStatic";
    public static final String SVC_INSERT                    = "insert";
    public static final String SVC_UPDATE                    = "update";
    public static final String SVC_DELETE                    = "delete";
    public static final String SVC_UPSERT                    = "upsert";
    public static final String SVC_INSERT_WITH_ROLE_RIGHT    = "insertWithRoleRight";
    public static final String SVC_UPDATE_WITH_ROLE_RIGHT    = "updateWithRoleRight";
    public static final String SVC_DELETE_WITH_ROLE_RIGHT    = "deleteWithRoleRight";
    public static final String SVC_UPSERT_WITH_ROLE_RIGHT    = "upsertWithRoleRight";
    public static final String SVC_SEND_EMAIL                = "sendEmail";

    public static UserInfo userInfo;
    public String ACCESSTOKEN;
    public String path;
    public String URL;
    protected Map<String, Object> record_new;
    protected Map<String, Object> record_old;
    public Map<String, String> Config = Tool.executeGetTokenCommand();
    public ServiceResult trigger = new ServiceResult();
    public String objectApiName;

    public CCService(UserInfo userInfo) {
        this.ACCESSTOKEN = Config.get("accessToken");
        this.path = Config.get("apiSvc") + "/openApi/common";
    }

    // -------- invokeCommon --------

    public JSONObject invokeCommon(JSONObject parObj) {
        String relStr = CCPost(path, parObj.toString());
        return JSONObject.parseObject(relStr);
    }

    // -------- cquery overloads --------

    @SuppressWarnings("rawtypes")
    public List<CCObject> cquery(String apiName) {
        List<CCObject> orst = new ArrayList<CCObject>();
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_CQUERY);
        parObj.put("objectApiName", apiName);
        parObj.put("expressions", "1=1");
        parObj.put("isAddDelete", "false");
        parObj.put("fields", "");

        JSONObject relObj = invokeCommon(parObj);
        if ("true".equals(relObj.getString("result"))) {
            List<Map> rst = relObj.getJSONArray("data").toJavaList(Map.class);
            for (Map m : rst) {
                CCObject co = new CCObject(apiName);
                for (Object _key : m.keySet()) {
                    co.put((String) _key, m.get(_key));
                }
                orst.add(co);
            }
        }
        return orst;
    }

    @SuppressWarnings("rawtypes")
    public List<CCObject> cquery(String apiName, String expression) {
        List<CCObject> orst = new ArrayList<CCObject>();
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_CQUERY);
        parObj.put("objectApiName", apiName);
        parObj.put("expressions", expression);
        parObj.put("isAddDelete", "false");
        parObj.put("fields", "");

        JSONObject relObj = invokeCommon(parObj);
        if ("true".equals(relObj.getString("result"))) {
            List<Map> rst = relObj.getJSONArray("data").toJavaList(Map.class);
            for (Map m : rst) {
                CCObject co = new CCObject(apiName);
                for (Object _key : m.keySet()) {
                    co.put((String) _key, m.get(_key));
                }
                orst.add(co);
            }
        } else {
            System.err.println("cquery报错: " + relObj.getString("returnInfo"));
        }
        return orst;
    }

    /** 带字段列表的查询 */
    @SuppressWarnings("rawtypes")
    public List<CCObject> cquery(String apiName, String expression, String fields) {
        List<CCObject> orst = new ArrayList<CCObject>();
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_CQUERY);
        parObj.put("objectApiName", apiName);
        parObj.put("expressions", expression);
        parObj.put("isAddDelete", "false");
        parObj.put("fields", fields == null ? "" : fields);

        JSONObject relObj = invokeCommon(parObj);
        if ("true".equals(relObj.getString("result"))) {
            List<Map> rst = relObj.getJSONArray("data").toJavaList(Map.class);
            for (Map m : rst) {
                CCObject co = new CCObject(apiName);
                for (Object _key : m.keySet()) {
                    co.put((String) _key, m.get(_key));
                }
                orst.add(co);
            }
        } else {
            System.err.println("cquery报错: " + relObj.getString("returnInfo"));
        }
        return orst;
    }

    /** 带 isShared 标志的查询 */
    @SuppressWarnings("rawtypes")
    public List<CCObject> cquery(String apiName, String expression, boolean isShared) {
        List<CCObject> orst = new ArrayList<CCObject>();
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_CQUERY);
        parObj.put("objectApiName", apiName);
        parObj.put("expressions", expression);
        parObj.put("isAddDelete", "false");
        parObj.put("fields", "");
        if (isShared) {
            parObj.put("isShared", "true");
        }

        JSONObject relObj = invokeCommon(parObj);
        if ("true".equals(relObj.getString("result"))) {
            List<Map> rst = relObj.getJSONArray("data").toJavaList(Map.class);
            for (Map m : rst) {
                CCObject co = new CCObject(apiName);
                for (Object _key : m.keySet()) {
                    co.put((String) _key, m.get(_key));
                }
                orst.add(co);
            }
        } else {
            System.err.println("cquery报错: " + relObj.getString("returnInfo"));
        }
        return orst;
    }

    /** 带字段列表和排序的查询 */
    @SuppressWarnings("rawtypes")
    public List<CCObject> cquery(String apiName, String expression, String fields, String orderBy) {
        List<CCObject> orst = new ArrayList<CCObject>();
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_CQUERY);
        parObj.put("objectApiName", apiName);
        parObj.put("expressions", expression);
        parObj.put("isAddDelete", "false");
        parObj.put("fields", fields == null ? "" : fields);
        if (orderBy != null && !orderBy.isEmpty()) {
            parObj.put("orderBy", orderBy);
        }

        JSONObject relObj = invokeCommon(parObj);
        if ("true".equals(relObj.getString("result"))) {
            List<Map> rst = relObj.getJSONArray("data").toJavaList(Map.class);
            for (Map m : rst) {
                CCObject co = new CCObject(apiName);
                for (Object _key : m.keySet()) {
                    co.put((String) _key, m.get(_key));
                }
                orst.add(co);
            }
        } else {
            System.err.println("cquery报错: " + relObj.getString("returnInfo"));
        }
        return orst;
    }

    // -------- cqueryWithRoleRight --------

    @SuppressWarnings("rawtypes")
    public List<CCObject> cqueryWithRoleRight(String apiName, String expression) {
        return cqueryWithRoleRight(apiName, expression, "");
    }

    @SuppressWarnings("rawtypes")
    public List<CCObject> cqueryWithRoleRight(String apiName, String expression, String fields) {
        List<CCObject> orst = new ArrayList<CCObject>();
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_CQUERY_WITH_ROLE_RIGHT);
        parObj.put("objectApiName", apiName);
        parObj.put("expressions", expression);
        parObj.put("isAddDelete", "false");
        parObj.put("fields", fields == null ? "" : fields);

        JSONObject relObj = invokeCommon(parObj);
        if ("true".equals(relObj.getString("result"))) {
            List<Map> rst = relObj.getJSONArray("data").toJavaList(Map.class);
            for (Map m : rst) {
                CCObject co = new CCObject(apiName);
                for (Object _key : m.keySet()) {
                    co.put((String) _key, m.get(_key));
                }
                orst.add(co);
            }
        } else {
            System.err.println("cqueryWithRoleRight报错: " + relObj.getString("returnInfo"));
        }
        return orst;
    }

    // -------- pageQuery --------

    public PageQueryResult pageQuery(String apiName, String expression, String fields, int pageNum, int pageSize) {
        return pageQueryWithService(SVC_PAGE_QUERY, apiName, expression, fields, pageNum, pageSize);
    }

    public PageQueryResult pageQueryWithRoleRight(String apiName, String expression, String fields, int pageNum, int pageSize) {
        return pageQueryWithService(SVC_PAGE_QUERY_WITH_ROLE_RIGHT, apiName, expression, fields, pageNum, pageSize);
    }

    @SuppressWarnings("rawtypes")
    private PageQueryResult pageQueryWithService(String serviceName, String apiName, String expression, String fields, int pageNum, int pageSize) {
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", serviceName);
        parObj.put("objectApiName", apiName);
        parObj.put("expressions", expression);
        parObj.put("fields", fields == null ? "" : fields);
        parObj.put("pageNum", pageNum);
        parObj.put("pageSize", pageSize);

        JSONObject relObj = invokeCommon(parObj);
        List<CCObject> records = new ArrayList<CCObject>();
        if ("true".equals(relObj.getString("result"))) {
            JSONObject data = relObj.getJSONObject("data");
            JSONArray rows = data != null ? data.getJSONArray("records") : null;
            if (rows != null) {
                List<Map> rst = rows.toJavaList(Map.class);
                for (Map m : rst) {
                    CCObject co = new CCObject(apiName);
                    for (Object _key : m.keySet()) {
                        co.put((String) _key, m.get(_key));
                    }
                    records.add(co);
                }
            }
        } else {
            System.err.println("pageQuery报错: " + relObj.getString("returnInfo"));
        }
        return new PageQueryResult(records, relObj);
    }

    // -------- getQueryPermission --------

    public JSONObject getQueryPermission(String apiName) {
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_GET_QUERY_PERMISSION);
        parObj.put("objectApiName", apiName);
        return invokeCommon(parObj);
    }

    // -------- cqlQuery / cqlQueryWithLogInfo / cqlQueryWithStatic --------

    @SuppressWarnings("rawtypes")
    public List<CCObject> cqlQuery(String apiName, String expression) {
        return cqlQueryWithLogInfo(apiName, expression);
    }

    @SuppressWarnings("rawtypes")
    public List<CCObject> cqlQueryWithLogInfo(String apiName, String expression) {
        List<CCObject> orst = new ArrayList<CCObject>();
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_CQL_QUERY_WITH_LOG_INFO);
        parObj.put("objectApiName", apiName);
        parObj.put("expressions", expression);

        JSONObject relObj = invokeCommon(parObj);
        if ("true".equals(relObj.getString("result"))) {
            List<Map> rst = relObj.getJSONArray("data").toJavaList(Map.class);
            for (Map m : rst) {
                CCObject co = new CCObject(apiName);
                for (Object _key : m.keySet()) {
                    co.put((String) _key, m.get(_key));
                }
                orst.add(co);
            }
        } else {
            System.err.println("cqlQueryWithLogInfo报错: " + relObj.getString("returnInfo"));
        }
        return orst;
    }

    @SuppressWarnings("rawtypes")
    public CqlStaticQueryResult cqlQueryWithStatic(String apiName, String expression, String nextRecordQueryId) {
        List<CCObject> orst = new ArrayList<CCObject>();
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_CQL_QUERY_WITH_STATIC);
        parObj.put("objectApiName", apiName);
        parObj.put("expressions", expression);
        if (nextRecordQueryId != null && !nextRecordQueryId.isEmpty()) {
            parObj.put("nextRecordQueryId", nextRecordQueryId);
        }

        JSONObject relObj = invokeCommon(parObj);
        String nextId = null;
        if ("true".equals(relObj.getString("result"))) {
            JSONObject data = relObj.getJSONObject("data");
            if (data != null) {
                nextId = data.getString("nextRecordQueryId");
                JSONArray rows = data.getJSONArray("records");
                if (rows != null) {
                    List<Map> rst = rows.toJavaList(Map.class);
                    for (Map m : rst) {
                        CCObject co = new CCObject(apiName);
                        for (Object _key : m.keySet()) {
                            co.put((String) _key, m.get(_key));
                        }
                        orst.add(co);
                    }
                }
            }
        }
        return new CqlStaticQueryResult(orst, nextId, relObj);
    }

    // -------- CRUD --------

    public void update(CCObject cobj) {
        JSONArray arry = buildDataArray(cobj);
        String objectApiName = extractObjectApiName(cobj);

        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_UPDATE);
        parObj.put("objectApiName", objectApiName);
        parObj.put("data", arry.toString());
        try {
            TriggerInvoker invoker = new TriggerInvoker(userInfo, "update", "before");
            invoker.handler(cobj);
            JSONObject relObj = invokeCommon(parObj);
            boolean result = Boolean.TRUE.equals(relObj.getBoolean("result"));
            if (result) {
                JSONObject outObj = relObj.getJSONObject("data");
                if (outObj != null && outObj.containsKey("ids")) {
                    JSONArray outData = outObj.getJSONArray("ids");
                    StringBuilder sb = new StringBuilder();
                    boolean hasSuccess = false;
                    for (int i = 0; i < outData.size(); i++) {
                        JSONObject acc = outData.getJSONObject(i);
                        if (acc.getBoolean("success")) {
                            String id = acc.getString("id");
                            if (sb.length() > 0) sb.append(',');
                            sb.append(id);
                            hasSuccess = true;
                        } else {
                            System.err.println("update报错: " + acc.getString("errors"));
                        }
                    }
                    if (hasSuccess) {
                        cobj.put("id", sb.toString());
                        invoker = new TriggerInvoker(userInfo, "update", "after");
                        invoker.handler(cobj);
                    }
                } else {
                    System.err.println("update未获取到ids参数");
                }
            } else {
                System.err.println("update报错= " + relObj.getString("returnInfo"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ServiceResult insert(CCObject cobj) {
        ServiceResult sr = new ServiceResult();
        JSONArray arry = buildDataArray(cobj);
        String objectApiName = extractObjectApiName(cobj);

        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_INSERT);
        parObj.put("objectApiName", objectApiName);
        parObj.put("data", arry.toString());
        try {
            TriggerInvoker invoker = new TriggerInvoker(userInfo, "insert", "before");
            invoker.handler(cobj);
            JSONObject relObj = invokeCommon(parObj);
            if ("true".equals(relObj.getString("result"))) {
                JSONArray outData = relObj.getJSONObject("data").getJSONArray("ids");
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < outData.size(); i++) {
                    JSONObject acc = outData.getJSONObject(i);
                    if (acc.getBoolean("success")) {
                        if (sb.length() > 0) sb.append(',');
                        sb.append(acc.getString("id"));
                    } else {
                        System.err.println("insert报错: " + acc.getString("errors"));
                    }
                }
                String retIds = sb.toString();
                sr.put("id", retIds);
                cobj.put("id", retIds);
                invoker = new TriggerInvoker(userInfo, "insert", "after");
                invoker.handler(cobj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sr;
    }

    public ServiceResult insert(List<CCObject> datalist) {
        ServiceResult sr = new ServiceResult();
        if (datalist == null || datalist.isEmpty()) return sr;

        JSONArray arry = new JSONArray();
        String objectApiName = "";
        for (CCObject cobj : datalist) {
            JSONObject obj = new JSONObject();
            for (String _key : cobj.keySet()) {
                if ("CCObjectAPI".equals(_key)) {
                    objectApiName = cobj.get("CCObjectAPI") == null ? "" : cobj.get("CCObjectAPI") + "";
                } else {
                    obj.put(_key, cobj.get(_key) == null ? "" : cobj.get(_key) + "");
                }
            }
            arry.add(obj);
        }
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_INSERT);
        parObj.put("objectApiName", objectApiName);
        parObj.put("data", arry.toString());
        try {
            JSONObject relObj = invokeCommon(parObj);
            if ("true".equals(relObj.getString("result"))) {
                JSONArray outData = relObj.getJSONObject("data").getJSONArray("ids");
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < outData.size(); i++) {
                    JSONObject acc = outData.getJSONObject(i);
                    if (acc.getBoolean("success")) {
                        if (sb.length() > 0) sb.append(',');
                        sb.append(acc.getString("id"));
                    } else {
                        System.err.println("insert报错: " + acc.getString("errors"));
                    }
                }
                sr.put("id", sb.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sr;
    }

    public void delete(CCObject cobj) {
        try {
            TriggerInvoker invoker = new TriggerInvoker(userInfo, "delete", "before");
            invoker.handler(cobj);

            JSONArray arry = buildDataArray(cobj);
            String objectApiName = extractObjectApiName(cobj);
            JSONObject parObj = new JSONObject();
            parObj.put("serviceName", SVC_DELETE);
            parObj.put("objectApiName", objectApiName);
            parObj.put("data", arry.toString());
            invokeCommon(parObj);

            invoker = new TriggerInvoker(userInfo, "delete", "after");
            invoker.handler(cobj);
        } catch (Exception e) {
            System.err.println("delete报错= " + e);
        }
    }

    public ServiceResult upsert(CCObject cobj) {
        ServiceResult sr = new ServiceResult();
        JSONArray arry = buildDataArray(cobj);
        String objectApiName = extractObjectApiName(cobj);

        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_UPSERT);
        parObj.put("objectApiName", objectApiName);
        parObj.put("data", arry.toString());
        try {
            TriggerInvoker invoker = new TriggerInvoker(userInfo, "upsert", "before");
            invoker.handler(cobj);
            JSONObject relObj = invokeCommon(parObj);
            if ("true".equals(relObj.getString("result"))) {
                JSONArray outData = relObj.getJSONObject("data").getJSONArray("ids");
                JSONObject acc = outData.getJSONObject(0);
                if (acc.getBoolean("success")) {
                    sr.put("id", acc.getString("id"));
                    cobj.put("id", acc.getString("id"));
                }
                invoker = new TriggerInvoker(userInfo, "upsert", "after");
                invoker.handler(cobj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sr;
    }

    // -------- WithRoleRight CRUD variants --------

    public ServiceResult insertWithRoleRight(CCObject cobj) {
        return insertInternal(cobj, SVC_INSERT_WITH_ROLE_RIGHT);
    }

    public ServiceResult updateWithRoleRight(CCObject cobj) {
        ServiceResult sr = new ServiceResult();
        JSONArray arry = buildDataArray(cobj);
        String objectApiName = extractObjectApiName(cobj);
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_UPDATE_WITH_ROLE_RIGHT);
        parObj.put("objectApiName", objectApiName);
        parObj.put("data", arry.toString());
        try {
            JSONObject relObj = invokeCommon(parObj);
            if (Boolean.TRUE.equals(relObj.getBoolean("result"))) {
                JSONObject outObj = relObj.getJSONObject("data");
                if (outObj != null && outObj.containsKey("ids")) {
                    JSONArray outData = outObj.getJSONArray("ids");
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < outData.size(); i++) {
                        JSONObject acc = outData.getJSONObject(i);
                        if (acc.getBoolean("success")) {
                            if (sb.length() > 0) sb.append(',');
                            sb.append(acc.getString("id"));
                        }
                    }
                    sr.put("id", sb.toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sr;
    }

    public void deleteWithRoleRight(CCObject cobj) {
        JSONArray arry = buildDataArray(cobj);
        String objectApiName = extractObjectApiName(cobj);
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_DELETE_WITH_ROLE_RIGHT);
        parObj.put("objectApiName", objectApiName);
        parObj.put("data", arry.toString());
        invokeCommon(parObj);
    }

    public ServiceResult upsertWithRoleRight(CCObject cobj) {
        ServiceResult sr = new ServiceResult();
        JSONArray arry = buildDataArray(cobj);
        String objectApiName = extractObjectApiName(cobj);
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", SVC_UPSERT_WITH_ROLE_RIGHT);
        parObj.put("objectApiName", objectApiName);
        parObj.put("data", arry.toString());
        try {
            JSONObject relObj = invokeCommon(parObj);
            if ("true".equals(relObj.getString("result"))) {
                JSONArray outData = relObj.getJSONObject("data").getJSONArray("ids");
                JSONObject acc = outData.getJSONObject(0);
                if (acc.getBoolean("success")) {
                    sr.put("id", acc.getString("id"));
                    cobj.put("id", acc.getString("id"));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sr;
    }

    private ServiceResult insertInternal(CCObject cobj, String serviceName) {
        ServiceResult sr = new ServiceResult();
        JSONArray arry = buildDataArray(cobj);
        String objectApiName = extractObjectApiName(cobj);
        JSONObject parObj = new JSONObject();
        parObj.put("serviceName", serviceName);
        parObj.put("objectApiName", objectApiName);
        parObj.put("data", arry.toString());
        try {
            JSONObject relObj = invokeCommon(parObj);
            if ("true".equals(relObj.getString("result"))) {
                JSONArray outData = relObj.getJSONObject("data").getJSONArray("ids");
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < outData.size(); i++) {
                    JSONObject acc = outData.getJSONObject(i);
                    if (acc.getBoolean("success")) {
                        if (sb.length() > 0) sb.append(',');
                        sb.append(acc.getString("id"));
                    } else {
                        System.err.println(serviceName + "报错: " + acc.getString("errors"));
                    }
                }
                sr.put("id", sb.toString());
                cobj.put("id", sb.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sr;
    }

    // -------- HTTP helpers --------

    public String sendPost(String path, String param) {
        return doPost(path, param, null);
    }

    public String CCPost(String path, String param) {
        return doPost(path, param, ACCESSTOKEN);
    }

    private String doPost(String urlStr, String param, String accessToken) {
        OutputStreamWriter out = null;
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        String exception = "";
        try {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestMethod("POST");
            conn.setConnectTimeout(15 * 1000);
            conn.setReadTimeout(20 * 1000);
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("accept", "*/*");
            if (accessToken != null) {
                conn.setRequestProperty("accessToken", accessToken);
            }
            conn.connect();
            out = new OutputStreamWriter(conn.getOutputStream());
            BufferedWriter bw = new BufferedWriter(out);
            bw.write(param);
            bw.flush();
            br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常！" + e);
            exception = "发送 POST 请求出现异常！" + e;
            e.printStackTrace();
        } finally {
            try {
                if (out != null) out.close();
                if (br != null) br.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return sb.length() == 0 ? exception : sb.toString();
    }

    public static String sendGet(String path) {
        StringBuilder sb = new StringBuilder();
        String exception = "";
        try {
            URL url = new URL(path);
            InputStream is = url.openStream();
            InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
            BufferedReader br = new BufferedReader(isr);
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();
            isr.close();
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
            exception = "发送 GET 请求出现异常！" + e;
        }
        return sb.length() == 0 ? exception : sb.toString();
    }

    // -------- CustomSetting --------

    public Map<String, Object> getListCustomSetting(String api) {
        Map<String, Object> datMap = new HashMap<String, Object>();
        JSONArray argArray = new JSONArray();
        JSONObject arryStr = new JSONObject();
        arryStr.put("argType", String.class);
        arryStr.put("argValue", api);
        argArray.add(arryStr);

        JSONObject parObj = new JSONObject();
        parObj.put("className", "TCCCustomSetting");
        parObj.put("methodName", "getListCustomSetting");
        parObj.put("params", argArray);
        try {
            String relStr = CCPost(path, parObj.toString());
            JSONObject relObj = JSONObject.parseObject(relStr);
            if ("true".equals(relObj.getString("result"))) {
                JSONObject data = relObj.getJSONObject("data");
                if (data != null) {
                    datMap.putAll(data);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return datMap;
    }

    public Map<String, Object> getListCustomSetting(String api, String key) {
        Map<String, Object> datMap = new HashMap<String, Object>();
        JSONArray argArray = new JSONArray();

        JSONObject apiParam = new JSONObject();
        apiParam.put("argType", String.class);
        apiParam.put("argValue", api);
        argArray.add(apiParam);

        JSONObject keyParam = new JSONObject();
        keyParam.put("argType", String.class);
        keyParam.put("argValue", key);
        argArray.add(keyParam);

        JSONObject parObj = new JSONObject();
        parObj.put("className", "TCCCustomSetting");
        parObj.put("methodName", "getListCustomSetting");
        parObj.put("params", argArray);
        try {
            String relStr = CCPost(path, parObj.toString());
            JSONObject relObj = JSONObject.parseObject(relStr);
            if ("true".equals(relObj.getString("result"))) {
                JSONObject data = relObj.getJSONObject("data");
                if (data != null) {
                    datMap.putAll(data);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return datMap;
    }

    // -------- Stub methods (platform-internal) --------

    public HashMap<String, Object> cqueryForEs(String account, String where, String aTrue, String s) {
        return null;
    }

    public String getPermission(String objectApiName, String id, String type) {
        return null;
    }

    public ServiceResult insert(CCObject cobj, boolean b, boolean b1, boolean b2, boolean b3) {
        return null;
    }

    public void addCaseTeam(List<HashMap<String, Object>> caseteamList, String recordid) {
    }

    public ServiceResult update(CCObject cobj, boolean b, boolean b1, boolean b2, boolean b3) {
        return null;
    }

    public ServiceResult insertLt(CCObject in) throws Exception {
        List<CCObject> datalist = new ArrayList<CCObject>();
        datalist.add(in);
        return insert(datalist);
    }

    public ServiceResult updateLt(CCObject objectData) throws Exception {
        return null;
    }

    public void addMicroPost(String string, Object object, Object object2, Object object3, Object object4,
            Object object5, Object object6, String string2, String emailSubject, String string3, String stkhid,
            String stkhid2) {
    }

    public String sendEmail(String subject, String htmlBody, String toAddress, String relatedid, String ccaddress,
            String bccaddress, String attachmentId, String s, String s1) {
        return "";
    }

    public String uploadAttachment(HashMap<String, Object> m) {
        return null;
    }

    public void deleteShareObjectBySql(String account, String s) {
    }

    public List<CCObject> cqueryByFields(String object, String exp, String fields) {
        return cquery(object, exp, fields);
    }

    public String submitForApproval(String id, String s, String s1) {
        return null;
    }

    public String process(String processType, String tid, String touserId, String comments, String appPath) {
        return null;
    }

    public ServiceResult update(List<CCObject> ccobjs) throws BusiException {
        return null;
    }

    // -------- Helper methods --------

    static JSONArray buildDataArray(CCObject cobj) {
        JSONArray arry = new JSONArray();
        JSONObject obj = new JSONObject();
        for (String _key : cobj.keySet()) {
            if (!"CCObjectAPI".equals(_key)) {
                obj.put(_key, cobj.get(_key) == null ? "" : cobj.get(_key) + "");
            }
        }
        arry.add(obj);
        return arry;
    }

    static String extractObjectApiName(CCObject cobj) {
        return cobj.get("CCObjectAPI") == null ? "" : cobj.get("CCObjectAPI") + "";
    }

    // -------- Inner classes --------

    public static class PageQueryResult {
        public final List<CCObject> records;
        public final JSONObject raw;

        public PageQueryResult(List<CCObject> records, JSONObject raw) {
            this.records = records;
            this.raw = raw;
        }

        public int getPageNum() {
            JSONObject data = raw != null ? raw.getJSONObject("data") : null;
            return data != null ? data.getIntValue("pageNum") : 0;
        }

        public int getPageSize() {
            JSONObject data = raw != null ? raw.getJSONObject("data") : null;
            return data != null ? data.getIntValue("pageSize") : 0;
        }

        public long getTotalCount() {
            JSONObject data = raw != null ? raw.getJSONObject("data") : null;
            return data != null ? data.getLongValue("totalCount") : 0L;
        }

        public int getPageCount() {
            JSONObject data = raw != null ? raw.getJSONObject("data") : null;
            return data != null ? data.getIntValue("pageCount") : 0;
        }

        public boolean isHasPre() {
            JSONObject data = raw != null ? raw.getJSONObject("data") : null;
            return data != null && Boolean.TRUE.equals(data.getBoolean("hasPre"));
        }

        public boolean isHasNext() {
            JSONObject data = raw != null ? raw.getJSONObject("data") : null;
            return data != null && Boolean.TRUE.equals(data.getBoolean("hasNext"));
        }
    }

    public static class CqlStaticQueryResult {
        public final List<CCObject> records;
        public final String nextRecordQueryId;
        public final JSONObject raw;

        public CqlStaticQueryResult(List<CCObject> records, String nextRecordQueryId, JSONObject raw) {
            this.records = records;
            this.nextRecordQueryId = nextRecordQueryId;
            this.raw = raw;
        }
    }
}
