package com.tcl.ah.module.serverinfo;

import com.tcl.ah.module.config.ProjectIni;
import com.tcl.ah.module.serverinfo.data.ServerInfo;
import com.tcl.ah.module.serverinfo.data.ServerInfoIni;
import com.tcl.ah.module.serverinfo.data.Wrapper;
import com.tcl.ah.module.token.TokenHelper;
import com.tcl.ah.security.LaravelHelper;
import com.xiyue.core.share.ErrorMessage;

import org.phprpc.util.AssocArray;
import org.phprpc.util.PHPSerializer;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Map;

import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;

public class ServerInfoHandler {
    public static final int SUCCESS = 0;

    private String mUrl = ProjectIni.URL;
    private String mac;
    private String packageName;

    private OkHttpClient mOkHttpClient;

    private String mJsonValue;
    private ErrorMessage mErrorMessage;
    private Wrapper mDataWrapper;
    private String decryptValue;
    private ServerInfo mServerInfo;

    private boolean isSuccess;

    //<editor-fold desc="getter and setter">
    public String getUrl() {
        return mUrl;
    }

    public ServerInfoHandler setUrl(String url) {
        mUrl = url;
        return this;
    }

    public String getMac() {
        return mac;
    }

    public ServerInfoHandler setMac(String mac) {
        this.mac = mac;
        return this;
    }

    public String getPackageName() {
        return packageName;
    }

    public ServerInfoHandler setPackageName(String packageName) {
        this.packageName = packageName;
        return this;
    }

    public OkHttpClient getOkHttpClient() {
        return mOkHttpClient;
    }

    public ServerInfoHandler setOkHttpClient(OkHttpClient okHttpClient) {
        mOkHttpClient = okHttpClient;
        return this;
    }


    public String getJsonValue() {
        return mJsonValue;
    }

    public ErrorMessage getErrorMessage() {
        return mErrorMessage;
    }

    public Wrapper getDataWrapper() {
        return mDataWrapper;
    }

    public String getDecryptValue() {
        return decryptValue;
    }

    public ServerInfo getServerInfo() {
        return mServerInfo;
    }

    public boolean isSuccess() {
        return isSuccess;
    }

    //</editor-fold>

