package club.clubk.ktag.apps.mqttbroker;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.widget.Toast;
import android.util.Log;

import androidx.core.app.NotificationCompat;
import androidx.preference.PreferenceManager;

import club.clubk.ktag.apps.sharedservices.MqttPreferenceKeys;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import io.moquette.broker.Server;
import io.moquette.broker.config.MemoryConfig;
import io.moquette.interception.AbstractInterceptHandler;
import io.moquette.interception.InterceptHandler;
import io.moquette.interception.messages.InterceptConnectMessage;
import io.moquette.interception.messages.InterceptDisconnectMessage;
import io.moquette.interception.messages.InterceptPublishMessage;
import io.moquette.interception.messages.InterceptSubscribeMessage;
import io.moquette.interception.messages.InterceptUnsubscribeMessage;

public class MqttBrokerService extends Service {
    private static final String TAG = "MqttBrokerService";
    private static final String CHANNEL_ID = "MqttBrokerChannel";
    private static final int NOTIFICATION_ID = 1;

    private Server mqttBroker;
    private int port = 1883;
    private int sslPort = 8883;
    private boolean sslEnabled = false;
    private boolean authEnabled = false;
    private boolean nsdEnabled = false;
    private NsdManager nsdManager;
    private NsdManager.RegistrationListener nsdRegistrationListener;

    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
        Log.d(TAG, "Service created");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        stopBroker();
        loadConfiguration();
        startForeground(NOTIFICATION_ID, createNotification("MQTT Broker is starting..."));
        startBroker();
        return START_STICKY;
    }

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

    private void loadConfiguration() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        sslEnabled = prefs.getBoolean(BrokerPreferenceKeys.SSL_ENABLED, false);
        authEnabled = prefs.getBoolean(BrokerPreferenceKeys.AUTH_ENABLED, false);
        nsdEnabled = prefs.getBoolean(BrokerPreferenceKeys.NSD_ENABLED, false);
        port = Integer.parseInt(prefs.getString(BrokerPreferenceKeys.PORT, "1883"));
        sslPort = Integer.parseInt(prefs.getString(BrokerPreferenceKeys.SSL_PORT, "8883"));
    }

    public boolean isBrokerRunning() {
        return mqttBroker != null;
    }

    private void startBroker() {
        MqttBrokerManager.updateStatus(BrokerStatus.Starting.INSTANCE);
        new Thread(() -> {
            try {
                mqttBroker = new Server();

                Properties configProps = new Properties();
                configProps.setProperty("host", "0.0.0.0");
                configProps.setProperty("port", String.valueOf(port));
                configProps.setProperty("allow_anonymous", String.valueOf(!authEnabled));
                configProps.setProperty("allow_zero_byte_client_id", "true");
                configProps.setProperty("reauthorize_subscriptions_on_connect", "false");

                // SSL Configuration
                if (sslEnabled) {
                    if (!SSLCertificateManager.keystoreExists(getApplicationContext())) {
                        Log.d(TAG, "Keystore not found, generating self-signed certificate...");
                        SSLCertificateManager.generateSelfSignedCertificate(getApplicationContext());
                    }

                    configProps.setProperty("ssl_port", String.valueOf(sslPort));
                    configProps.setProperty("jks_path",
                            SSLCertificateManager.getKeystoreFile(getApplicationContext()).getAbsolutePath());
                    configProps.setProperty("key_store_type", "pkcs12");
                    configProps.setProperty("key_store_password", SSLCertificateManager.getKeystorePassword());
                    configProps.setProperty("key_manager_password", SSLCertificateManager.getKeystorePassword());

                    Log.d(TAG, "SSL enabled on port " + sslPort);
                }

                // Persistent data path
                File dataDir = new File(getFilesDir(), "moquette_data");
                if (!dataDir.exists()) {
                    dataDir.mkdirs();
                }
                configProps.setProperty("persistent_store", dataDir.getAbsolutePath());

                MemoryConfig memoryConfig = new MemoryConfig(configProps);

                List<InterceptHandler> handlers = new ArrayList<>();
                handlers.add(new BrokerInterceptor());

                // Ensure KTag autodiscovery credential exists
                ensureKTagCredential();

                if (authEnabled) {
                    FileAuthenticator authenticator = new FileAuthenticator(getApplicationContext());
                    mqttBroker.startServer(memoryConfig, handlers, null, authenticator, null);
                } else {
                    mqttBroker.startServer(memoryConfig, handlers);
                }

                String statusMsg = "Broker running on port " + port;
                if (sslEnabled) {
                    statusMsg += " (SSL: " + sslPort + ")";
                }
                if (authEnabled) {
                    statusMsg += " (Auth)";
                }
                if (nsdEnabled) {
                    statusMsg += " (mDNS)";
                }

                Log.d(TAG, "MQTT Broker started - " + statusMsg);
                updateNotification(statusMsg);
                MqttBrokerManager.updateStatus(new BrokerStatus.Running(port,
                        sslEnabled ? Integer.valueOf(sslPort) : null));

                if (nsdEnabled) {
                    registerNsdService();
                }

            } catch (Exception e) {
                Log.e(TAG, "Failed to start broker", e);
                updateNotification("Failed to start: " + e.getMessage());
                MqttBrokerManager.updateStatus(new BrokerStatus.Error(
                        e.getMessage() != null ? e.getMessage() : "Unknown error"));
            }
        }).start();
    }

    private void ensureKTagCredential() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        String battlefield = prefs.getString(
                MqttPreferenceKeys.BATTLEFIELD,
                MqttPreferenceKeys.DEFAULT_BATTLEFIELD);
        CredentialManager.addCredential(getApplicationContext(), "KTag", battlefield);
        Log.d(TAG, "Ensured KTag credential with battlefield: " + battlefield);
    }

    private void stopBroker() {
        unregisterNsdService();
        if (mqttBroker != null) {
            mqttBroker.stopServer();
            mqttBroker = null;
        }
        MqttBrokerManager.updateStatus(BrokerStatus.Stopped.INSTANCE);
    }

    private void registerNsdService() {
        NsdServiceInfo serviceInfo = new NsdServiceInfo();
        serviceInfo.setServiceName("MQTT Broker - KTag");
        serviceInfo.setServiceType("_mqtt._tcp.");
        serviceInfo.setPort(port);
        serviceInfo.setAttribute("purpose", "KTag MQTT Broker");
        serviceInfo.setAttribute("version", "1");
        serviceInfo.setAttribute("ssl", String.valueOf(sslEnabled));
        serviceInfo.setAttribute("auth", String.valueOf(authEnabled));

        nsdManager = (NsdManager) getSystemService(Context.NSD_SERVICE);
        nsdRegistrationListener = new NsdManager.RegistrationListener() {
            @Override
            public void onServiceRegistered(NsdServiceInfo info) {
                Log.d(TAG, "NSD service registered: " + info.getServiceName());
            }

            @Override
            public void onRegistrationFailed(NsdServiceInfo info, int errorCode) {
                Log.e(TAG, "NSD registration failed: " + errorCode);
            }

            @Override
            public void onServiceUnregistered(NsdServiceInfo info) {
                Log.d(TAG, "NSD service unregistered");
            }

            @Override
            public void onUnregistrationFailed(NsdServiceInfo info, int errorCode) {
                Log.e(TAG, "NSD unregistration failed: " + errorCode);
            }
        };

        nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, nsdRegistrationListener);
    }

    private void unregisterNsdService() {
        if (nsdManager != null && nsdRegistrationListener != null) {
            try {
                nsdManager.unregisterService(nsdRegistrationListener);
            } catch (IllegalArgumentException e) {
                Log.w(TAG, "NSD service already unregistered", e);
            }
            nsdRegistrationListener = null;
            nsdManager = null;
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        new Thread(this::stopBroker).start();
        Log.d(TAG, "Service destroyed");
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "MQTT Broker Service",
                    NotificationManager.IMPORTANCE_LOW
            );
            channel.setDescription("MQTT Broker background service");

            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }
    }

    private Notification createNotification(String contentText) {
        Intent notificationIntent;
        try {
            notificationIntent = new Intent(this,
                    Class.forName("club.clubk.ktag.apps.MainActivity"));
        } catch (ClassNotFoundException e) {
            notificationIntent = new Intent();
        }
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE
        );

        return new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("MQTT Broker")
                .setContentText(contentText)
                .setSmallIcon(android.R.drawable.ic_dialog_info)
                .setContentIntent(pendingIntent)
                .setOngoing(true)
                .build();
    }

    private void updateNotification(String contentText) {
        NotificationManager manager = getSystemService(NotificationManager.class);
        if (manager != null) {
            manager.notify(NOTIFICATION_ID, createNotification(contentText));
        }
    }

    private class BrokerInterceptor extends AbstractInterceptHandler {
        @Override
        public String getID() {
            return "BrokerMonitor";
        }

        @Override
        public void onConnect(InterceptConnectMessage msg) {
            Log.d(TAG, "Client connected: " + msg.getClientID());
            new Handler(Looper.getMainLooper()).post(() ->
                    Toast.makeText(MqttBrokerService.this,
                            "MQTT: " + msg.getClientID() + " connected",
                            Toast.LENGTH_SHORT).show());
        }

        @Override
        public void onDisconnect(InterceptDisconnectMessage msg) {
            Log.d(TAG, "Client disconnected: " + msg.getClientID());
        }

        @Override
        public void onPublish(InterceptPublishMessage msg) {
            Log.d(TAG, "Message published to " + msg.getTopicName() + " by " + msg.getClientID());
        }

        @Override
        public void onSubscribe(InterceptSubscribeMessage msg) {
            Log.d(TAG, msg.getClientID() + " subscribed to " + msg.getTopicFilter());
        }

        @Override
        public void onUnsubscribe(InterceptUnsubscribeMessage msg) {
            Log.d(TAG, msg.getClientID() + " unsubscribed from " + msg.getTopicFilter());
        }

        @Override
        public void onSessionLoopError(Throwable ex) {
            Log.e(TAG, "Session loop error", ex);
        }
    }
}
