package com.change.freevpn.vpn;

import android.content.Context;
import android.content.Intent;
import android.net.VpnService;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

/**
 * Simple VPN Service for OpenVPN connections
 * This implements a basic VPN tunnel using Android's VpnService API
 */
public class OpenVpnService extends VpnService {
    private static final String TAG = "OpenVpnService";
    private static final int VPN_MTU = 1500;
    
    private Thread vpnThread;
    private ParcelFileDescriptor vpnInterface;
    private String serverConfig;
    private volatile boolean isRunning = false;
    
    private static ConnectionCallback callback;
    
    public interface ConnectionCallback {
        void onConnected();
        void onDisconnected();
        void onError(String error);
    }
    
    public static void setCallback(ConnectionCallback cb) {
        callback = cb;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null && intent.hasExtra("config")) {
            serverConfig = intent.getStringExtra("config");
            startVpn();
        }
        return START_STICKY;
    }
    
    private void startVpn() {
        if (isRunning) {
            Log.w(TAG, "VPN already running");
            return;
        }
        
        isRunning = true;
        vpnThread = new Thread(() -> {
            try {
                runVpnConnection();
            } catch (Exception e) {
                Log.e(TAG, "VPN connection error", e);
                notifyError("Connection failed: " + e.getMessage());
                stopVpn();
            }
        });
        vpnThread.start();
    }
    
    private void runVpnConnection() throws Exception {
        Log.d(TAG, "Parsing config length: " + (serverConfig != null ? serverConfig.length() : 0));
        
        // Parse basic OpenVPN config
        String serverAddress = parseServerAddress(serverConfig);
        int serverPort = parseServerPort(serverConfig);
        
        Log.d(TAG, "Parsed Server: " + serverAddress + ":" + serverPort);
        
        if (serverAddress == null || serverAddress.isEmpty()) {
            throw new Exception("Invalid server configuration: Address not found in config");
        }
        
        // Configure VPN interface
        Builder builder = new Builder();
        builder.setMtu(VPN_MTU);
        builder.addAddress("10.8.0.2", 24); // Virtual IP
        builder.addRoute("0.0.0.0", 0); // Route all traffic
        builder.addDnsServer("8.8.8.8");
        builder.addDnsServer("8.8.4.4");
        builder.setSession("Free VPN");
        
        vpnInterface = builder.establish();
        
        if (vpnInterface == null) {
            throw new Exception("Failed to establish VPN interface");
        }
        
        notifyConnected();
        
        // Keep connection alive
        protect(vpnInterface.getFd());
        
        // Simple packet forwarding loop
        ByteBuffer packet = ByteBuffer.allocate(VPN_MTU);
        
        while (isRunning && !Thread.interrupted()) {
            try {
                // Read packets from VPN interface
                int length = new java.io.FileInputStream(vpnInterface.getFileDescriptor()).read(packet.array());
                
                if (length > 0) {
                    // In a real implementation, encrypt and send to VPN server
                    // For now, we'll just keep the tunnel open
                }
                
                Thread.sleep(100);
            } catch (IOException | InterruptedException e) {
                if (isRunning) {
                    Log.e(TAG, "Packet processing error", e);
                }
                break;
            }
        }
    }
    
    private String parseServerAddress(String config) {
        try {
            BufferedReader reader = new BufferedReader(new java.io.StringReader(config));
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                // Match "remote IP PORT" or "remote IP"
                if (line.startsWith("remote")) {
                    String[] parts = line.split("\\s+");
                    if (parts.length >= 2) {
                        return parts[1];
                    }
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Error parsing server address", e);
        }
        return null;
    }
    
    private int parseServerPort(String config) {
        try {
            BufferedReader reader = new BufferedReader(new java.io.StringReader(config));
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.startsWith("remote")) {
                    String[] parts = line.split("\\s+");
                    if (parts.length >= 3) {
                        return Integer.parseInt(parts[2]);
                    }
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Error parsing server port", e);
        }
        return 1194; // Default
    }
    
    private void stopVpn() {
        isRunning = false;
        
        if (vpnThread != null) {
            vpnThread.interrupt();
            vpnThread = null;
        }
        
        if (vpnInterface != null) {
            try {
                vpnInterface.close();
            } catch (IOException e) {
                Log.e(TAG, "Error closing VPN interface", e);
            }
            vpnInterface = null;
        }
        
        notifyDisconnected();
        stopSelf();
    }
    
    @Override
    public void onDestroy() {
        stopVpn();
        super.onDestroy();
    }
    
    private void notifyConnected() {
        new Handler(Looper.getMainLooper()).post(() -> {
            if (callback != null) {
                callback.onConnected();
            }
        });
    }
    
    private void notifyDisconnected() {
        new Handler(Looper.getMainLooper()).post(() -> {
            if (callback != null) {
                callback.onDisconnected();
            }
        });
    }
    
    private void notifyError(String error) {
        new Handler(Looper.getMainLooper()).post(() -> {
            if (callback != null) {
                callback.onError(error);
            }
        });
    }
}
