package vassistent.model;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
 * Observable in-memory state shared between services and UI controllers.
 */
public class AppState {

    private final AtomicLong dataVersion = new AtomicLong(0);
    private final AtomicBoolean mqttConnected = new AtomicBoolean(false);
    private final AtomicReference<ProblemLevel> problemLevel =
            new AtomicReference<>(ProblemLevel.NONE);

    private final List<Consumer<AppState>> listeners =
            new CopyOnWriteArrayList<>();

    /**
     * Registers a state listener that is notified on relevant updates.
     *
     * @param listener callback receiving the current state instance
     */
    public void addListener(Consumer<AppState> listener) {
        listeners.add(
                Objects.requireNonNull(
                        listener,
                        "listener must not be null"
                )
        );
    }

    /**
     * Notifies all currently registered listeners.
     */
    private void notifyListeners() {
        listeners.forEach(listener -> listener.accept(this));
    }

    /**
     * Returns the current computed problem level.
     *
     * @return current problem level
     */
    public ProblemLevel getProblemLevel() {
        return problemLevel.get();
    }

    /**
     * Updates the problem level and notifies listeners.
     *
     * @param problemLevel new problem level
     */
    public void setProblemLevel(ProblemLevel problemLevel) {
        this.problemLevel.set(
                Objects.requireNonNull(
                        problemLevel,
                        "problemLevel must not be null"
                )
        );

        dataVersion.incrementAndGet();
        notifyListeners();
    }

    /**
     * Indicates whether MQTT is currently connected.
     *
     * @return {@code true} if connected, otherwise {@code false}
     */
    public boolean isMqttConnected() {
        return mqttConnected.get();
    }

    /**
     * Updates MQTT connection status and notifies listeners only when changed.
     *
     * @param mqttConnected new MQTT connection flag
     */
    public void setMqttConnected(boolean mqttConnected) {
        boolean previous =
                this.mqttConnected.getAndSet(mqttConnected);

        if (previous != mqttConnected) {
            dataVersion.incrementAndGet();
            notifyListeners();
        }
    }
}
