/**
 * State reader — hardcoded JSON for Phase 2, WebSocket in Phase 3.
 *
 * Provides Timmy's current state to the scene. In Phase 2 this is a
 * static default; the WebSocket path is stubbed for future use.
 *
 * Also manages connection health monitoring: pings /api/matrix/health
 * every 30 seconds and notifies listeners when online/offline state
 * changes so the Workshop can replay any queued messages.
 */

const DEFAULTS = {
    timmyState: {
        mood: "focused",
        activity: "Pondering the arcane arts",
        energy: 0.6,
        confidence: 0.7,
    },
    activeThreads: [],
    recentEvents: [],
    concerns: [],
    visitorPresent: false,
    updatedAt: new Date().toISOString(),
    version: 1,
};

const _HEALTH_URL = "/api/matrix/health";
const _PING_INTERVAL_MS = 30_000;
const _WS_RECONNECT_DELAY_MS = 5_000;

export class StateReader {
    constructor() {
        this.state = { ...DEFAULTS };
        this.listeners = [];
        this.connectionListeners = [];
        this._ws = null;
        this._online = false;
        this._pingTimer = null;
        this._reconnectTimer = null;
    }

    /** Subscribe to state changes. */
    onChange(fn) {
        this.listeners.push(fn);
    }

    /** Subscribe to online/offline transitions. Called with (isOnline: bool). */
    onConnectionChange(fn) {
        this.connectionListeners.push(fn);
    }

    /** Notify all state listeners. */
    _notify() {
        for (const fn of this.listeners) {
            try {
                fn(this.state);
            } catch (e) {
                console.warn("State listener error:", e);
            }
        }
    }

    /** Fire connection listeners only when state actually changes. */
    _notifyConnection(online) {
        if (online === this._online) return;
        this._online = online;
        for (const fn of this.connectionListeners) {
            try {
                fn(online);
            } catch (e) {
                console.warn("Connection listener error:", e);
            }
        }
    }

    /** Ping the health endpoint once and update connection state. */
    async _ping() {
        try {
            const r = await fetch(_HEALTH_URL, {
                signal: AbortSignal.timeout(5000),
            });
            this._notifyConnection(r.ok);
        } catch {
            this._notifyConnection(false);
        }
    }

    /** Start 30-second health-check loop (idempotent). */
    _startHealthCheck() {
        if (this._pingTimer) return;
        this._pingTimer = setInterval(() => this._ping(), _PING_INTERVAL_MS);
    }

    /** Schedule a WebSocket reconnect attempt after a delay (idempotent). */
    _scheduleReconnect() {
        if (this._reconnectTimer) return;
        this._reconnectTimer = setTimeout(() => {
            this._reconnectTimer = null;
            this._connectWS();
        }, _WS_RECONNECT_DELAY_MS);
    }

    /** Open (or re-open) the WebSocket connection. */
    _connectWS() {
        const proto = location.protocol === "https:" ? "wss:" : "ws:";
        const url = `${proto}//${location.host}/api/world/ws`;
        try {
            this._ws = new WebSocket(url);
            this._ws.onopen = () => {
                const dot = document.getElementById("connection-dot");
                if (dot) dot.classList.add("connected");
                this._notifyConnection(true);
            };
            this._ws.onclose = () => {
                const dot = document.getElementById("connection-dot");
                if (dot) dot.classList.remove("connected");
                this._notifyConnection(false);
                this._scheduleReconnect();
            };
            this._ws.onmessage = (ev) => {
                try {
                    const msg = JSON.parse(ev.data);
                    if (msg.type === "world_state" || msg.type === "timmy_state") {
                        if (msg.timmyState) this.state.timmyState = msg.timmyState;
                        if (msg.mood) {
                            this.state.timmyState.mood = msg.mood;
                            this.state.timmyState.activity = msg.activity || "";
                            this.state.timmyState.energy = msg.energy ?? 0.5;
                        }
                        this._notify();
                    }
                } catch (e) {
                    /* ignore parse errors */
                }
            };
        } catch (e) {
            console.warn("WebSocket unavailable — using static state");
            this._scheduleReconnect();
        }
    }

    /** Connect to the world WebSocket and start health-check polling. */
    connect() {
        this._connectWS();
        this._startHealthCheck();
        // Immediate ping so connection status is known before the first interval.
        this._ping();
    }

    /** Current mood string. */
    get mood() {
        return this.state.timmyState.mood;
    }

    /** Current activity string. */
    get activity() {
        return this.state.timmyState.activity;
    }

    /** Energy level 0-1. */
    get energy() {
        return this.state.timmyState.energy;
    }

    /** Whether the server is currently reachable. */
    get isOnline() {
        return this._online;
    }
}
