package com.foxx.androidcast.vpn;

import android.app.Service;
import android.content.Intent;
import android.net.VpnService;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

import com.foxx.androidcast.IVpnOperationCallback;
import com.foxx.androidcast.IVpnService;
import com.foxx.androidcast.IVpnStateCallback;

/**
 * Standalone VPN process ({@code com.foxx.androidcast:vpn}) — AIDL control plane for WireGuard.
 */
public final class AndroidCastVpnService extends Service {
    private static final String TAG = "AndroidCastVpn";
    /** Debug/lab: record synthetic VPN-process crash (see scripts/simulate-vpn-crashes.sh). */
    public static final String ACTION_SIMULATE_CRASH = "com.foxx.androidcast.vpn.SIMULATE_CRASH";
    public static final String EXTRA_SIM_SEQ = "sim_seq";

    private final RemoteCallbackList<IVpnStateCallback> stateCallbacks = new RemoteCallbackList<>();
    private final Object stateLock = new Object();
    private volatile int state = VpnState.DOWN;
    private volatile String stateDetail = "";

    private HandlerThread workerThread;
    private Handler worker;

    private final IVpnService.Stub binder = new IVpnService.Stub() {
        @Override
        public void registerStateCallback(IVpnStateCallback callback) {
            if (callback != null) {
                stateCallbacks.register(callback);
            }
        }

        @Override
        public void unregisterStateCallback(IVpnStateCallback callback) {
            if (callback != null) {
                stateCallbacks.unregister(callback);
            }
        }

        @Override
        public int getState() {
            return state;
        }

        @Override
        public String getStateDetail() {
            synchronized (stateLock) {
                return stateDetail != null ? stateDetail : "";
            }
        }

        @Override
        public boolean isVpnPermissionGranted() {
            return VpnService.prepare(AndroidCastVpnService.this) == null;
        }

        @Override
        public void applyWireGuardConfig(String wgQuickConfig, IVpnOperationCallback callback) {
            ensureWorker();
            setState(VpnState.CONNECTING, "applying wireguard config");
            worker.post(() -> {
                boolean ok = WireGuardVpnEngine.apply(getApplicationContext(), wgQuickConfig);
                if (ok) {
                    setState(VpnState.UP, "wireguard tunnel up");
                    notifyOperation(callback, true, null);
                } else {
                    setState(VpnState.ERROR, "wireguard apply failed");
                    notifyOperation(callback, false, "apply failed or VPN permission missing");
                }
            });
        }

        @Override
        public void tearDown(IVpnOperationCallback callback) {
            ensureWorker();
            worker.post(() -> {
                WireGuardVpnEngine.tearDown(getApplicationContext());
                setState(VpnState.DOWN, "tunnel down");
                notifyOperation(callback, true, null);
            });
        }

        @Override
        public void simulateCrashForTest(int sequence, IVpnOperationCallback callback) {
            ensureWorker();
            worker.post(() -> {
                boolean ok = VpnCrashSimulator.recordSimulatedCrash(getApplicationContext(), sequence);
                if (ok) {
                    notifyOperation(callback, true, null);
                } else {
                    notifyOperation(callback, false, "simulate only in debug builds");
                }
            });
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "VPN process service created");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null && ACTION_SIMULATE_CRASH.equals(intent.getAction())) {
            int seq = intent.getIntExtra(EXTRA_SIM_SEQ, 0);
            ensureWorker();
            worker.post(() -> {
                VpnCrashSimulator.recordSimulatedCrash(getApplicationContext(), seq);
                stopSelf(startId);
            });
            return START_NOT_STICKY;
        }
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        if (workerThread != null) {
            workerThread.quitSafely();
            workerThread = null;
            worker = null;
        }
        WireGuardVpnEngine.tearDown(getApplicationContext());
        super.onDestroy();
    }

    private void ensureWorker() {
        if (worker != null) {
            return;
        }
        workerThread = new HandlerThread("androidcast-vpn-worker");
        workerThread.start();
        worker = new Handler(workerThread.getLooper());
    }

    private void setState(int newState, String detail) {
        synchronized (stateLock) {
            state = newState;
            stateDetail = detail != null ? detail : "";
        }
        broadcastState(newState, stateDetail);
    }

    private void broadcastState(int newState, String detail) {
        int n = stateCallbacks.beginBroadcast();
        try {
            for (int i = 0; i < n; i++) {
                try {
                    stateCallbacks.getBroadcastItem(i).onStateChanged(newState, detail);
                } catch (RemoteException ignored) {
                }
            }
        } finally {
            stateCallbacks.finishBroadcast();
        }
    }

    private static void notifyOperation(IVpnOperationCallback callback, boolean ok, String reason) {
        if (callback == null) {
            return;
        }
        try {
            if (ok) {
                callback.onSuccess();
            } else {
                callback.onFailure(reason != null ? reason : "error");
            }
        } catch (RemoteException e) {
            Log.w(TAG, "operation callback died: " + e.getMessage());
        }
    }
}
