
package com.appbundle;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.content.Context;
import android.content.SharedPreferences;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static androidx.core.content.FileProvider.getUriForFile;
import static com.appbundle.RNAppBundlePackage.BUNDLE_FILE;
import static com.appbundle.RNAppBundlePackage.CURRENT_VERSION_KEY;
import static com.appbundle.RNAppBundlePackage.IS_FIRST_LOAD_OK_KEY;
import static com.appbundle.RNAppBundlePackage.IS_FIRST_TIME_KEY;
import static com.appbundle.RNAppBundlePackage.LAST_VERSION_KEY;
import static com.appbundle.RNAppBundlePackage.PACKAGE_KEY;
import static com.appbundle.RNAppBundlePackage.PACK_INFO_KEY;
import static com.appbundle.RNAppBundlePackage.PREVIOUS_VERSION_KEY;
import static com.appbundle.RNAppBundlePackage.appendPathComponent;
import static com.appbundle.RNAppBundlePackage.getAppVersionName;
import static com.appbundle.RNAppBundlePackage.getBundleInfoForPath;
import static com.appbundle.RNAppBundlePackage.getCurrentPackPath;
import static com.appbundle.RNAppBundlePackage.getPackageForVersion;

public class RNAppBundleModule extends ReactContextBaseJavaModule {

    private final ReactApplicationContext reactContext;
    private static final String TAG = "RNAppBundleModule";

    private boolean _restartInProgress = false;
    private ArrayList<Boolean> _restartQueue = new ArrayList<>();


