package com.halo.assistant;

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Build;
import android.os.StrictMode;
import android.preference.PreferenceManager;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.collection.ArrayMap;
import androidx.lifecycle.ProcessLifecycleOwner;
import androidx.multidex.MultiDexApplication;
import androidx.webkit.WebViewCompat;

import com.alibaba.android.arouter.launcher.ARouter;
import com.facebook.animated.giflite.GifDecoder;
import com.facebook.imageformat.DefaultImageFormats;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.core.ImagePipelineFactory;
import com.facebook.imagepipeline.core.ImageTranscoderType;
import com.facebook.imagepipeline.core.MemoryChunkType;
import com.facebook.imagepipeline.decoder.ImageDecoderConfig;
import com.gh.base.GlobalActivityLifecycleObserver;
import com.gh.common.FixedRateJobHelper;
import com.gh.common.filter.RegionSettingHelper;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DownloadNotificationHelper;
import com.gh.common.util.DownloadObserver;
import com.gh.common.util.HomeBottomBarHelper;
import com.gh.common.util.PackageHelper;
import com.gh.common.util.PackageUtils;
import com.gh.common.videolog.VideoRecordUtils;
import com.gh.download.simple.DownloadMessageHandler;
import com.gh.download.simple.SimpleDownloadDatabase;
import com.gh.download.simple.SimpleDownloadManager;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.Injection;
import com.gh.gamecenter.common.constant.Config;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.image.EmptyDecoder;
import com.gh.gamecenter.common.tracker.Tracker;
import com.gh.gamecenter.common.utils.DarkModeUtils;
import com.gh.gamecenter.common.utils.DeviceUtils;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.ImageUtils;
import com.gh.gamecenter.common.utils.PackageFlavorHelper;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.gamecenter.core.iinterface.IApplication;
import com.gh.gamecenter.core.utils.DisplayUtils;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.entity.SubjectRecommendEntity;
import com.gh.gamecenter.fragment.MainWrapperRepository;
import com.gh.gamecenter.login.user.UserRepository;
import com.gh.gamecenter.packagehelper.PackageRepository;
import com.gh.gamecenter.provider.ChannelProvider;
import com.gh.gamecenter.provider.ChannelProviderImp;
import com.gh.gamecenter.receiver.ActivitySkipReceiver;
import com.gh.gamecenter.receiver.DownloadReceiver;
import com.gh.gamecenter.receiver.InstallAndUninstallReceiver;
import com.gh.gamecenter.receiver.InstallReceiver;
import com.gh.gamecenter.receiver.NetworkStateReceiver;
import com.gh.vspace.VHelper;
import com.github.piasy.biv.BigImageViewer;
import com.github.piasy.biv.loader.fresco.FrescoImageLoader;
import com.lightgame.utils.Utils;
import com.llew.huawei.verifier.LoadedApkHuaWei;
import com.shuyu.gsyvideoplayer.cache.CacheFactory;
import com.shuyu.gsyvideoplayer.player.PlayerFactory;

import java.lang.reflect.Method;
import java.util.List;
import java.util.ServiceLoader;

import io.reactivex.plugins.RxJavaPlugins;
import tv.danmaku.ijk.media.exo2.Exo2PlayerManager;
import tv.danmaku.ijk.media.exo2.ExoPlayerCacheManager;

public class HaloApp extends MultiDexApplication {

    private static HaloApp mInstance;
    private static final ArrayMap<String, Object> sObjectMap = new ArrayMap<>();

    private String mChannel;
    private String mGid;
    private String mTemporaryLocalDeviceId; // 临时设备ID (本地生成，与GID不相关)
    private String mUA;
    private String mOAID = "";
    private String mServerUserMark = ""; // 服务端用来标记用户是新/老的标记，有 new 和 old 两个，获取不到时为空

    private boolean mIsPostInitialized = false; // 是否已经延迟初始化过相关组件，避免重复初始化
    private boolean mIsReinstallTheSameVersion = false; // 当前用户是否重新安装了一次当前版本后的第一次打开

    public long deviceRamSize = 0;
    public boolean isBrandNewInstall = false; // 当前用户是否是安装光环后第一次打开
    public boolean isNewForThisVersion = false; // 当前用户是否是安装当前版本后第一次打开 (包括全新和更新)
    public boolean isRunningForeground = false;  // 标记当前 APP 是否处于前台运行中

    public boolean isAlreadyUpAndRunning = false; // 应用是否处于运行中状态 (进入到 MainActivity 就当运行中)
    private List<String> webViewAbiList;
    private ChannelProvider mChannelProvider = new ChannelProviderImp();
    private final ServiceLoader<IApplication> mApplicationList = ServiceLoader.load(IApplication.class, this.getClass().getClassLoader());

