# SSE Event Streamer

## Interface Definition

**Interface Name**: `ISSEEventStreamer`

### Interface Methods

```python
class ISSEEventStreamer(ABC):
    @abstractmethod
    def create_stream(self, flight_id: str, client_id: str) -> StreamConnection:
        pass
    
    @abstractmethod
    def send_frame_result(self, flight_id: str, frame_result: FrameResult) -> bool:
        pass
    
    @abstractmethod
    def send_search_progress(self, flight_id: str, search_status: SearchStatus) -> bool:
        pass
    
    @abstractmethod
    def send_user_input_request(self, flight_id: str, request: UserInputRequest) -> bool:
        pass
    
    @abstractmethod
    def send_refinement(self, flight_id: str, frame_id: int, updated_result: FrameResult) -> bool:
        pass
    
    @abstractmethod
    def send_heartbeat(self, flight_id: str) -> bool:
        pass
    
    @abstractmethod
    def close_stream(self, flight_id: str, client_id: str) -> bool:
        pass
    
    @abstractmethod
    def get_active_connections(self, flight_id: str) -> int:
        pass
```

## Component Description

### Responsibilities
- Real-time communication with clients.
- Buffering events for disconnected clients.

### Callers
- **F02.1 Flight Lifecycle Manager**: Calls `create_stream` (delegated from F01).
- **F02.2 Flight Processing Engine**: Calls `send_user_input_request`, `send_search_progress`.
- **F14 Result Manager**: Calls `send_frame_result`, `send_refinement`.

### Consistency Fix
- Previously listed F11 as caller. **Correction**: F11 returns request objects to F02.2. **F02.2 is the sole caller** for user input requests and search status updates. F11 has no dependencies on F15.

### Scope
- SSE protocol implementation
- Event formatting and sending
- Connection management
- Client reconnection handling
- Multiple concurrent streams per flight

## API Methods

### `create_stream(flight_id: str, client_id: str) -> StreamConnection`

**Description**: Establishes a server-sent events connection.

**Called By**: F01 REST API (GET /stream endpoint)

**Output**:
```python
StreamConnection:
    stream_id: str
    flight_id: str
    client_id: str
    last_event_id: Optional[str]
```

**Event Types**:
- `frame_processed`
- `frame_refined`
- `search_expanded`
- `user_input_needed`
- `processing_blocked`
- `route_api_updated`
- `route_completed`

**Test Cases**:
1. Create stream → client receives keepalive pings
2. Multiple clients → each gets own stream

---

### `send_frame_result(flight_id: str, frame_result: FrameResult) -> bool`

**Description**: Sends frame_processed event.

**Called By**: F14 Result Manager

**Event Format**:
```json
{
  "event": "frame_processed",
  "id": "frame_237",
  "data": {
    "frame_id": 237,
    "gps": {"lat": 48.123, "lon": 37.456},
    "altitude": 800.0,
    "confidence": 0.95,
    "heading": 87.3,
    "timestamp": "2025-11-24T10:30:00Z"
  }
}
```

**Test Cases**:
1. Send event → all clients receive
2. Client disconnected → event buffered for replay

---

### `send_search_progress(flight_id: str, search_status: SearchStatus) -> bool`

**Description**: Sends search_expanded event.

**Called By**: F02.2 Flight Processing Engine (via F11 status return)

**Event Format**:
```json
{
  "event": "search_expanded",
  "data": {
    "frame_id": 237,
    "grid_size": 9,
    "status": "searching"
  }
}
```

---

### `send_user_input_request(flight_id: str, request: UserInputRequest) -> bool`

**Description**: Sends user_input_needed event.

**Called By**: F02.2 Flight Processing Engine (via F11 request object)

**Event Format**:
```json
{
  "event": "user_input_needed",
  "data": {
    "request_id": "uuid",
    "frame_id": 237,
    "candidate_tiles": [...]
  }
}
```

---

### `send_refinement(flight_id: str, frame_id: int, updated_result: FrameResult) -> bool`

**Description**: Sends frame_refined event.

**Called By**: F14 Result Manager

**Event Format**:
```json
{
  "event": "frame_refined",
  "data": {
    "frame_id": 237,
    "gps": {"lat": 48.1235, "lon": 37.4562},
    "refined": true
  }
}
```

---

### `send_heartbeat(flight_id: str) -> bool`

**Description**: Sends heartbeat/keepalive to all clients subscribed to flight.

**Called By**:
- Background heartbeat task (every 30 seconds)
- F02.2 Flight Processing Engine (periodically during processing)

**Event Format**:
```
:heartbeat
```

**Behavior**:
- Sends SSE comment (`:heartbeat`) which doesn't trigger event handlers
- Keeps TCP connection alive
- Client can use to detect connection health

**Test Cases**:
1. Send heartbeat → all clients receive ping
2. Client timeout → connection marked stale

---

### `close_stream(flight_id: str, client_id: str) -> bool`

**Description**: Closes SSE connection.

**Called By**: F01 REST API (on client disconnect)

---

### `get_active_connections(flight_id: str) -> int`

**Description**: Returns count of active SSE connections for a flight.

**Called By**:
- F02.2 Flight Processing Engine (monitoring)
- Admin tools

**Test Cases**:
1. No connections → returns 0
2. 5 clients connected → returns 5

## Integration Tests

### Test 1: Real-Time Streaming
1. Client connects
2. Process 100 frames
3. Client receives 100 frame_processed events
4. Verify order and completeness

### Test 2: Reconnection with Replay
1. Client connects
2. Process 50 frames
3. Client disconnects
4. Process 50 more frames
5. Client reconnects with last_event_id
6. Client receives frames 51-100

### Test 3: User Input Flow
1. Processing blocks
2. send_user_input_request()
3. Client receives event
4. Client responds with fix
5. Processing resumes

## Non-Functional Requirements

### Performance
- **Event latency**: < 500ms from generation to client
- **Throughput**: 100 events/second
- **Concurrent connections**: 1000+ clients

### Reliability
- Event buffering for disconnected clients
- Automatic reconnection support
- Keepalive pings every 30 seconds

## Dependencies

### Internal Components
- None (receives calls from other components)

### External Dependencies
- **FastAPI** or **Flask** SSE support
- **asyncio**: For async event streaming

## Data Models

### StreamConnection
```python
class StreamConnection(BaseModel):
    stream_id: str
    flight_id: str
    client_id: str
    created_at: datetime
    last_event_id: Optional[str]
```

### SSEEvent
```python
class SSEEvent(BaseModel):
    event: str
    id: Optional[str]
    data: Dict[str, Any]
```
