# STOMP Messaging Integration

Die Anwendung unterstützt jetzt STOMP (Simple Text Oriented Messaging Protocol) für die Kommunikation mit externen Apps über WebSocket-Verbindungen.

## Übersicht

Das System bietet folgende STOMP-Funktionalitäten:

### WebSocket-Endpunkte

- **`/ws`** - STOMP-Endpunkt mit SockJS-Fallback-Unterstützung
- **`/websocket`** - Reiner WebSocket-Endpunkt ohne SockJS

### Nachrichtendestinationen

#### Eingehende Nachrichten (Client → Server)
- **`/app/message`** - Allgemeine Nachrichten
- **`/app/job/status`** - Job-Status-Updates
- **`/app/device/location`** - Gerätestandort-Updates
- **`/app/auth/login`** - Anmeldung eines App-Users (Payload: { email, password })

#### Ausgehende Nachrichten (Server → Client)
- **`/topic/messages`** - Broadcast aller allgemeinen Nachrichten
- **`/topic/job-updates`** - Job-Status-Updates für alle Abonnenten
- **`/topic/device-locations`** - Gerätestandort-Updates
- **`/topic/broadcasts`** - System-weite Broadcast-Nachrichten
- **`/queue/notifications`** - Benutzerspezifische Benachrichtigungen

## Verwendung für Apps

### 1. Verbindung aufbauen

```javascript
// Mit SockJS
const socket = new SockJS('http://192.168.180.196:8080/ws');
const stompClient = Stomp.over(socket);

// Oder mit nativem WebSocket (WICHTIG: ws:// verwenden, nicht http://)
const socket = new WebSocket('ws://192.168.180.196:8080/websocket');
const stompClient = Stomp.over(socket);
```

### Flutter/Dart STOMP Client

**Für Flutter-Apps verwenden Sie diese Konfiguration:**

```dart
import 'package:stomp_dart_client/stomp.dart';
import 'package:stomp_dart_client/stomp_config.dart';
import 'package:stomp_dart_client/stomp_frame.dart';
import 'dart:convert';

// WICHTIG: Verwenden Sie ws:// für WebSocket-Verbindungen, NICHT http://
final stompClient = StompClient(
  config: StompConfig(
    url: 'ws://192.168.180.196:8080/websocket',  // Beachten Sie ws:// statt http://
    onConnect: onConnectCallback,
    onWebSocketError: (dynamic error) => print('WebSocket Error: $error'),
    onStompError: (StompFrame frame) => print('Stomp Error: ${frame.body}'),
    onDisconnect: (StompFrame frame) => print('Disconnected'),
    // Heartbeat-Konfiguration
    heartbeatIncoming: Duration(seconds: 20),
    heartbeatOutgoing: Duration(seconds: 20),
  ),
);

void onConnectCallback(StompFrame frame) {
  print('Connected to STOMP server');
  
  // Nachrichten abonnieren
  stompClient.subscribe(
    destination: '/topic/messages',
    callback: (StompFrame frame) {
      print('Received message: ${frame.body}');
    },
  );
}

// Verbindung herstellen
stompClient.activate();

// Nachricht senden
void sendMessage(String content) {
  stompClient.send(
    destination: '/app/message',
    body: jsonEncode({
      'content': content,
      'sender': 'FlutterApp',
    }),
  );
}
```

**Alternative Endpunkte für Flutter:**
Falls Probleme mit `/websocket` auftreten, versuchen Sie:
- `ws://192.168.180.196:8080/stomp` (zusätzlicher Endpunkt)
- `ws://192.168.180.196:8080/ws` (SockJS-Endpunkt ohne SockJS-Protokoll)

### 2. Verbindung herstellen

```javascript
stompClient.connect({}, function(frame) {
    console.log('Verbunden: ' + frame);
    
    // Nachrichten abonnieren
    stompClient.subscribe('/topic/messages', function(message) {
        console.log('Nachricht erhalten:', JSON.parse(message.body));
    });
});
```

### 3. Nachrichten senden

```javascript
// Allgemeine Nachricht senden
stompClient.send('/app/message', {}, JSON.stringify({
    content: 'Hallo vom App',
    sender: 'MobileApp'
}));

// Job-Status-Update senden
stompClient.send('/app/job/status', {}, JSON.stringify({
    jobId: '12345',
    status: 'IN_PROGRESS',
    progress: 75
}));

// Gerätestandort senden
stompClient.send('/app/device/location', {}, JSON.stringify({
    deviceId: 'device-001',
    latitude: 52.5200,
    longitude: 13.4050,
    accuracy: 10
}));

// Anmeldung eines App-Users
// Zuerst die Antwort-Warteschlange abonnieren (user-spezifisch)
const authSubscription = stompClient.subscribe('/user/queue/auth', function(message) {
    const resp = JSON.parse(message.body);
    console.log('Login-Antwort:', resp);
});

// Login-Request senden
stompClient.send('/app/auth/login', {}, JSON.stringify({
    email: 'user@example.com',
    password: 'geheimesPasswort'
}));
```

## Backend-Integration

### Programmatische Nachrichten senden

