"""NIP-01 WebSocket relay client for Nostr event publication.

Connects to Nostr relays via WebSocket and publishes events using
the NIP-01 ``["EVENT", event]`` message format.

Degrades gracefully when the relay is unavailable or the ``websockets``
package is not installed.

Usage
-----
    from infrastructure.nostr.relay import publish_to_relay

    ok = await publish_to_relay("wss://relay.damus.io", signed_event)
    # Returns True if the relay accepted the event.
"""

from __future__ import annotations

import asyncio
import json
import logging
from typing import Any

logger = logging.getLogger(__name__)

NostrEvent = dict[str, Any]

# Timeout for relay operations (seconds)
_CONNECT_TIMEOUT = 10
_PUBLISH_TIMEOUT = 15


async def publish_to_relay(relay_url: str, event: NostrEvent) -> bool:
    """Publish a signed NIP-01 event to a single relay.

    Parameters
    ----------
    relay_url:
        ``wss://`` or ``ws://`` WebSocket URL of the relay.
    event:
        A fully signed NIP-01 event dict.

    Returns
    -------
    bool
        True if the relay acknowledged the event (``["OK", id, true, …]``),
        False otherwise (never raises).
    """
    try:
        import websockets
    except ImportError:
        logger.warning(
            "websockets package not available — Nostr relay publish skipped "
            "(install with: pip install websockets)"
        )
        return False

    event_id = event.get("id", "")
    message = json.dumps(["EVENT", event], separators=(",", ":"))

    try:
        async with asyncio.timeout(_CONNECT_TIMEOUT):
            ws = await websockets.connect(relay_url, open_timeout=_CONNECT_TIMEOUT)
    except Exception as exc:
        logger.warning("Nostr relay connect failed (%s): %s", relay_url, exc)
        return False

    try:
        async with ws:
            await ws.send(message)
            # Wait for OK response with timeout
            async with asyncio.timeout(_PUBLISH_TIMEOUT):
                async for raw in ws:
                    try:
                        resp = json.loads(raw)
                    except json.JSONDecodeError:
                        continue
                    if (
                        isinstance(resp, list)
                        and len(resp) >= 3
                        and resp[0] == "OK"
                        and resp[1] == event_id
                    ):
                        if resp[2] is True:
                            logger.debug("Relay %s accepted event %s", relay_url, event_id[:8])
                            return True
                        else:
                            reason = resp[3] if len(resp) > 3 else ""
                            logger.warning(
                                "Relay %s rejected event %s: %s",
                                relay_url,
                                event_id[:8],
                                reason,
                            )
                            return False
    except TimeoutError:
        logger.warning("Relay %s timed out waiting for OK on event %s", relay_url, event_id[:8])
        return False
    except Exception as exc:
        logger.warning("Relay %s error publishing event %s: %s", relay_url, event_id[:8], exc)
        return False

    logger.warning("Relay %s closed without OK for event %s", relay_url, event_id[:8])
    return False


async def publish_to_relays(relay_urls: list[str], event: NostrEvent) -> dict[str, bool]:
    """Publish an event to multiple relays concurrently.

    Parameters
    ----------
    relay_urls:
        List of relay WebSocket URLs.
    event:
        A fully signed NIP-01 event dict.

    Returns
    -------
    dict[str, bool]
        Mapping of relay URL → success flag.
    """
    if not relay_urls:
        return {}

    tasks = {url: asyncio.create_task(publish_to_relay(url, event)) for url in relay_urls}
    results: dict[str, bool] = {}
    for url, task in tasks.items():
        try:
            results[url] = await task
        except Exception as exc:
            logger.warning("Unexpected error publishing to %s: %s", url, exc)
            results[url] = False
    return results
