"""``/api/activity`` and ``/api/activity/stream`` — operator-facing event log.

Two endpoints, both gated by ``matrix_gate``:

- ``GET /api/activity``          — paginated event list with ``kind`` /
  ``since`` filters, plus an NDJSON variant when the caller sends
  ``Accept: application/x-ndjson``.
- ``GET /api/activity/stream``   — NDJSON live-tail: snapshot of the
  most recent N events, then ``activity_recorded`` events as they
  hit the store. Default ``limit`` is 50 (smaller than the list
  endpoint's 200) because activity is high-volume — callers usually
  want the most recent handful, not a full replay.

The snapshot halves of ``/api/activity`` and ``/api/activity/stream``
share the same in-memory filter logic (``kind`` + ``since``) so the
two endpoints are interchangeable for the snapshot half.

SP36 Task 10: this block moved here from ``api.py:2606`` (the
``/api/activity*`` pair).
"""
from __future__ import annotations

from typing import Any, AsyncIterator

from fastapi import APIRouter, Depends, Query, Request
from fastapi.responses import StreamingResponse

from cyclone.api_helpers import (
    ndjson_line as _ndjson_line,
    ndjson_stream_list as _ndjson_stream_list,
    tail_events as _tail_events,
    wants_ndjson as _wants_ndjson,
)
from cyclone.auth.deps import matrix_gate
from cyclone.pubsub import EventBus
from cyclone.store import store

router = APIRouter(dependencies=[Depends(matrix_gate)])


@router.get("/api/activity")
def list_activity(
    request: Request,
    kind: str | None = Query(None),
    since: str | None = Query(None),
    limit: int = Query(200, ge=1, le=5000),
) -> Any:
    events = store.recent_activity(limit=limit)
    if kind is not None:
        events = [e for e in events if e["kind"] == kind]
    if since is not None:
        events = [e for e in events if e["timestamp"] >= since]
    total = len(events)
    has_more = False
    if _wants_ndjson(request):
        return StreamingResponse(
            _ndjson_stream_list(events, total, total, has_more),
            media_type="application/x-ndjson",
        )
    return {
        "items": events,
        "total": total,
        "returned": total,
        "has_more": has_more,
    }


@router.get("/api/activity/stream")
async def activity_stream(
    request: Request,
    kind: str | None = Query(None),
    since: str | None = Query(None),
    limit: int = Query(50, ge=1, le=5000),
) -> StreamingResponse:
    """Stream Activity events as NDJSON: snapshot first, then live events.

    Subscribes to ``activity_recorded``. Default ``limit`` is 50
    (smaller than the list endpoint's 200) because activity is
    high-volume — callers usually want the most recent handful, not a
    full replay.
    """
    bus: EventBus = request.app.state.event_bus

    async def gen() -> AsyncIterator[bytes]:
        # Snapshot reuses the same in-memory filter as ``list_activity``
        # so the two endpoints are interchangeable for the snapshot
        # half.
        events = store.recent_activity(limit=limit)
        if kind is not None:
            events = [e for e in events if e["kind"] == kind]
        if since is not None:
            events = [e for e in events if e["timestamp"] >= since]
        for ev in events:
            yield _ndjson_line({"type": "item", "data": ev})
        yield _ndjson_line({
            "type": "snapshot_end", "data": {"count": len(events)},
        })

        async for chunk in _tail_events(request, bus, ["activity_recorded"]):
            yield chunk

    return StreamingResponse(gen(), media_type="application/x-ndjson")