```java
@Autowired
private MessageController messageController;

// Benachrichtigung an spezifischen Benutzer
messageController.sendNotificationToUser("username", "Neue Aufgabe verfügbar");

// Broadcast-Nachricht an alle
messageController.sendBroadcastMessage("Systemwartung in 10 Minuten");
```

## Zeroconf (mDNS) Veröffentlichung

Die Anwendung veröffentlicht die STOMP-Schnittstelle via Zeroconf (DNS-SD/mDNS), sofern verfügbar. Es wird der Service-Typ `_stomp._tcp.local.` mit folgenden TXT-Records publiziert:
- path = Pfad für SockJS-Endpoint (Standard: /ws)
- websocket = Pfad für nativen WebSocket (Standard: /websocket)
- protocol = "stomp"

Clients können per Bonjour/mDNS nach `_stomp._tcp` suchen und erhalten Port und Metadaten.

Hinweise:
- Die Implementierung nutzt JmDNS, falls die Bibliothek auf dem Klassenpfad vorhanden ist. In Umgebungen ohne JmDNS bleibt Zeroconf stillschweigend deaktiviert (es wird ein Hinweis im Log ausgegeben).
- Konfigurierbare Properties:
  - app.zeroconf.enabled (default: true)
  - app.zeroconf.serviceName (default: votianlt-stomp)
  - app.stomp.wsPath (default: /ws)
  - app.stomp.websocketPath (default: /websocket)

## Konfiguration

Die STOMP-Konfiguration befindet sich in:
- **`WebSocketConfig.java`** - WebSocket und STOMP-Konfiguration
- **`MessageController.java`** - Nachrichtenbehandlung
- **`application.properties`** - Zusätzliche WebSocket-Einstellungen

### Wichtige Konfigurationsparameter

```properties
# Nachrichtenpuffergröße
spring.websocket.servlet.max-text-message-buffer-size=8192
spring.websocket.servlet.max-binary-message-buffer-size=8192

# STOMP aktivieren
spring.websocket.stomp.enabled=true

# Heartbeat-Einstellungen
spring.websocket.stomp.heartbeat.outgoing=10000
spring.websocket.stomp.heartbeat.incoming=10000
```

## Sicherheitshinweise

- WebSocket-Verbindungen verwenden die gleiche Authentifizierung wie die Web-Anwendung
- Nachrichten werden automatisch mit Zeitstempel versehen
- Alle Nachrichten werden in JSON-Format verarbeitet

## Testing

Zum Testen der STOMP-Funktionalität können Sie:
1. Eine WebSocket-Client-Bibliothek verwenden
2. Browser-Entwicklertools für WebSocket-Verbindungen nutzen
3. Spezialisierte STOMP-Testing-Tools einsetzen

Die Implementierung ist vollständig und bereit für die Integration mit externen Apps.

## Neue STOMP-Schnittstelle: Task-Erledigung melden

Mit dieser Schnittstelle kann ein Client die Erledigung eines Tasks melden.

- Senden (Client → Server): `/app/task/completed`
- Broadcasts (Server → Client):
  - Global: `/topic/task-updates`
  - Task-spezifisch: `/topic/tasks/{taskId}`

Payload (JSON):

```json
{
  "taskId": "<MongoId als String>",
  "completedBy": "<optional: Name/ID des Ausführenden>",
  "note": "<optional: Kommentar>"
}
```

Antwort (Beispiel):

```json
{
  "timestamp": "2025-09-05T09:25:00",
  "type": "taskCompletedAck",
  "success": true,
  "taskId": "...",
  "jobId": "...",
  "completed": true,
  "completedAt": "2025-09-05T09:25:00",
  "completedBy": "driver01",
  "note": "Übergabe erfolgreich",
  "event": "taskCompleted"
}
```

JavaScript Beispiel:

```javascript
// Abonnieren der globalen Updates
stompClient.subscribe('/topic/task-updates', (frame) => {
  console.log('Task update:', JSON.parse(frame.body));
});

// Abonnieren eines spezifischen Tasks
stompClient.subscribe('/topic/tasks/' + taskId, (frame) => {
  console.log('Task-specific update:', JSON.parse(frame.body));
});

// Task als erledigt melden
stompClient.send('/app/task/completed', {}, JSON.stringify({
  taskId: taskId,
  completedBy: 'driver01',
  note: 'Übergabe erfolgreich'
}));
```

Flutter/Dart Beispiel:

```dart
stompClient.subscribe(
  destination: '/topic/task-updates',
  callback: (frame) => print('Task update: ${frame.body}'),
);

stompClient.subscribe(
  destination: '/topic/tasks/$taskId',
  callback: (frame) => print('Task-specific update: ${frame.body}'),
);

stompClient.send(
  destination: '/app/task/completed',
  body: jsonEncode({
    'taskId': taskId,
    'completedBy': 'driver01',
    'note': 'Übergabe erfolgreich',
  }),
);
```

Hinweise:
- `taskId` ist Pflicht. Bei ungültiger oder unbekannter `taskId` wird `success=false` zurückgegeben.
- Der Server setzt `completed=true` und `completedAt` automatisch.
- Zusätzlich zum globalen Broadcast wird ein task-spezifisches Event auf `/topic/tasks/{taskId}` versendet.
