package com.capacitorjs.liveupdates;

import android.app.Activity;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import androidx.annotation.NonNull;
import androidx.core.content.pm.PackageInfoCompat;
import com.getcapacitor.Bridge;
import com.getcapacitor.JSObject;
import com.getcapacitor.Logger;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginConfig;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import com.getcapacitor.util.InternalUtils;
import io.ionic.liveupdates.LiveUpdate;
import io.ionic.liveupdates.LiveUpdateManager;
import io.ionic.liveupdates.data.model.FailResult;
import io.ionic.liveupdates.data.model.FailStep;
import io.ionic.liveupdates.data.model.SyncResult;
import io.ionic.liveupdates.network.ProgressCallback;
import io.ionic.liveupdates.network.SyncCallback;
import java.io.File;
import java.io.IOException;
import org.json.JSONObject;

@CapacitorPlugin(name = "LiveUpdates")
public class LiveUpdatesPlugin extends Plugin {

    private static final String LAST_BINARY_VERSION_CODE = "lastBinaryVersionCode";
    private static final String LAST_BINARY_VERSION_NAME = "lastBinaryVersionName";
    private static final String PREFS_NAME = "liveUpdatesPreferences";
    private static final String CONFIG_KEY_NAME = "liveUpdatesConfig";

    private LiveUpdateConfig staticConfig;
    private LiveUpdateConfig config;
    private SharedPreferences prefs;

    @Override
    public void load() {
        PluginConfig config = getConfig();
        init(config);
    }

    public void init(PluginConfig pluginConfig) {
        prefs = getContext().getSharedPreferences(PREFS_NAME, Activity.MODE_PRIVATE);
        if (!bridge.getConfig().isLoggingEnabled()) {
            LiveUpdateManager.loggingEnabled(false);
        }

        staticConfig = LiveUpdateConfig.create(pluginConfig);
        if (isNewBinary()) {
            config = staticConfig;
            prefs.edit().remove(CONFIG_KEY_NAME).apply();
        } else {
            String configJsonString = prefs.getString(CONFIG_KEY_NAME, null);
            if (configJsonString != null) {
                try {
                    JSONObject configJson = new JSONObject(configJsonString);
                    config = LiveUpdateConfig.create(configJson);
                } catch (Exception e) {
                    // Invalid string written to prefs, delete it
                    prefs.edit().remove(CONFIG_KEY_NAME).apply();
                    config = staticConfig;
                }
            } else {
                config = staticConfig;
            }
        }
        LiveUpdateManager.initialize(getContext());
        if (config != null) {
            if (!config.isEnabled()) {
                Logger.warn("Live Updates is disabled in the config. Skipping initialization...");
                return;
            }

            String urlToken = config.getUrlToken();
            if (urlToken != null) {
                LiveUpdateManager.setCustomUrl(getContext(), urlToken);
            }

            boolean usesSecureLiveUpdates = false;
            String keyFilePath = config.getKey();
            if (keyFilePath != null) {
                int keyFileTrimIndex = 0;
                if (keyFilePath.lastIndexOf('/') > -1) {
                    keyFileTrimIndex = keyFilePath.lastIndexOf('/') + 1;
                }

                String keyFileName = keyFilePath.substring(keyFileTrimIndex);

                try {
                    getContext().getAssets().open(keyFileName);
                    LiveUpdateManager.INSTANCE.setSecureLiveUpdatePEM(keyFileName);
                    usesSecureLiveUpdates = true;
                } catch (IOException e) {
                    throw new RuntimeException("Secure live update public key file not found in assets: " + keyFileName);
                }
            }

            LiveUpdate liveUpdate = new LiveUpdate(
                config.getAppId(),
                config.getChannel(),
                usesSecureLiveUpdates,
                config.getUpdateStrategy()
            );
            liveUpdate.setAssetPath(Bridge.DEFAULT_WEB_ASSET_DIR);
            if (config.getMaxVersions() != null) {
                LiveUpdateManager.INSTANCE.setMaxVersions(config.getMaxVersions());
            }
            LiveUpdateManager.cleanVersions(getContext(), liveUpdate.getAppId());
            LiveUpdateManager.addLiveUpdateInstance(getContext(), liveUpdate);

            if (config.getAutoUpdateMethod().equalsIgnoreCase("background")) {
                LiveUpdateManager.sync(
                    getContext(),
                    liveUpdate.getAppId(),
                    new SyncCallback() {
                        @Override
                        public void onAppComplete(@NonNull FailResult failResult) {
                            // Fail msg
                        }

                        @Override
                        public void onAppComplete(@NonNull SyncResult syncResult) {
                            File latestApp = LiveUpdateManager.getLatestAppDirectory(getContext(), config.getAppId());
                            if (latestApp != null) {
                                SharedPreferences prefs = getContext().getSharedPreferences(
                                    com.getcapacitor.plugin.WebView.WEBVIEW_PREFS_NAME,
                                    Activity.MODE_PRIVATE
                                );
                                prefs.edit().putString(com.getcapacitor.plugin.WebView.CAP_SERVER_PATH, latestApp.getPath()).apply();
                            }
                        }

                        @Override
                        public void onSyncComplete() {
                            // Do nothing
                        }
                    }
                );
            }

            Integer maxVersion = config.getMaxVersions();
            if (maxVersion != null) {
                LiveUpdateManager.INSTANCE.setMaxVersions(maxVersion);
            }
        }
    }

