"""High-level examples for OmniLink agent messaging and state snapshots."""

from __future__ import annotations

import json
import os
import signal
import time
from typing import Any, Dict, Optional

from omnilink import (
    AgentFeedback,
    AgentQuestion,
    OmniLinkEngine,
    OmniLinkMQTTBridge,
    RemoteCommandClient,
)


def build_question_engine() -> OmniLinkEngine:
    """Return an engine whose handler asks for operator confirmation."""

    engine = OmniLinkEngine(["confirm_[item:any]"])

    def _confirm_handler(evt: Dict[str, Any]) -> Dict[str, Any]:
        messenger = evt.get("messenger")
        if messenger is None:
            raise RuntimeError(
                "This example requires a messenger-aware bridge such as OmniLinkMQTTBridge."
            )

        item = str(evt["vars"]["item"])
        messenger.acknowledge("pending", message=f"awaiting approval for {item}")
        pending = messenger.ask_question(
            AgentQuestion(
                prompt=f"Approve action for {item}?",
                choices=["yes", "no"],
                data={"item": item},
            )
        )

        try:
            reply = pending.wait(timeout=30, cancel_on_timeout=True)
        except TimeoutError:
            messenger.send_feedback(
                AgentFeedback(
                    message=f"Timed out waiting for {item} approval",
                    kind="error",
                    ok=False,
                )
            )
            raise

        messenger.send_feedback(
            AgentFeedback(
                message=f"Operator response: {reply.get('answer', reply)}",
                kind="success",
                data=reply,
            )
        )
        return {"ack": True, "reply": reply}

    engine.on_template("confirm_[item:any]", _confirm_handler)
    return engine


def start_mqtt_question_demo(item: str = "sample") -> OmniLinkMQTTBridge:
    """Spin up an MQTT bridge that will ask for confirmation when triggered."""

    engine = build_question_engine()
    transport_env = os.environ.get("MQTT_TRANSPORT")
    bridge_kwargs = {}
    if not transport_env:
        bridge_kwargs["transport"] = "tcp"
    bridge = OmniLinkMQTTBridge(engine, **bridge_kwargs)
    bridge.start()
    print(
        f"Publish the command 'confirm {item}' to"
        f" {bridge.command_topic!r} to see the question/answer flow."
    )
    return bridge


def publish_engine_state(bridge: OmniLinkMQTTBridge, history_limit: int = 5) -> Dict[str, Any]:
    """Publish a compact state snapshot over the bridge's state topic."""

    snapshot = bridge.publish_state_snapshot(history_limit=history_limit)
    print(json.dumps(snapshot, indent=2, sort_keys=True))
    return snapshot


def fetch_remote_state_snapshot(
    client: Optional[RemoteCommandClient] = None,
) -> Optional[Dict[str, Any]]:
    """Fetch the latest remote snapshot via Supabase."""

    client = client or RemoteCommandClient()
    snapshot = client.fetch_state_snapshot()
    print(json.dumps(snapshot, indent=2, sort_keys=True) if snapshot else "<no snapshot>")
    return snapshot


__all__ = [
    "build_question_engine",
    "start_mqtt_question_demo",
    "publish_engine_state",
    "fetch_remote_state_snapshot",
]


def _stop_bridge(bridge: OmniLinkMQTTBridge) -> None:
    """Attempt to stop the MQTT loop and disconnect cleanly."""

    try:
        bridge.client.loop_stop()
    finally:
        try:
            bridge.client.disconnect()
        except Exception:
            pass


def main(item: str = "sample") -> None:
    """Run the MQTT messaging demo until interrupted."""

    bridge = start_mqtt_question_demo(item=item)

    def _shutdown(_sig: int, _frame: Optional[Any]) -> None:
        print("\n[messaging-demo] Shutting down...")
        _stop_bridge(bridge)

    signal.signal(signal.SIGINT, _shutdown)
    signal.signal(signal.SIGTERM, _shutdown)

    print("[messaging-demo] Waiting for commands. Press Ctrl+C to exit.")
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        pass
    finally:
        _stop_bridge(bridge)


if __name__ == "__main__":
    main()
