package com.foxx.androidcast.dev;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.core.app.NotificationCompat;

import com.foxx.androidcast.AppPreferences;
import com.foxx.androidcast.BuildConfig;
import com.foxx.androidcast.DeveloperSettingsActivity;
import com.foxx.androidcast.R;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** Debug-only: re-enable adb wifi on network changes and publish connect info. */
public final class DevAdbWifiKeeper {
    private static final String CHANNEL_ID = "dev_adb_wifi";
    private static final int NOTIFICATION_ID = 50391;

    private static final ExecutorService WORKER = Executors.newSingleThreadExecutor(r -> {
        Thread t = new Thread(r, "DevAdbWifiKeeper");
        t.setDaemon(true);
        return t;
    });
    private static final Handler MAIN = new Handler(Looper.getMainLooper());

    private static volatile DevAdbWifiKeeper instance;

    private final Context appContext;
    private final DevAdbConnectHttpServer httpServer = new DevAdbConnectHttpServer();
    private final ConnectivityManager.NetworkCallback networkCallback =
            new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    scheduleRefresh("network-available");
                }

                @Override
                public void onCapabilitiesChanged(Network network, NetworkCapabilities caps) {
                    if (caps != null && caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                        scheduleRefresh("network-validated");
                    }
                }

                @Override
                public void onLost(Network network) {
                    scheduleRefresh("network-lost");
                }
            };

    private DevAdbConnectInfo lastInfo = new DevAdbConnectInfo(
            null, null, false, false, false, DevAdbConnectInfo.LEGACY_TCP_PORT, 0L);
    private boolean registered;

    private DevAdbWifiKeeper(Context context) {
        appContext = context.getApplicationContext();
    }

    public static void ensureStarted(Context context) {
        if (!BuildConfig.DEBUG) {
            return;
        }
        if (!AppPreferences.isDevAdbWifiKeeperEnabled(context)) {
            stop(context);
            return;
        }
        if (instance == null) {
            synchronized (DevAdbWifiKeeper.class) {
                if (instance == null) {
                    instance = new DevAdbWifiKeeper(context);
                }
            }
        }
        instance.start();
    }

    public static void stop(Context context) {
        DevAdbWifiKeeper keeper = instance;
        if (keeper != null) {
            keeper.shutdown();
        }
        instance = null;
    }

    public static void onPreferenceChanged(Context context, boolean enabled) {
        if (!BuildConfig.DEBUG) {
            return;
        }
        if (enabled) {
            ensureStarted(context);
            refreshNow(context, "preference-enabled");
        } else {
            stop(context);
        }
    }

    public static void refreshNow(Context context, String reason) {
        if (!BuildConfig.DEBUG) {
            return;
        }
        ensureStarted(context);
        DevAdbWifiKeeper keeper = instance;
        if (keeper != null) {
            keeper.scheduleRefresh(reason);
        }
    }

    public static DevAdbConnectInfo getLastInfo(Context context) {
        DevAdbWifiKeeper keeper = instance;
        if (keeper != null) {
            return keeper.lastInfo;
        }
        return DevAdbWifiHelper.refresh(context);
    }

    public static void copyPrimaryConnectLine(Context context) {
        DevAdbConnectInfo info = getLastInfo(context);
        String line = info.primaryConnectLine();
        if (line.isEmpty()) {
            return;
        }
        ClipboardManager clipboard =
                (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
        if (clipboard != null) {
            clipboard.setPrimaryClip(ClipData.newPlainText("adb connect", "adb connect " + line));
        }
    }

    private void start() {
        ensureNotificationChannel();
        loadCachedJsonIntoHttpServer();
        httpServer.start();
        registerNetworkCallback();
        scheduleRefresh("keeper-start");
    }

    private void loadCachedJsonIntoHttpServer() {
        File cached = new File(appContext.getFilesDir(), "dev/adb_connect.json");
        if (!cached.isFile()) {
            return;
        }
        try {
            byte[] bytes = java.nio.file.Files.readAllBytes(cached.toPath());
            String json = new String(bytes, StandardCharsets.UTF_8).trim();
            if (!json.isEmpty()) {
                httpServer.updatePayload(json);
            }
        } catch (IOException e) {
            Log.d(DevAdbConnectInfo.LOG_TAG, "no cached adb_connect.json", e);
        }
    }

    private void shutdown() {
        unregisterNetworkCallback();
        httpServer.stop();
        NotificationManager nm =
                (NotificationManager) appContext.getSystemService(Context.NOTIFICATION_SERVICE);
        if (nm != null) {
            nm.cancel(NOTIFICATION_ID);
        }
    }

    private void registerNetworkCallback() {
        if (registered) {
            return;
        }
        ConnectivityManager cm =
                (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) {
            return;
        }
        NetworkRequest request = new NetworkRequest.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .build();
        cm.registerNetworkCallback(request, networkCallback);
        registered = true;
    }

    private void unregisterNetworkCallback() {
        if (!registered) {
            return;
        }
        ConnectivityManager cm =
                (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm != null) {
            try {
                cm.unregisterNetworkCallback(networkCallback);
            } catch (Exception ignored) {
            }
        }
        registered = false;
    }

    private void scheduleRefresh(String reason) {
        WORKER.execute(() -> performRefresh(reason));
    }

    private void performRefresh(String reason) {
        DevAdbConnectInfo info = DevAdbWifiHelper.refresh(appContext);
        lastInfo = info;
        String json = info.toJson();
        httpServer.updatePayload(json);
        writeJsonFile(json);
        Log.i(DevAdbConnectInfo.LOG_TAG, info.toLogLine());
        Log.d(DevAdbConnectInfo.LOG_TAG, "refresh reason=" + reason
                + " secure=" + info.secureSettingsGranted
                + " connect=" + info.connect);
        MAIN.post(() -> updateNotification(info));
    }

    private void writeJsonFile(String json) {
        File dir = new File(appContext.getFilesDir(), "dev");
        if (!dir.exists() && !dir.mkdirs()) {
            return;
        }
        File out = new File(dir, "adb_connect.json");
        try (FileOutputStream fos = new FileOutputStream(out, false)) {
            fos.write(json.getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            Log.w(DevAdbConnectInfo.LOG_TAG, "write adb_connect.json failed", e);
        }
    }

    private void ensureNotificationChannel() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            return;
        }
        NotificationManager nm =
                (NotificationManager) appContext.getSystemService(Context.NOTIFICATION_SERVICE);
        if (nm == null) {
            return;
        }
        NotificationChannel channel = new NotificationChannel(
                CHANNEL_ID,
                appContext.getString(R.string.dev_adb_wifi_notification_channel),
                NotificationManager.IMPORTANCE_LOW);
        channel.setDescription(appContext.getString(R.string.dev_adb_wifi_notification_channel_desc));
        nm.createNotificationChannel(channel);
    }

    private void updateNotification(DevAdbConnectInfo info) {
        if (!AppPreferences.isDevAdbWifiKeeperEnabled(appContext)) {
            return;
        }
        NotificationManager nm =
                (NotificationManager) appContext.getSystemService(Context.NOTIFICATION_SERVICE);
        if (nm == null) {
            return;
        }
        String line = info.primaryConnectLine();
        String text = line.isEmpty()
                ? appContext.getString(R.string.dev_adb_wifi_notification_waiting)
                : appContext.getString(R.string.dev_adb_wifi_notification_line, line);
        Intent openDev = new Intent(appContext, DeveloperSettingsActivity.class);
        openDev.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent content = PendingIntent.getActivity(
                appContext,
                0,
                openDev,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        Notification notification = new NotificationCompat.Builder(appContext, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentTitle(appContext.getString(R.string.dev_adb_wifi_notification_title))
                .setContentText(text)
                .setStyle(new NotificationCompat.BigTextStyle().bigText(text))
                .setContentIntent(content)
                .setOngoing(true)
                .setOnlyAlertOnce(true)
                .build();
        nm.notify(NOTIFICATION_ID, notification);
    }
}