    private void setServerBasePath() {
        if (config.getAppId() != null) {
            File latestApp = LiveUpdateManager.getLatestAppDirectory(getContext(), config.getAppId());
            if (latestApp != null) {
                bridge.setServerBasePath(latestApp.getPath());
            }
        }
    }

    @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
    public void sync(final PluginCall call) {
        if (!config.isEnabled()) {
            Logger.warn("Live Updates is disabled in the config. Skipping sync...");

            JSObject returnObj = new JSObject();
            returnObj.put("failStep", FailStep.CHECK.name());
            returnObj.put("message", "Live Update failed because the plugin is disabled in the Capacitor config.");
            call.resolve(returnObj);
            return;
        }

        if (config.getAppId() == null || config.getAppId().isEmpty()) {
            JSObject returnObj = new JSObject();
            returnObj.put("failStep", FailStep.CHECK.name());
            returnObj.put("message", "Live Update failed because appId was not provided in the plugin config.");
            call.resolve(returnObj);
            return;
        }
        call.setKeepAlive(true);
        LiveUpdateManager.sync(
            getContext(),
            config.getAppId(),
            new ProgressCallback() {
                @Override
                public void onProgress(@NonNull String appId, double progressValue) {
                    JSObject returnObj = new JSObject();
                    returnObj.put("progress", progressValue);

                    call.resolve(returnObj);
                }

                @Override
                public void onAppComplete(@NonNull FailResult failResult) {
                    JSObject returnObj = new JSObject();
                    returnObj.put("appId", failResult.getLiveUpdate().getAppId());
                    returnObj.put("failStep", failResult.getFailStep());
                    returnObj.put(
                        "message",
                        "Live Update failed on " + failResult.getFailStep() + " step. Reason: " + failResult.getFailMsg()
                    );

                    call.resolve(returnObj);
                    call.release(getBridge());
                }

                @Override
                public void onAppComplete(@NonNull SyncResult syncResult) {
                    JSObject returnObj = new JSObject();

                    JSObject liveUpdateObj = new JSObject();
                    liveUpdateObj.put("appId", syncResult.getLiveUpdate().getAppId());
                    liveUpdateObj.put("channel", syncResult.getLiveUpdate().getChannelName());
                    returnObj.put("liveUpdate", liveUpdateObj);

                    if (syncResult.getSnapshot() == null) {
                        returnObj.put("snapshot", null);
                    } else {
                        JSObject snapshotObj = new JSObject();
                        snapshotObj.put("id", syncResult.getSnapshot().getId());
                        snapshotObj.put("buildId", syncResult.getSnapshot().getBuildId());
                        returnObj.put("snapshot", snapshotObj);
                    }

                    returnObj.put("source", syncResult.getSource().name().toLowerCase());
                    returnObj.put("activeApplicationPathChanged", syncResult.getLatestAppDirectoryChanged());

                    File latestApp = LiveUpdateManager.getLatestAppDirectory(getContext(), config.getAppId());
                    if (latestApp != null) {
                        SharedPreferences prefs = getContext().getSharedPreferences(
                            com.getcapacitor.plugin.WebView.WEBVIEW_PREFS_NAME,
                            Activity.MODE_PRIVATE
                        );
                        prefs.edit().putString(com.getcapacitor.plugin.WebView.CAP_SERVER_PATH, latestApp.getPath()).apply();
                    }

                    call.resolve(returnObj);
                    call.release(getBridge());
                }

                @Override
                public void onSyncComplete() {
                    // Do Nothing
                }
            }
        );
    }

