"""Realtime subscription to conversations via Supabase Realtime (WebSocket)."""

from __future__ import annotations

import json
import threading
import time
from typing import Any, Callable

import websocket

from .config import load_env_config


def subscribe_to_conversations(
    did: str,
    access_token: str,
    on_message: Callable[[dict[str, Any]], None],
    on_error: Callable[[str], None] | None = None,
) -> Callable[[], None]:
    """
    Subscribe to realtime changes on the interactions table for a given DID.
    Returns a stop() function to close the connection.
    """
    supabase_url, anon_key = load_env_config()

    # Build realtime WS URL
    ws_url = supabase_url.replace("https://", "wss://").replace("http://", "ws://")
    ws_url = f"{ws_url}/realtime/v1/websocket?apikey={anon_key}&vsn=1.0.0"

    stop_event = threading.Event()
    heartbeat_ref = [0]

    def on_ws_message(ws: Any, raw: str) -> None:
        try:
            msg = json.loads(raw)
        except json.JSONDecodeError:
            return

        event = msg.get("event")

        if event == "phx_reply":
            status = msg.get("payload", {}).get("status")
            if status != "ok" and on_error:
                on_error(f"phx_reply status={status}: {msg.get('payload')}")
            return

        if event == "postgres_changes":
            payload = msg.get("payload", {})
            data = payload.get("data", {})
            record = data.get("record", {})
            if record:
                on_message(record)
            return

        if event in ("INSERT", "UPDATE"):
            record = msg.get("payload", {}).get("record", {})
            if record:
                on_message(record)

    def on_ws_error(ws: Any, error: Any) -> None:
        if on_error:
            on_error(str(error))

    def on_ws_open(ws: Any) -> None:
        # Join the realtime channel for interactions involving our DID
        # Subscribe to both participant_a and participant_b columns
        join_msg = {
            "topic": f"realtime:public:interactions",
            "event": "phx_join",
            "payload": {
                "config": {
                    "broadcast": {"self": False},
                    "presence": {"key": ""},
                    "postgres_changes": [
                        {
                            "event": "*",
                            "schema": "public",
                            "table": "interactions",
                            "filter": f"participant_a_did=eq.{did}",
                        },
                        {
                            "event": "*",
                            "schema": "public",
                            "table": "interactions",
                            "filter": f"participant_b_did=eq.{did}",
                        },
                    ],
                },
            },
            "ref": "1",
        }
        ws.send(json.dumps(join_msg))

        # Set access token
        token_msg = {
            "topic": f"realtime:public:interactions",
            "event": "access_token",
            "payload": {"access_token": access_token},
            "ref": "2",
        }
        ws.send(json.dumps(token_msg))

        # Start heartbeat
        def heartbeat() -> None:
            while not stop_event.is_set():
                heartbeat_ref[0] += 1
                try:
                    ws.send(json.dumps({
                        "topic": "phoenix",
                        "event": "heartbeat",
                        "payload": {},
                        "ref": str(heartbeat_ref[0]),
                    }))
                except Exception:
                    break
                stop_event.wait(30)

        t = threading.Thread(target=heartbeat, daemon=True)
        t.start()

    ws_app = websocket.WebSocketApp(
        ws_url,
        on_message=on_ws_message,
        on_error=on_ws_error,
        on_open=on_ws_open,
        header={"Authorization": f"Bearer {access_token}"},
    )

    thread = threading.Thread(
        target=lambda: ws_app.run_forever(ping_interval=30),
        daemon=True,
    )
    thread.start()

    def stop() -> None:
        stop_event.set()
        ws_app.close()

    return stop
