package com.foxx.androidcast.ota;

/*********************************************************************
 * OtaMqttFetch.java
 * Created at: Wed 20 May 2026 14:31:55 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (2 commits, 120 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	83b61695b2344fb831a826cb0ef9687578aab90231f5c08416d1806462b09ec6
**********************************************************************/
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import java.net.URI;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/** Fetches retained payloads from MQTT topics addressed by mqtt:// or mqtts:// URLs. */
final class OtaMqttFetch {
    private static final int DEFAULT_MQTT_PORT = 1883;
    private static final int DEFAULT_MQTTS_PORT = 8883;

    private OtaMqttFetch() {}

    static byte[] getPayload(String mqttUrl, int timeoutMs) throws Exception {
        URI uri = URI.create(mqttUrl);
        String scheme = uri.getScheme() == null ? "" : uri.getScheme().toLowerCase(Locale.US);
        boolean tls;
        if ("mqtt".equals(scheme)) {
            tls = false;
        } else if ("mqtts".equals(scheme)) {
            tls = true;
        } else {
            throw new IllegalStateException("Unsupported MQTT scheme: " + scheme);
        }
        String host = uri.getHost();
        if (host == null || host.isEmpty()) {
            throw new IllegalStateException("MQTT URL missing host");
        }
        int port = uri.getPort();
        if (port <= 0) {
            port = tls ? DEFAULT_MQTTS_PORT : DEFAULT_MQTT_PORT;
        }
        String topic = uri.getPath();
        if (topic == null || topic.isEmpty() || "/".equals(topic)) {
            throw new IllegalStateException("MQTT URL missing topic path");
        }
        if (topic.startsWith("/")) {
            topic = topic.substring(1);
        }
        String brokerUri = (tls ? "ssl" : "tcp") + "://" + host + ":" + port;

        AtomicReference<byte[]> payload = new AtomicReference<>();
        AtomicReference<Throwable> error = new AtomicReference<>();
        CountDownLatch latch = new CountDownLatch(1);

        String clientId = "androidcast-ota-" + UUID.randomUUID().toString().replace("-", "");
        MqttClient client = new MqttClient(brokerUri, clientId, new MemoryPersistence());
        try {
            MqttConnectOptions opts = new MqttConnectOptions();
            opts.setAutomaticReconnect(false);
            opts.setCleanSession(true);
            opts.setConnectionTimeout(Math.max(3, timeoutMs / 1000));
            opts.setKeepAliveInterval(20);
            client.setCallback(new MqttCallback() {
                @Override
                public void connectionLost(Throwable cause) {
                    error.compareAndSet(null, cause);
                    latch.countDown();
                }

                @Override
                public void messageArrived(String topic, MqttMessage message) {
                    payload.compareAndSet(null, message.getPayload());
                    latch.countDown();
                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken token) {
                    // not used for OTA fetch
                }
            });
            client.connect(opts);
            client.subscribe(topic, 0);
            boolean got = latch.await(Math.max(1, timeoutMs), TimeUnit.MILLISECONDS);
            client.unsubscribe(topic);
            if (!got) {
                throw new IllegalStateException("MQTT timeout waiting for topic: " + topic);
            }
            if (error.get() != null) {
                Throwable t = error.get();
                throw new IllegalStateException(t.getMessage() != null ? t.getMessage() : "MQTT error", t);
            }
            byte[] body = payload.get();
            if (body == null || body.length == 0) {
                throw new IllegalStateException("MQTT topic has empty payload: " + topic);
            }
            return body;
        } catch (MqttException e) {
            throw new IllegalStateException(e.getMessage() != null ? e.getMessage() : "MQTT error", e);
        } finally {
            try {
                if (client.isConnected()) {
                    client.disconnect();
                }
            } catch (Exception ignored) {
            }
            try {
                client.close();
            } catch (Exception ignored) {
            }
        }
    }
}
