package com.foxx.androidcast.dev;

import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Build;
import android.util.Log;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/** Discover local wireless-adb ports via mDNS (_adb-tls-connect._tcp, legacy _adb._tcp). */
final class DevAdbMdnsProbe {
    private static final String[] SERVICE_TYPES = {
            "_adb-tls-connect._tcp",
            "_adb._tcp",
            "_adb-tls-pairing._tcp",
    };

    private DevAdbMdnsProbe() {}

    static List<Integer> discoverPorts(Context context, long timeoutMs) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            return new ArrayList<>();
        }
        NsdManager nsd = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
        if (nsd == null) {
            return new ArrayList<>();
        }
        Set<Integer> ports = new LinkedHashSet<>();
        Set<String> localIps = new LinkedHashSet<>(DevAdbWifiHelper.collectWifiIpv4Addresses(context));
        CountDownLatch done = new CountDownLatch(1);
        AtomicBoolean stopped = new AtomicBoolean(false);

        NsdManager.DiscoveryListener listener = new NsdManager.DiscoveryListener() {
            @Override
            public void onDiscoveryStarted(String serviceType) {
                Log.d(DevAdbConnectInfo.LOG_TAG, "mDNS discovery started: " + serviceType);
            }

            @Override
            public void onServiceFound(NsdServiceInfo serviceInfo) {
                nsd.resolveService(serviceInfo, new NsdManager.ResolveListener() {
                    @Override
                    public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
                        Log.d(DevAdbConnectInfo.LOG_TAG,
                                "mDNS resolve failed code=" + errorCode);
                    }

                    @Override
                    public void onServiceResolved(NsdServiceInfo info) {
                        int port = info.getPort();
                        if (port <= 0 || port >= 65536) {
                            return;
                        }
                        String host = info.getHost() != null ? info.getHost().getHostAddress() : "";
                        if (!localIps.isEmpty()
                                && host != null
                                && !host.isEmpty()
                                && !localIps.contains(host)) {
                            return;
                        }
                        ports.add(port);
                        Log.i(DevAdbConnectInfo.LOG_TAG,
                                "mDNS adb port " + port + " type=" + info.getServiceType());
                    }
                });
            }

            @Override
            public void onServiceLost(NsdServiceInfo serviceInfo) {}

            @Override
            public void onDiscoveryStopped(String serviceType) {
                if (stopped.compareAndSet(false, true)) {
                    done.countDown();
                }
            }

            @Override
            public void onStartDiscoveryFailed(String serviceType, int errorCode) {
                Log.d(DevAdbConnectInfo.LOG_TAG,
                        "mDNS start failed type=" + serviceType + " code=" + errorCode);
                if (stopped.compareAndSet(false, true)) {
                    done.countDown();
                }
            }

            @Override
            public void onStopDiscoveryFailed(String serviceType, int errorCode) {
                if (stopped.compareAndSet(false, true)) {
                    done.countDown();
                }
            }
        };

        try {
            nsd.discoverServices(SERVICE_TYPES[0], NsdManager.PROTOCOL_DNS_SD, listener);
            if (!done.await(timeoutMs, TimeUnit.MILLISECONDS)) {
                Log.d(DevAdbConnectInfo.LOG_TAG, "mDNS discovery timed out");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            try {
                nsd.stopServiceDiscovery(listener);
            } catch (Exception ignored) {
            }
        }
        return new ArrayList<>(ports);
    }
}
