package com.foxx.androidcast.dev;

import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** Re-enable adb / wireless debugging and collect adb connect endpoints (debug builds). */
public final class DevAdbWifiHelper {
    private static final String GLOBAL_ADB_WIFI_ENABLED = "adb_wifi_enabled";
    private static final Pattern ENDPOINT_PATTERN =
            Pattern.compile("(\\d{1,3}(?:\\.\\d{1,3}){3}):(\\d{2,5})");

    private DevAdbWifiHelper() {}

    public static DevAdbConnectInfo refresh(Context context) {
        Context app = context.getApplicationContext();
        boolean secure = hasWriteSecureSettings(app);
        boolean adbEnabled = isGlobalFlagOn(app, Settings.Global.ADB_ENABLED);
        boolean adbWifiEnabled = Build.VERSION.SDK_INT < 30
                || isGlobalFlagOn(app, GLOBAL_ADB_WIFI_ENABLED);

        if (secure) {
            enableDeveloperAdb(app);
            adbEnabled = isGlobalFlagOn(app, Settings.Global.ADB_ENABLED);
            adbWifiEnabled = Build.VERSION.SDK_INT < 30
                    || isGlobalFlagOn(app, GLOBAL_ADB_WIFI_ENABLED);
        } else {
            Log.w(DevAdbConnectInfo.LOG_TAG,
                    "WRITE_SECURE_SETTINGS not granted — cannot auto re-enable adb wifi");
        }

        List<String> ips = collectWifiIpv4Addresses(app);
        List<Integer> ports = discoverPorts(app);
        ports.addAll(DevAdbMdnsProbe.discoverPorts(app, 2500L));
        List<String> connect = buildConnectEndpoints(ips, ports);
        connect.addAll(parseDumpsysEndpoints(runDumpsysAdb(app)));

        List<String> dedupedConnect = orderConnectEndpoints(dedupe(connect), ports);
        long now = System.currentTimeMillis();
        return new DevAdbConnectInfo(
                ips,
                dedupedConnect,
                adbEnabled,
                adbWifiEnabled,
                secure,
                DevAdbConnectInfo.LEGACY_TCP_PORT,
                now);
    }

    private static void enableDeveloperAdb(Context context) {
        try {
            Settings.Global.putInt(
                    context.getContentResolver(),
                    Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
                    1);
            if (Build.VERSION.SDK_INT >= 30) {
                Settings.Global.putInt(context.getContentResolver(), GLOBAL_ADB_WIFI_ENABLED, 1);
            }
            // Toggle USB debugging on to kick adbd after Wi-Fi drops.
            Settings.Global.putInt(context.getContentResolver(), Settings.Global.ADB_ENABLED, 0);
            Settings.Global.putInt(context.getContentResolver(), Settings.Global.ADB_ENABLED, 1);
            if (Build.VERSION.SDK_INT >= 30) {
                Settings.Global.putInt(context.getContentResolver(), GLOBAL_ADB_WIFI_ENABLED, 1);
            }
        } catch (SecurityException e) {
            Log.w(DevAdbConnectInfo.LOG_TAG, "enableDeveloperAdb denied", e);
        }
    }

    public static boolean hasWriteSecureSettings(Context context) {
        return context.checkSelfPermission("android.permission.WRITE_SECURE_SETTINGS")
                == PackageManager.PERMISSION_GRANTED;
    }

    private static boolean isGlobalFlagOn(Context context, String key) {
        try {
            return Settings.Global.getInt(context.getContentResolver(), key, 0) == 1;
        } catch (Exception e) {
            return false;
        }
    }