    public RNAppBundleModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
        Log.e(TAG, "RNAppBundleModule: 111111");
    }

    @Override
    public String getName() {
        return "AppBundleUpdate";
    }

    @ReactMethod
    protected void installAPK(String path) {

        File apkFile = new File(path);
        if (!apkFile.exists()) {
            return;
        }
        try {
            Uri apkUri;
            Intent intent;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                apkUri = getUriForFile(reactContext, reactContext.getPackageName() + ".fileprovider", apkFile);
                intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
                intent.setData(apkUri);
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            } else {
                apkUri = Uri.fromFile(apkFile);
                intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            reactContext.startActivity(intent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @ReactMethod
    public void updateBundle(String path) {
        try {
            // 1. 读取包信息
            JSONObject bundleInfo = getBundleInfoForPath(path);
            if (bundleInfo == null) {
                Log.e("AppBundleUpdate", "未读取到版本信息");
                return;
            }

            // 2. 版本校验
            String version = bundleInfo.getString("version");
            String appVersion = getAppVersionName();
            if (!appVersion.equals(version)) {
                Log.e("AppBundleUpdate", "大版本不一致");
                return;
            }

            // 3. 准备目录路径
            int bundle = bundleInfo.getInt("bundle");
            File packPath = getPackageForVersion(version, bundle);

            // 4. 更新配置信息
            SharedPreferences prefs = reactContext
                    .getSharedPreferences("RNAppBundle", Context.MODE_PRIVATE);
            JSONObject newInfo = new JSONObject(prefs.getString(PACK_INFO_KEY, "{}"));

            newInfo.put(PREVIOUS_VERSION_KEY, newInfo.opt(CURRENT_VERSION_KEY));
            newInfo.put(LAST_VERSION_KEY, bundleInfo);
            newInfo.put(CURRENT_VERSION_KEY, bundleInfo);
            newInfo.put(IS_FIRST_TIME_KEY, true);
            newInfo.put(IS_FIRST_LOAD_OK_KEY, false);

            prefs.edit()
                    .putString(PACK_INFO_KEY, newInfo.toString())
                    .apply();

            copy(path,packPath.getAbsolutePath());
            // 5. 文件操作
            loadBundle();

        } catch (JSONException e) {
            Log.e("AppBundleUpdate", "更新失败", e);
        }
    }

    @ReactMethod
    public void markSuccess() {
        try {
            SharedPreferences prefs = reactContext
                    .getSharedPreferences("RNAppBundle", Context.MODE_PRIVATE);
            JSONObject newInfo = new JSONObject(prefs.getString(PACK_INFO_KEY, "{}"));

            newInfo.put(IS_FIRST_TIME_KEY, false);
            newInfo.put(IS_FIRST_LOAD_OK_KEY, true);

            prefs.edit()
                    .putString(PACK_INFO_KEY, newInfo.toString())
                    .apply();

            clearInvalidFiles();
        } catch (JSONException e) {
            Log.e("AppBundleUpdate", "标记成功状态失败", e);
        }
    }

    // 新增获取最后版本方法
    @ReactMethod
    public void getLastVersion(Promise promise) {
        try {
            SharedPreferences prefs = reactContext.getSharedPreferences("RNAppBundle", Context.MODE_PRIVATE);
            String packInfoJson = prefs.getString(PACK_INFO_KEY, "{}");
            JSONObject packInfo = new JSONObject(packInfoJson);

            if (packInfo.has(LAST_VERSION_KEY)) {
                JSONObject lastVersion = packInfo.getJSONObject(LAST_VERSION_KEY);
                WritableMap writableMap = convertJsonToWritableMap(lastVersion);
                promise.resolve(writableMap);
            } else {
                promise.resolve(null);
            }
        } catch (JSONException e) {
            promise.reject("PARSE_ERROR", "解析版本信息失败");
            Log.e("AppBundleUpdate", "获取最后版本失败", e);
        }
    }

    private WritableMap convertJsonToWritableMap(JSONObject jsonObject) throws JSONException {
        WritableMap map = new WritableNativeMap();
        Iterator<String> iterator = jsonObject.keys();
        while (iterator.hasNext()) {
            String key = iterator.next();
            Object value = jsonObject.get(key);
            if (value instanceof String) {
                map.putString(key, (String) value);
            } else if (value instanceof Boolean) {
                map.putBoolean(key, (Boolean) value);
            } else if (value instanceof Integer) {
                map.putInt(key, (Integer) value);
            } else if (value instanceof Double) {
                map.putDouble(key, (Double) value);
            } else if (value instanceof Long) {
                map.putDouble(key, ((Long) value).doubleValue());
            }
        }
        return map;
    }

    private void clearInvalidFiles() {
        File packageRoot = new File(reactContext.getFilesDir(), PACKAGE_KEY);
        String currentVersion = getAppVersionName();

        File[] versionDirs = packageRoot.listFiles();
        if (versionDirs != null) {
            for (File versionDir : versionDirs) {
                if (!versionDir.getName().equals(currentVersion)) {
                    deleteFolder(versionDir);
                }
            }
        }
    }



    private void loadBundle() {
        try {
            // #1) Get the ReactInstanceManager instance, which is what includes the
            //     logic to reload the current React context.
            final ReactInstanceManager instanceManager = resolveInstanceManager();
            if (instanceManager == null) {
                return;
            }

            File bundleFile = new File(getCurrentPackPath(), BUNDLE_FILE);
            String latestJSBundleFile = bundleFile.getAbsolutePath();

            // #2) Update the locally stored JS bundle file path
            setJSBundle(instanceManager, latestJSBundleFile);

            // #3) Get the context creation method and fire it on the UI thread (which RN enforces)
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    try {
                        instanceManager.recreateReactContextInBackground();
                    } catch (Exception e) {
                        loadBundleLegacy();
                    }
                }
            });

        } catch (Exception e) {
            Log.e(TAG, "Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
            loadBundleLegacy();
        }
    }

    private void loadBundleLegacy() {
        final Activity currentActivity = getCurrentActivity();
        if (currentActivity == null) {
            // The currentActivity can be null if it is backgrounded / destroyed, so we simply
            // no-op to prevent any null pointer exceptions.
            return;
        }

        currentActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                currentActivity.recreate();
            }
        });
    }

    private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBundleFile) throws IllegalAccessException {
        try {
            JSBundleLoader latestJSBundleLoader;
            if (latestJSBundleFile.toLowerCase().startsWith("assets://")) {
                latestJSBundleLoader = JSBundleLoader.createAssetLoader(getReactApplicationContext(), latestJSBundleFile, false);
            } else {
                latestJSBundleLoader = JSBundleLoader.createFileLoader(latestJSBundleFile);
            }

            Field bundleLoaderField = instanceManager.getClass().getDeclaredField("mBundleLoader");
            bundleLoaderField.setAccessible(true);
            bundleLoaderField.set(instanceManager, latestJSBundleLoader);
        } catch (Exception e) {
            Log.e(TAG, "Unable to set JSBundle - may not support this version of React Native");
            throw new IllegalAccessException("Could not setJSBundle");
        }
    }

    private ReactInstanceManager resolveInstanceManager() throws NoSuchFieldException, IllegalAccessException {


        final Activity currentActivity = getCurrentActivity();
        if (currentActivity == null) {
            return null;
        }

        ReactApplication reactApplication = (ReactApplication) currentActivity.getApplication();
        ReactInstanceManager instanceManager = reactApplication.getReactNativeHost().getReactInstanceManager();

        return instanceManager;
    }


    public boolean copy(String fromFile, String toFile) {
        Log.e(TAG, "copy: "+fromFile+"  "+toFile );
        //要复制的文件目录
        File[] currentFiles;
        File root = new File(fromFile);
        if (!root.exists()) {
            return false;
        }
        //若是存在则获取当前目录下的所有文件 填充数组
        currentFiles = root.listFiles();
        //目标目录
        File targetDir = new File(toFile);
        //建立目录
        if (!targetDir.exists()) {
            targetDir.mkdirs();
        }
        //遍历要复制该目录下的所有文件
        for (int i = 0; i < currentFiles.length; i++) {
            if (currentFiles[i].isDirectory()) {//若是当前项为子目录 进行递归{
                copy(currentFiles[i].getPath(), toFile + "/" + currentFiles[i].getName());
            } else {//若是当前项为文件则进行文件拷贝
                copySdcardFile(currentFiles[i].getPath(), toFile + "/" + currentFiles[i].getName());
            }
        }
        return true;
    }

    //要复制的目录下的全部非子目录(文件夹)文件拷贝
    public int copySdcardFile(String fromFile, String toFile) {
        try {
            InputStream fosfrom = new FileInputStream(fromFile);
            OutputStream fosto = new FileOutputStream(toFile);
            byte bt[] = new byte[1024];
            int c;
            while ((c = fosfrom.read(bt)) > 0) {
                fosto.write(bt, 0, c);
            }
            fosfrom.close();
            fosto.close();
            return 0;
        } catch (Exception ex) {
            return -1;
        }
    }

    private void deleteFolder(File folder) {
        if (folder.isDirectory()) {
            File[] files = folder.listFiles();
            if (files != null) {
                for (File file : files) {
                    deleteFolder(file);
                }
            }
        }
        folder.delete();
    }


}


