/*
 * Tencent is pleased to support the open source community by making wechat-matrix available.
 * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
 * Licensed under the BSD 3-Clause License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://opensource.org/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.gem.media.splash.base.matrix;

import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Process;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;

import com.gem.media.StatsStaticHelper;
import com.gem.media.splash.base.utils.ALog;
import com.tencent.matrix.plugin.DefaultPluginListener;
import com.tencent.matrix.report.Issue;

import org.json.JSONObject;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class MdPluginListener extends DefaultPluginListener {

    public static final String TAG = "MdPluginListener";

    private static final String EVENT_ANR_EXCEPTION = "anr_ex";

    private static final int MSG_CHECK_REAL_ANR = 1;

    private static final int CHECK_ERROR_STATE_INTERVAL = 500;
    private static final int ANR_DUMP_MAX_TIME = 20000;
    private static final int CHECK_ERROR_STATE_COUNT = ANR_DUMP_MAX_TIME / CHECK_ERROR_STATE_INTERVAL;


    private Context context;

    private Handler anrCheckHandler;

    private HandlerThread handlerThread;

    private volatile int checkNum = 0;

    public MdPluginListener(Context context) {
        super(context);
        this.context = context;
    }

    private void initHandle() {
        handlerThread = new HandlerThread("mtx_ar_t");
        handlerThread.start();
        anrCheckHandler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what) {
                    case MSG_CHECK_REAL_ANR:
                        try {
                            boolean myAnr = checkErrorState();
//                            ALog.d(TAG, " myAnr :" + myAnr + ", checkNum :" + checkNum);
                            if (myAnr) {
                                ALog.d(TAG, "real anr occur !");
                                Process.killProcess(Process.myPid());
                                break;
                            }
                            ALog.d(TAG, " ---not anr ----" + ", checkNum :" + checkNum);
                        } catch (Throwable e) {
                            ALog.e(TAG, "myAnr e : " + Log.getStackTraceString(e));
                        } finally {
                            checkNum++;
                            ALog.d(TAG, "finally checkNum: " + checkNum + ",,max :" + CHECK_ERROR_STATE_COUNT);
                            if (checkNum < CHECK_ERROR_STATE_COUNT && anrCheckHandler != null) {
                                try {
                                    Message message = Message.obtain();
                                    message.what = MSG_CHECK_REAL_ANR;
                                    message.arg1 = checkNum;
                                    anrCheckHandler.sendMessageDelayed(message, CHECK_ERROR_STATE_INTERVAL);
                                } catch (Exception e) {
                                    destroyHandler();
                                }
                            } else {
                                ALog.d(TAG, "destroyHandler");
                                destroyHandler();
                            }
                        }
                        break;
                }
            }
        };
    }

    private void destroyHandler() {
        try {
            if (handlerThread != null) {
                handlerThread.quitSafely();
            }
        } catch (Exception e) {
        }
        handlerThread = null;
        anrCheckHandler = null;
        checkNum = 0;
    }

    @Override
    public void onReportIssue(final Issue issue) {
        super.onReportIssue(issue);
//        ALog.d(TAG, " issue :" + issue);
        checkANRHappened(issue);
    }

    private void checkANRHappened(Issue issue) {
        if (issue == null) {
            return;
        }
        try {
            JSONObject content = issue.getContent();
            if (content == null) {
                return;
            }
            String type = content.getString("detail");
            ALog.d(TAG, " issue type :" + type);
            if (!TextUtils.isEmpty(type) && type.contains("ANR")) {
                ALog.d(TAG, "check anr !");
                statAnr(type);
                if (anrCheckHandler == null) {
                    initHandle();
                }
                Message message = Message.obtain();
                message.what = MSG_CHECK_REAL_ANR;
                message.arg1 = checkNum;
                anrCheckHandler.sendMessageDelayed(message, CHECK_ERROR_STATE_INTERVAL);
            }
        } catch (Exception e) {
        }
//        ALog.d(TAG, " isAnrMsg :" + isAnrMsg);
/*        if (!isAnrMsg) {
            return;
        }*/

 /*       new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    try {
                        boolean myAnr = checkErrorState();
                        ALog.d(TAG, " myAnr :" + myAnr + ", i :" + i);
                        if (myAnr) {
                            Process.killProcess(Process.myPid());
                            break;
                        }
                        ALog.d(TAG, " -------"  + ", i :" + i);
                        Thread.sleep(500);
                    } catch (Throwable e) {
                        ALog.e(TAG, "myAnr e : " + Log.getStackTraceString(e));
                        break;
                    }
                }
            }
        }).start();*/
    }


    private boolean checkErrorState() {
        try {
            ALog.i(TAG, "[checkErrorState] start");
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.ProcessErrorStateInfo> procs = am.getProcessesInErrorState();
            if (procs == null) {
                ALog.i(TAG, "[checkErrorState] procs == null");
                return false;
            }

            for (ActivityManager.ProcessErrorStateInfo proc : procs) {
                ALog.i(TAG, "[checkErrorState] found Error State proccessName = "+ proc.processName + ", proc.condition = " + proc.condition);
                if (proc.uid != Process.myUid()
                        && proc.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) {
                    ALog.i(TAG, "maybe received other apps ANR signal");
                    return false;
                }

                if (proc.pid != Process.myPid()) continue;

                if (proc.condition != ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) {
                    continue;
                }
                ALog.i(TAG, "error sate proc.condition =" + proc.condition);
                return true;
            }
            return false;
        } catch (Throwable t) {
            ALog.e(TAG, "[checkErrorState] error : %s", t.getMessage());
        }
        return false;
    }

    private void statAnr(String type) {
        try {
            Map<String, String> map = new HashMap<>();
            map.put("type", type);
            StatsStaticHelper.reportOnEvent(EVENT_ANR_EXCEPTION, null, map);
        } catch (Exception e) {
        }
    }
}
