package com.foxx.androidcast.remoteaccess.control;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;

import com.foxx.androidcast.AppPreferences;
import com.foxx.androidcast.BuildConfig;
import com.foxx.androidcast.IRemoteAccessControlHost;
import com.foxx.androidcast.remoteaccess.RemoteAccessCoordinator;
import com.foxx.androidcast.remoteaccess.RemoteAccessMode;
import com.foxx.androidcast.remoteaccess.RemoteAccessStatusStore;
import com.foxx.androidcast.remoteaccess.RemoteAccessVpnRouting;
import com.foxx.androidcast.remoteaccess.ReverseSshTunnelBridge;
import com.foxx.androidcast.vpn.VpnServiceClient;
import com.foxx.androidcast.vpn.VpnState;

import org.json.JSONObject;

/**
 * Main-process AIDL host: reads/writes {@link AppPreferences} for the RA control web UI.
 */
public final class RemoteAccessControlHostImpl extends IRemoteAccessControlHost.Stub {
    private static final String TAG = "RaControlHost";

    private final Context appContext;
    private final Handler main = new Handler(Looper.getMainLooper());

    public RemoteAccessControlHostImpl(Context context) {
        appContext = context.getApplicationContext();
    }

    @Override
    public String getSettingsJson() {
        try {
            RemoteAccessStatusStore.Snapshot status = RemoteAccessStatusStore.load(appContext);
            JSONObject settings = RemoteAccessControlPrefsMirror.buildSnapshot(
                    AppPreferences.getDevRemoteAccessMode(appContext),
                    AppPreferences.getDevRemoteAccessVpnRouteScope(appContext),
                    AppPreferences.getDevRemoteAccessVpnAppScope(appContext),
                    status.sessionId,
                    AppPreferences.getDevRemoteAccessExpiresAt(appContext));
            JSONObject out = new JSONObject();
            out.put("ok", true);
            out.put("settings", settings);
            return out.toString();
        } catch (Exception e) {
            return errorJson("settings read failed: " + e.getMessage());
        }
    }

    @Override
    public String applySettingsJson(String bodyJson) {
        RemoteAccessControlPrefsMirror.ApplyResult patch =
                RemoteAccessControlPrefsMirror.parseApplyPatch(bodyJson);
        if (!patch.ok) {
            return errorJson(patch.error);
        }
        if (!patch.hasChanges()) {
            return getSettingsJson();
        }
        final java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1);
        final String[] result = new String[1];
        main.post(() -> {
            try {
                if (patch.mode != null) {
                    AppPreferences.setDevRemoteAccessMode(appContext, patch.mode);
                }
                if (patch.routeScope != null) {
                    AppPreferences.setDevRemoteAccessVpnRouteScope(appContext, patch.routeScope);
                }
                if (patch.appScope != null) {
                    AppPreferences.setDevRemoteAccessVpnAppScope(appContext, patch.appScope);
                }
                if (patch.mode != null) {
                    RemoteAccessCoordinator.applyMode(appContext, patch.mode);
                } else if (patch.routeScope != null || patch.appScope != null) {
                    RemoteAccessCoordinator.onVpnRoutingPreferenceChanged(appContext);
                }
                result[0] = getSettingsJson();
            } catch (Exception e) {
                result[0] = errorJson("apply failed: " + e.getMessage());
            } finally {
                latch.countDown();
            }
        });
        try {
            latch.await(5_000L, java.util.concurrent.TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return errorJson("apply interrupted");
        }
        return result[0] != null ? result[0] : errorJson("apply timeout");
    }

    @Override
    public String getInternalsJson() {
        try {
            RemoteAccessStatusStore.Snapshot status = RemoteAccessStatusStore.load(appContext);
            RemoteAccessControlClient.EndpointSnapshot control =
                    RemoteAccessControlClient.snapshot(appContext);
            JSONObject out = new JSONObject();
            out.put("ok", true);
            out.put("app_version", BuildConfig.VERSION_NAME);
            out.put("git_commit", BuildConfig.GIT_COMMIT_SHORT);
            out.put("remote_access_mode", AppPreferences.getDevRemoteAccessMode(appContext).name());
            out.put("vpn_state", VpnServiceClient.get(appContext).getStateIfBound());
            out.put("vpn_state_label", vpnStateLabel(VpnServiceClient.get(appContext).getStateIfBound()));
            out.put("vpn_detail", VpnServiceClient.get(appContext).getStateDetailIfBound());
            out.put("rssh_connected", ReverseSshTunnelBridge.isConnected());
            out.put("status", new JSONObject()
                    .put("state", status.state)
                    .put("method", status.method)
                    .put("initiator", status.initiator)
                    .put("fail_reason", status.failReason)
                    .put("detail", status.detail)
                    .put("session_id", status.sessionId)
                    .put("updated_ms", status.updatedMs));
            if (control != null && control.running) {
                out.put("control_http_port", control.port);
                out.put("control_urls", new org.json.JSONArray(control.urls));
            }
            return out.toString();
        } catch (Exception e) {
            Log.w(TAG, "internals failed: " + e.getMessage());
            return errorJson("internals failed: " + e.getMessage());
        }
    }

    private static String vpnStateLabel(int state) {
        if (state == VpnState.UP) {
            return "up";
        }
        if (state == VpnState.CONNECTING) {
            return "connecting";
        }
        if (state == VpnState.ERROR) {
            return "error";
        }
        return "down";
    }

    private static String errorJson(String message) {
        try {
            return RemoteAccessControlPrefsMirror.errorResponse(message).toString();
        } catch (Exception e) {
            return "{\"ok\":false,\"error\":\"error\"}";
        }
    }
}