    public static void put(String key, Object object) {
        sObjectMap.put(key, object);
    }

    public static Object get(String key, boolean isRemove) {
        if (isRemove) {
            return sObjectMap.remove(key);
        } else {
            return sObjectMap.get(key);
        }
    }

    public static void remove(String key) {
        sObjectMap.remove(key);
    }

    public void setGid(String gid) {
        mGid = gid;
    }

    public String getGid() {
        return mGid;
    }

    public void refreshGid() {
        DataUtils.getGid();
    }

    public void setLocalTemporaryDeviceId(String key) {
        mTemporaryLocalDeviceId = key;
    }

    public String getTemporaryLocalDeviceId() {
        return mTemporaryLocalDeviceId;
    }

    public void setServerUserMark(String mark) {
        mServerUserMark = mark;
    }

    public String getServerUserMark() {
        return mServerUserMark;
    }

    public static synchronized HaloApp getInstance() {
        return mInstance;
    }


    public String getChannel() {
        // 存在 IO 初始化线程阻塞(万物皆可阻塞)导致 mChannel 为空的情况，这里特殊处理下
        if (TextUtils.isEmpty(mChannel)) {
            return "";
        } else {
            return mChannel;
        }
    }

    public void setChannel(String channel) {
        mChannel = channel;
    }

    public void setOAID(String oaid) {
        mOAID = oaid;
    }

    public String getOAID() {
        return mOAID;
    }

    public String getUserAgent() {
        if (TextUtils.isEmpty(mUA)) {
            mUA = DeviceUtils.getUserAgent();
        }
        return mUA;
    }