    static List<String> collectWifiIpv4Addresses(Context context) {
        Set<String> ips = new LinkedHashSet<>();
        ConnectivityManager cm =
                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) {
            return new ArrayList<>();
        }
        for (Network network : cm.getAllNetworks()) {
            NetworkCapabilities caps = cm.getNetworkCapabilities(network);
            if (caps == null || !caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                continue;
            }
            LinkProperties props = cm.getLinkProperties(network);
            if (props == null) {
                continue;
            }
            for (LinkAddress link : props.getLinkAddresses()) {
                InetAddress address = link.getAddress();
                if (address instanceof Inet4Address && !address.isLoopbackAddress()) {
                    ips.add(address.getHostAddress());
                }
            }
        }
        return new ArrayList<>(ips);
    }

    private static List<Integer> discoverPorts(Context context) {
        Set<Integer> ports = new LinkedHashSet<>();

        int tlsProp = parsePositiveInt(readSystemProperty("service.adb.tls.port"));
        if (tlsProp > 0) {
            ports.add(tlsProp);
        }
        int tcpProp = parsePositiveInt(readSystemProperty("service.adb.tcp.port"));
        if (tcpProp > 0) {
            ports.add(tcpProp);
        }
        int persistTls = parsePositiveInt(readSystemProperty("persist.adb.tls_server.port"));
        if (persistTls > 0) {
            ports.add(persistTls);
        }

        for (String key : new String[] {"adb_wifi_port", "adb_wifi_tls_port", "adb_port"}) {
            int fromSettings = readGlobalInt(context, key, -1);
            if (fromSettings > 0) {
                ports.add(fromSettings);
            }
            String asString = readGlobalString(context, key);
            int parsed = parsePositiveInt(asString);
            if (parsed > 0) {
                ports.add(parsed);
            }
        }
        if (ports.isEmpty()) {
            ports.add(DevAdbConnectInfo.LEGACY_TCP_PORT);
        }
        return new ArrayList<>(ports);
    }

    private static List<String> buildConnectEndpoints(List<String> ips, List<Integer> ports) {
        List<String> endpoints = DevAdbConnectInfo.mergeEndpoints(ips, ports);
        if (endpoints.isEmpty() && !ips.isEmpty()) {
            endpoints.add(ips.get(0) + ":" + DevAdbConnectInfo.LEGACY_TCP_PORT);
        }
        return endpoints;
    }

    /** Prefer TLS / mDNS ports over legacy :5555 guesses. */
    private static List<String> orderConnectEndpoints(List<String> endpoints, List<Integer> ports) {
        boolean hasNonLegacy = false;
        for (Integer port : ports) {
            if (port != null && port != DevAdbConnectInfo.LEGACY_TCP_PORT) {
                hasNonLegacy = true;
                break;
            }
        }
        if (!hasNonLegacy) {
            return endpoints;
        }
        List<String> preferred = new ArrayList<>();
        List<String> legacy = new ArrayList<>();
        for (String endpoint : endpoints) {
            if (endpoint.endsWith(":" + DevAdbConnectInfo.LEGACY_TCP_PORT)) {
                legacy.add(endpoint);
            } else {
                preferred.add(endpoint);
            }
        }
        preferred.addAll(legacy);
        return preferred;
    }

    private static String readSystemProperty(String key) {
        try {
            Class<?> clazz = Class.forName("android.os.SystemProperties");
            Method get = clazz.getMethod("get", String.class, String.class);
            Object value = get.invoke(null, key, "");
            return value != null ? value.toString() : "";
        } catch (Exception ignored) {
            return "";
        }
    }

    private static int readGlobalInt(Context context, String key, int def) {
        try {
            return Settings.Global.getInt(context.getContentResolver(), key, def);
        } catch (Exception e) {
            return def;
        }
    }

    private static String readGlobalString(Context context, String key) {
        try {
            String value = Settings.Global.getString(context.getContentResolver(), key);
            return value != null ? value : "";
        } catch (Exception e) {
            return "";
        }
    }

    private static int parsePositiveInt(String raw) {
        if (TextUtils.isEmpty(raw)) {
            return -1;
        }
        try {
            int value = Integer.parseInt(raw.trim());
            return value > 0 ? value : -1;
        } catch (NumberFormatException e) {
            return -1;
        }
    }

    private static String runDumpsysAdb(Context context) {
        if (context.checkSelfPermission("android.permission.DUMP")
                != PackageManager.PERMISSION_GRANTED) {
            return "";
        }
        Process process = null;
        try {
            process = new ProcessBuilder("dumpsys", "adb")
                    .redirectErrorStream(true)
                    .start();
            if (!process.waitFor(3, TimeUnit.SECONDS)) {
                process.destroyForcibly();
                return "";
            }
            StringBuilder out = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    out.append(line).append('\n');
                }
            }
            return out.toString();
        } catch (Exception e) {
            Log.d(DevAdbConnectInfo.LOG_TAG, "dumpsys adb unavailable", e);
            return "";
        } finally {
            if (process != null) {
                process.destroy();
            }
        }
    }

    static List<String> parseDumpsysEndpoints(String dumpsys) {
        Set<String> endpoints = new LinkedHashSet<>();
        if (TextUtils.isEmpty(dumpsys)) {
            return new ArrayList<>();
        }
        Matcher matcher = ENDPOINT_PATTERN.matcher(dumpsys);
        while (matcher.find()) {
            endpoints.add(matcher.group(1) + ":" + matcher.group(2));
        }
        return new ArrayList<>(endpoints);
    }

    private static List<String> dedupe(List<String> values) {
        Set<String> out = new LinkedHashSet<>();
        for (String value : values) {
            if (!TextUtils.isEmpty(value)) {
                out.add(value.trim());
            }
        }
        return new ArrayList<>(out);
    }
}