    private boolean getFromNet() throws Exception{
        Response<ResponseBody> response = null;
        try {
            Retrofit.Builder builder = new Retrofit.Builder()
                    .baseUrl(mUrl);
            if (mOkHttpClient != null) {
                builder.client(mOkHttpClient);
            }
            ServerInfoService service = builder.build().create(ServerInfoService.class);
            Call<ResponseBody> call = service.getConfigInfo(mac, packageName);


            response = call.execute();
            if (response.isSuccessful()) {
                mJsonValue = response.body().string();
                return true;
            } else {
                mErrorMessage = ErrorMessage.of(response.code(), response.message());
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            if (response == null) {
                if (e instanceof SocketTimeoutException) {
                    mErrorMessage = ErrorMessage.of(ErrorMessage.Code.TIMEOUT, e.getMessage(), e);
                } else {
                    mErrorMessage = ErrorMessage.of(ErrorMessage.Code.NET, e.getMessage(), e);
                }
            } else {
                mErrorMessage = ErrorMessage.of(response.code(), response.message(), e);
            }
            return false;
        }
    }

    private boolean parse() {
        try {
            mDataWrapper = Wrapper.parse(mJsonValue);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            mErrorMessage = ErrorMessage.of(ErrorMessage.Code.PARSE, e.getMessage(), e);
            return false;
        }
    }

    private boolean checkEncrypt() {
        if (SUCCESS == mDataWrapper.code) {
            if (mDataWrapper.data != null) {
                if (mDataWrapper.data.dataStr != null && mDataWrapper.data.dataStr.trim().length() != 0) {
                    return true;
                } else {
                    mErrorMessage = ErrorMessage.incompleteData(mDataWrapper.data.errMsg);
                    return false;
                }
            } else {
                mErrorMessage = ErrorMessage.emptyData(null);
                return false;
            }
        } else {
            String subMessage = null;
            if (mDataWrapper.data != null) {
                subMessage = mDataWrapper.data.errMsg;
            }
            if (subMessage != null) {
                subMessage = mDataWrapper.code + subMessage;
            } else {
                subMessage = String.valueOf(mDataWrapper.code);
            }
            mErrorMessage = ErrorMessage.of(ErrorMessage.Code.CODE, subMessage);
            return false;
        }
    }

    private String getDecryptAppCode() {
        if (TokenHelper.isExpired()) {
            if (TokenHelper.updateToken(mUrl, mac.toUpperCase())) {
                return TokenHelper.getToken().getDecryptAppCode();
            }
            return "";
        } else {
            return TokenHelper.getToken().getDecryptAppCode();
        }

    }

    private boolean decrypt() {
        String decryptAppCode = getDecryptAppCode();
        if (decryptAppCode == null || decryptAppCode.trim().length() == 0) {
            mErrorMessage = ErrorMessage.of(ErrorMessage.Code.DECRYPT, "get/update token fail");
            return false;
        } else {
            try {
                decryptValue = LaravelHelper.decrypt(decryptAppCode, mDataWrapper.data.dataStr);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                mErrorMessage = ErrorMessage.of(ErrorMessage.Code.DECRYPT, "decrypt data fail:" + e.getMessage(), e);
                return false;
            }
        }
    }

    private boolean parseDecrypt() {
        if (decryptValue == null) {
            mErrorMessage = ErrorMessage.of(ErrorMessage.Code.PARSE, "parse decrypt data is empty");
            return false;
        }
        ServerInfo info = new ServerInfo();
        PHPSerializer p = new PHPSerializer();
        AssocArray assocArray = null;
        try {
            assocArray = (AssocArray) p.unserialize(decryptValue.getBytes());
//            for (int i = 0; i < assocArray.size(); i++) {
//                AssocArray array = (AssocArray) assocArray.get(i);
            AssocArray array = assocArray;
            Map map;
            if (array.toArrayList().size() == 0) {
                map = array.toLinkedHashMap();
            } else {
                map = ((AssocArray) array.get(0)).toLinkedHashMap();
            }

            if (map.containsKey(ServerInfoIni.Prop.HTTP_INFO)) {
                AssocArray httpArray = (AssocArray) map.get(ServerInfoIni.Prop.HTTP_INFO);
                Map<String, Object> httpMap;
                if (httpArray.toArrayList().size() == 0) {
                    httpMap = httpArray.toLinkedHashMap();
                } else {
                    httpMap = ((AssocArray) httpArray.get(0)).toLinkedHashMap();
                }
                String apiHost = convertToStringOrNull(httpMap.get(ServerInfoIni.Prop.API_HOST));
                if (apiHost != null && apiHost.trim().length() != 0) {
                    if (mServerInfo == null) {
                        mServerInfo = new ServerInfo();
                    }
                    mServerInfo.httpInfo.apiHost = apiHost;
                }
            }
            if (map.containsKey(ServerInfoIni.Prop.MQTT_INFO)) {
                AssocArray mqttArray = (AssocArray) map.get(ServerInfoIni.Prop.MQTT_INFO);
                Map<String, Object> mqttMap;
                if (mqttArray.toArrayList().size() == 0) {
                    mqttMap = mqttArray.toLinkedHashMap();
                } else {
                    mqttMap = ((AssocArray) mqttArray.get(0)).toLinkedHashMap();
                }

                String host = convertToStringOrNull(mqttMap.get(ServerInfoIni.Prop.MQTT_HOST));
                String port = convertToStringOrNull(mqttMap.get(ServerInfoIni.Prop.MQTT_PORT));
                String user = convertToStringOrNull(mqttMap.get(ServerInfoIni.Prop.MQTT_USER));
                String password = convertToStringOrNull(mqttMap.get(ServerInfoIni.Prop.MQTT_PWD));
                String heartbeatLive = convertToStringOrNull(mqttMap.get(ServerInfoIni.Prop.HEARTBEAT_LIVE));
                String expire = convertToStringOrNull(mqttMap.get(ServerInfoIni.Prop.EXPIRE));
                if (!isEmpty(host) && !isEmpty(user) && !isEmpty(password)) {
                    if (mServerInfo == null) {
                        mServerInfo = new ServerInfo();
                    }
                    mServerInfo.mqttInfo.host = host;
                    mServerInfo.mqttInfo.user = user;
                    mServerInfo.mqttInfo.password = password;
                    try {
                        mServerInfo.mqttInfo.port = Integer.parseInt(port);
                    } catch (NumberFormatException e) {
                        e.printStackTrace();
                    }
                    try {
                        mServerInfo.mqttInfo.heartbeatLive = Integer.parseInt(heartbeatLive);
                    } catch (NumberFormatException e) {
                        e.printStackTrace();
                    }
                    try {
                        mServerInfo.mqttInfo.expire = Integer.parseInt(expire);
                    } catch (NumberFormatException e) {
                        e.printStackTrace();
                    }
                }
            }
            if (mServerInfo != null) {
                return true;
            } else {
                mErrorMessage = ErrorMessage.of(ErrorMessage.Code.PARSE, "parse decrypt data fail:empty");
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            mErrorMessage = ErrorMessage.of(ErrorMessage.Code.PARSE, "parse decrypt data fail:" + e.getMessage(), e);
            return false;
        }
    }

    private boolean checkDecrypt() {
        if (mServerInfo == null) {
            mErrorMessage = ErrorMessage.of(ErrorMessage.Code.EMPTY, " decrypt data is empty");
            return false;
        }
        boolean ret = mServerInfo.isQualified();
        if (!ret) {
            mErrorMessage = ErrorMessage.of(ErrorMessage.Code.INCOMPLETE);
            return false;
        } else {
            return true;
        }
    }

    public synchronized boolean get() throws Exception{
        mJsonValue = null;
        mErrorMessage = null;
        mDataWrapper = null;
        decryptValue = null;
        mServerInfo = null;
        isSuccess = false;
        if (getFromNet()) {
            if (parse()) {
                if (checkEncrypt()) {
                    if (decrypt()) {
                        if (parseDecrypt()) {
                            if (checkDecrypt()) {
                                isSuccess = true;
                            }
                        }
                    }
                }
            }
        }
        return isSuccess;
    }

    private static String convertToStringOrNull(Object value) {
        if (value != null) {
            if (value instanceof byte[]) {
                return new String((byte[]) value);
            } else if (value instanceof String) {
                return (String) value;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    private static boolean isEmpty(String val) {
        if (val == null || val.trim().length() == 0) {
            return true;
        }
        return false;
    }
}