    public List<String> getWebViewAbiList() {
        return webViewAbiList;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        initArouter();

        if (!Injection.appInit(this)) {
            return;
        }

        mInstance = this;

        for (IApplication application : mApplicationList) {
            application.onCreate(mInstance);
        }

        // 70ms
        PlayerFactory.setPlayManager(Exo2PlayerManager.class);
        CacheFactory.setCacheManager(ExoPlayerCacheManager.class);

        AppExecutor.getIoExecutor().execute(() -> {
            initDataHelper();
            ExtensionsKt.doOnMainProcessOnly(this, () -> Tracker.init(this));

            initFresco();

            deviceRamSize = DeviceUtils.getTotalRamSizeOfDevice(this);
            mChannel = mChannelProvider.getChannelStr(this);

            // 异步初始化 SP
            SPUtils.getString("");
        });

        RxJavaPlugins.setIoSchedulerHandler(scheduler -> AppExecutor.INSTANCE.getCachedScheduler());

        if (isUserAcceptPrivacyPolicy(this)) {
            postInit(false);
        }

        //设置严格模式
        if (BuildConfig.DEBUG) {
            StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
                    .detectAll()
                    .penaltyLog()
                    .build();
            StrictMode.setVmPolicy(policy);
        }

        // debug 包和 jenkins 内部包启用 dokit
        if (PackageFlavorHelper.IS_TEST_FLAVOR) {
            try {
                Class<?> clazz = Class.forName("com.didichuxing.doraemonkit.DoraemonKit");
                Method method = clazz.getMethod("install", Application.class);
                method.invoke(null, this);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        registerActivityLifecycleCallbacks(new GlobalActivityLifecycleObserver());

        DarkModeUtils.INSTANCE.initDarkMode();
    }

    /**
     * 需要延迟初始化的东西，以下代码调用都放置到用户同意了隐私政策之后
     */
    public void postInit(boolean initImmediately) {
        if (mIsPostInitialized) return;

        long delay = 500;
        if (initImmediately) {
            delay = 0;
        }
        postInit(delay);

        mIsPostInitialized = true;
    }

    private void postInit(long delay) {
        // 获取/更新 GID 和 读 SP 的操作不需要 delay
        DataUtils.getGid();

        String localTemporaryDeviceId = SPUtils.getString(Constants.SP_TEMPORARY_DEVICE_ID);
        if (!TextUtils.isEmpty(localTemporaryDeviceId)) {
            HaloApp.getInstance().setLocalTemporaryDeviceId(localTemporaryDeviceId);
        }

        // 刷新内存中的用户信息，避免应用进程重建时因没有用户信息数据而显示为掉登录状态
        // 必须放在外面，否则不能及时刷新用户数据
        UserRepository.getInstance().getLoginUserInfo();

        MainWrapperRepository.Companion.getInstance().getHomeNavBar();

        AppExecutor.getUiExecutor().executeWithDelay(() -> {
            initThirdPartySdk();

            FixedRateJobHelper.begin();

            RegionSettingHelper.getRegionSetting();

            ExtensionsKt.doOnMainProcessOnly(this, () -> {
                retrieveVGameInfoIfNeeded();
                PackageRepository.initData();
                PackageHelper.refreshLocalPackageList();
                PackageHelper.initList();
            });

            initReceiver();
            initPackageChangesReceiver();
            initConnectivityChangesReceiver();

            initTimeConsumingAction();

            getWebviewAbiList();

            // 注册回调以用于做各种统计
            ProcessLifecycleOwner.get().getLifecycle().addObserver(new ProcessorLifeCycleOwner());

            // 开发环境不要强制捕获相关异常,这些异常通常是需要处理的
            if (!BuildConfig.DEBUG) {
                RxJavaPlugins.setErrorHandler(throwable -> {
                    if (Config.DEFAULT_CHANNEL.equals(mChannel)) {
                        Utils.toast(this, "当前页面未捕获的异常:" + throwable.toString());
                    }
                });
            }
        }, delay);
    }

    private void initArouter() {
        if (BuildConfig.DEBUG) {  // 这两行必须写在init之前，否则这些配置在init过程中将无效
            ARouter.openLog();     // 打印日志
            ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行，必须开启调试模式！线上版本需要关闭,否则有安全风险)
        }
        ARouter.init(this); // 尽可能早，推荐在Application中初始化
    }

    public void getWebviewAbiList() {
        int versionCode = SPUtils.getInt(Constants.SP_WEBVIEW_VERSION_CODE);
        final PackageInfo webviewPackageInfo = WebViewCompat.getCurrentWebViewPackage(this);
        if (webviewPackageInfo == null) return;
        int webviewVersionCode = webviewPackageInfo.versionCode;
        String json = SPUtils.getString(Constants.SP_WEBVIEW_ABI_LIST);
        if (versionCode == webviewVersionCode && !json.isEmpty()) {
            webViewAbiList = GsonUtils.fromJsonList(json);
            return;
        }
        AppExecutor.getIoExecutor().execute(() -> {
            final String webviewPath = PackageUtils.getWebviewPath(this);
            if (webviewPath == null) return;
            final List<String> abiList = PackageUtils.getApkAbiList(webviewPath);
            webViewAbiList = abiList;
            SPUtils.setString(Constants.SP_WEBVIEW_ABI_LIST, GsonUtils.toJson(abiList));
            SPUtils.setInt(Constants.SP_WEBVIEW_VERSION_CODE, webviewVersionCode);
        });
    }

    private void initThirdPartySdk() {
        DataUtils.init(this, mChannel);
    }

    private void initDataHelper() {
        VideoRecordUtils.init(this, AppExecutor.getLogExecutor());
    }

    // todo 动态注册和静态注册并行的话会触发多次回调
    // 3.5 开始将 targetSdk 升级至 26，原来写在 Manifest 的部分 receiver 由于系统限制需要换成在运行时注册
    private void initReceiver() {
        DownloadReceiver downloadReceiver = new DownloadReceiver();
        IntentFilter downloadFilter = new IntentFilter();
        downloadFilter.addAction(DownloadNotificationHelper.ACTION_DOWNLOAD);
        downloadFilter.addAction(DownloadNotificationHelper.ACTION_VDOWNLOAD);
        this.registerReceiver(downloadReceiver, downloadFilter);

        InstallReceiver installReceiver = new InstallReceiver();
        IntentFilter installFilter = new IntentFilter();
        installFilter.addAction(DownloadNotificationHelper.ACTION_INSTALL);
        this.registerReceiver(installReceiver, installFilter);

        ActivitySkipReceiver skipReceiver = new ActivitySkipReceiver();
        IntentFilter skipFilter = new IntentFilter();
        skipFilter.addAction(ActivitySkipReceiver.ACTION_ACTIVITY_SKIP);
        this.registerReceiver(skipReceiver, skipFilter);
    }

    private void initPackageChangesReceiver() {
        InstallAndUninstallReceiver receiver = new InstallAndUninstallReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addDataScheme("package");
        this.registerReceiver(receiver, intentFilter);
    }

    private void initConnectivityChangesReceiver() {
        NetworkStateReceiver receiver = new NetworkStateReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        this.registerReceiver(receiver, intentFilter);
    }

    private void initTimeConsumingAction() {
        AppExecutor.getIoExecutor().execute(() -> {
            // 初始化全局下载监听
            DownloadObserver.initObserver();

            if ("miui".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
                DisplayUtils.sShouldUseLegacyMiuiStatusBarMethod = true;
            }

            // 避免在华为设备上出现 `Register too many Broadcast Receivers` 异常，可见 https://github.com/llew2011/HuaWeiVerifier
            LoadedApkHuaWei.hookHuaWeiVerifier(this);

            SimpleDownloadManager.INSTANCE.init();
            DownloadMessageHandler.INSTANCE.init(SimpleDownloadDatabase.getInstance().downloadDao());

            // 预加载游戏库图标
            SubjectRecommendEntity barData = HomeBottomBarHelper.getDefaultGameBarData();
            if (!TextUtils.isEmpty(barData.getIconSelect())) {
                ImageUtils.getPicasso().load(Uri.parse(barData.getIconSelect())).fetch();
            }
            if (!TextUtils.isEmpty(barData.getIconUnselect())) {
                ImageUtils.getPicasso().load(Uri.parse(barData.getIconUnselect())).fetch();
            }
        });
    }


    public void initFresco() {
        // 初始化 Fresco(BigImageViewer 已包含Fresco)
        if (!ImageUtils.isFrescoInitialized()) {
            // 在 5.0 & 5.1 设备上 disable native code，原因是应用附带了 arm64 的 SO 以后在部分 5.0/5.1 设备
            // 会出现找不到 arm64 so 的情况，具体可见
            // https://sentry.ghzs.com/organizations/lightgame/issues/53107/
            // 所以这里尝试在 5.0 & 5.1 设备上关闭 fresco 的 native 解码，应该会让 5.0 & 5.1 的设备 OOM 概率提高，但先试试效果
            // 同时禁用动图
            ImagePipelineConfig.Builder pipelineConfigBuilder = ImagePipelineConfig.newBuilder(this);
            ImageDecoderConfig.Builder decodeConfigBuilder = ImageDecoderConfig.newBuilder();

            if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
                    || Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) {
                pipelineConfigBuilder.setMemoryChunkType(MemoryChunkType.BUFFER_MEMORY)
                        .setImageTranscoderType(ImageTranscoderType.JAVA_TRANSCODER);

                decodeConfigBuilder.overrideDecoder(DefaultImageFormats.GIF, new GifDecoder()).build();

                String manufacture = Build.MANUFACTURER.toLowerCase();

                // OPPO 和 VIVO 的 5.1.1 设备还会去加载 WEBP_ANIMATED 的 SO，
                // 实测没有发现有地方使用 WEBP_ANIMATED 的图片，这里用空占位图来替换 WEBP 动图
                if ("oppo".equals(manufacture) || "vivo".equals(manufacture)) {
                    decodeConfigBuilder.overrideDecoder(DefaultImageFormats.WEBP_ANIMATED, new EmptyDecoder()).build();
                }

                pipelineConfigBuilder
                        .setImageDecoderConfig(decodeConfigBuilder.build())
                        .experiment()
                        .setNativeCodeDisabled(true);

                // 图片仅加载静态图片
                ImageUtils.disableAnimatedImage();
            }

            try {
                BigImageViewer.initialize(FrescoImageLoader.with(this, pipelineConfigBuilder.build()));
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    private void retrieveVGameInfoIfNeeded() {
        VHelper.init(this, true);
    }

    public boolean isReinstallTheSameVersion() {
        return mIsReinstallTheSameVersion;
    }

    public void setIsReinstallTheSameVersion(boolean value) {
        mIsReinstallTheSameVersion = value;
    }

    public Application getApplication() {
        return this;
    }

    /**
     * 判断当前用户是否是已经同意过隐私政策协议
     * 覆盖安装进入过首页的也当作是同意了
     */
    public static boolean isUserAcceptPrivacyPolicy(Context context) {
        return !PreferenceManager.getDefaultSharedPreferences(context).getBoolean("isNewFirstLaunchV" + PackageUtils.getGhVersionName(), true)
                || !SPUtils.getBooleanWithContext(context, Constants.SP_BRAND_NEW_USER, true)
                || SPUtils.getBooleanWithContext(context, Constants.SP_IS_USER_ACCEPTED_PRIVACY_STATEMENT, false);
    }

//    @NonNull
//    @Override
//    public Configuration getWorkManagerConfiguration() {
//        return new Configuration.Builder().build();
//    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        for (IApplication application : mApplicationList) {
            application.attachBaseContext();
        }
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();

        clearMemoryCaches();

        for (IApplication application : mApplicationList) {
            application.onLowMemory();
        }
    }

    @Override
    public void onConfigurationChanged(@NonNull android.content.res.Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        for (IApplication application : mApplicationList) {
            application.onConfigurationChanged(newConfig);
        }
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        for (IApplication application : mApplicationList) {
            application.onTerminate();
        }
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);

        if (level >= TRIM_MEMORY_MODERATE) {
            clearMemoryCaches();
        }

        for (IApplication application : mApplicationList) {
            application.onTrimMemory(level);
        }
    }

    private void clearMemoryCaches() {
        ImagePipelineFactory.getInstance().getImagePipeline().clearMemoryCaches();
        ImageUtils.getPicassoLruCache().clear();
        ImageUtils.clearUrlCaches();
    }

}
