"""Server-Sent Events (SSE) view for django-devicehub realtime events.

A lightweight alternative to Django Channels that requires no Redis,
no ASGI server, and no additional dependencies. Works with any WSGI
server that supports streaming responses (gunicorn with sync workers,
Django's dev server, etc.).

URL patterns (defined in ``urls.py``)::

    /iot/stream/{device_type}/               # all devices of a type
    /iot/stream/{device_type}/{device_id}/    # specific device

Events are sent in standard SSE format::

    event: data
    data: {"device_id": "ws-001", "data": {"temperature": 25.3}, ...}

    event: status
    data: {"device_id": "ws-001", "data": {"old_status": "offline", ...}, ...}

Configuration::

    DJANGO_DEVICEHUB = {
        "REALTIME": {
            "BACKEND": "sse",
            "SSE_KEEPALIVE_INTERVAL": 30,   # seconds between keepalive comments
        },
    }
"""

import json
import logging
import queue
import time

from django.contrib.auth.decorators import login_required
from django.http import StreamingHttpResponse
from django.views.decorators.http import require_GET

from ..conf import devicehub_settings

logger = logging.getLogger("django_devicehub.realtime.sse")


def _get_keepalive_interval():
    """Return the SSE keepalive interval in seconds."""
    rt_conf = getattr(devicehub_settings, "REALTIME", {})
    return rt_conf.get("SSE_KEEPALIVE_INTERVAL", 30)


def _event_stream(group_name, event_queue):
    """Generator that yields SSE-formatted events.

    Blocks on the queue, yielding events as they arrive. Sends a
    keepalive comment (``:``) if no events arrive within the keepalive
    interval, preventing proxies/browsers from closing the connection.
    """
    keepalive = _get_keepalive_interval()

    # Initial connection event
    yield _format_sse("connected", json.dumps({"group": group_name}))

    try:
        while True:
            try:
                data = event_queue.get(timeout=keepalive)
            except queue.Empty:
                # Keepalive comment
                yield ": keepalive\n\n"
                continue

            # Parse to extract event type
            try:
                parsed = json.loads(data)
                event_type = parsed.get("event", "message")
            except (json.JSONDecodeError, TypeError):
                event_type = "message"

            yield _format_sse(event_type, data)
    except GeneratorExit:
        # Client disconnected
        pass


def _format_sse(event, data, event_id=None):
    """Format a message in SSE wire format.

    Parameters:
        event: Event type name (appears as ``event:`` line).
        data: Serialized data string (appears as ``data:`` line(s)).
        event_id: Optional event ID (appears as ``id:`` line).

    Returns:
        str ready to write to the response stream.
    """
    lines = []
    if event_id is not None:
        lines.append(f"id: {event_id}")
    lines.append(f"event: {event}")
    # SSE data lines — split on newlines to handle multi-line payloads
    for line in data.split("\n"):
        lines.append(f"data: {line}")
    lines.append("")  # Blank line terminates the event
    lines.append("")
    return "\n".join(lines)


@require_GET
@login_required
def sse_stream(request, device_type, device_id=None):
    """SSE endpoint that streams realtime device events.

    Requires authentication (redirects to login if anonymous).

    Parameters:
        device_type: The device type name (from URL).
        device_id: Optional specific device ID (from URL).

    Returns:
        StreamingHttpResponse with ``text/event-stream`` content type.
    """
    from .bridge import realtime_bridge

    # Build group name(s) — same naming convention as Channels consumer
    if device_id:
        group_name = f"iot_{device_type}_{device_id}"
    else:
        group_name = f"iot_{device_type}"

    # Create a queue for this subscriber
    event_queue = queue.Queue(maxsize=256)
    realtime_bridge.sse_subscribe(group_name, event_queue)

    def stream():
        try:
            yield from _event_stream(group_name, event_queue)
        finally:
            realtime_bridge.sse_unsubscribe(group_name, event_queue)

    response = StreamingHttpResponse(
        streaming_content=stream(),
        content_type="text/event-stream",
    )
    # Disable buffering for nginx/reverse proxies
    response["Cache-Control"] = "no-cache"
    response["X-Accel-Buffering"] = "no"
    return response


@require_GET
@login_required
def sse_poll(request, device_type, device_id=None):
    """Polling fallback endpoint for clients that don't support SSE.

    Returns the latest event (if any) as a single JSON response.
    Clients should poll this endpoint at a regular interval.

    This is a simple fallback — for production use, prefer SSE or WebSockets.
    """
    from django.http import JsonResponse
    from .bridge import realtime_bridge

    if device_id:
        group_name = f"iot_{device_type}_{device_id}"
    else:
        group_name = f"iot_{device_type}"

    # Create a temporary queue, check for pending events, return immediately
    event_queue = queue.Queue(maxsize=16)
    realtime_bridge.sse_subscribe(group_name, event_queue)

    events = []
    # Give a very brief window for any queued events
    deadline = time.monotonic() + 0.1
    while time.monotonic() < deadline:
        try:
            data = event_queue.get_nowait()
            try:
                events.append(json.loads(data))
            except (json.JSONDecodeError, TypeError):
                events.append({"raw": data})
        except queue.Empty:
            break

    realtime_bridge.sse_unsubscribe(group_name, event_queue)

    return JsonResponse({"events": events})