    @PluginMethod
    public void reload(final PluginCall call) {
        setServerBasePath();
        call.resolve();
    }

    @PluginMethod
    public void getConfig(final PluginCall call) {
        call.resolve(config.toJson());
    }

    @PluginMethod
    public void setConfig(final PluginCall call) {
        LiveUpdate current = LiveUpdateManager.getApps().get(config.getAppId());
        boolean configUpdated = config.update(call.getData());
        if (configUpdated) {
            prefs.edit().putString(CONFIG_KEY_NAME, config.toJson().toString()).apply();
            LiveUpdateManager.INSTANCE.setMaxVersions(config.getMaxVersions());
            LiveUpdate newLiveUpdate = new LiveUpdate(
                config.getAppId(),
                config.getChannel(),
                current.getUsesSecureLiveUpdates(), // This defaults to false
                config.getUpdateStrategy()
            );
            newLiveUpdate.setAssetPath(current.getAssetPath());
            LiveUpdateManager.addLiveUpdateInstance(getContext(), newLiveUpdate);
        }
        call.resolve();
    }

    @PluginMethod
    public void resetConfig(final PluginCall call) {
        LiveUpdate current = LiveUpdateManager.getApps().get(config.getAppId());
        prefs.edit().remove(CONFIG_KEY_NAME).apply();
        config = staticConfig;
        LiveUpdate resetLiveUpdate = new LiveUpdate(
            config.getAppId(),
            config.getChannel(),
            current.getUsesSecureLiveUpdates(),
            config.getUpdateStrategy()
        );
        resetLiveUpdate.setAssetPath(Bridge.DEFAULT_WEB_ASSET_DIR);
        LiveUpdateManager.addLiveUpdateInstance(getContext(), resetLiveUpdate);
    }

    // Mostly copy-pasta from Bridge.java without writing new versions
    private boolean isNewBinary() {
        String versionCode = "";
        String versionName = "";
        SharedPreferences prefs = getContext().getSharedPreferences(
            com.getcapacitor.plugin.WebView.WEBVIEW_PREFS_NAME,
            Activity.MODE_PRIVATE
        );
        String lastVersionCode = prefs.getString(LAST_BINARY_VERSION_CODE, null);
        String lastVersionName = prefs.getString(LAST_BINARY_VERSION_NAME, null);

        try {
            PackageManager pm = getContext().getPackageManager();
            PackageInfo pInfo = InternalUtils.getPackageInfo(pm, getContext().getPackageName());
            versionCode = Integer.toString((int) PackageInfoCompat.getLongVersionCode(pInfo));
            versionName = pInfo.versionName;
        } catch (Exception ex) {
            Logger.error("Unable to get package info", ex);
        }

        return !versionCode.equals(lastVersionCode) || !versionName.equals(lastVersionName);
    }
}
