# Piano: Collaborazione Real-Time nel Report Designer

## Obiettivo

Implementare la collaborazione in tempo reale nel Report Designer, simile a Google Docs o Excel Online. Quando un utente modifica un template, tutti gli altri utenti collegati allo stesso template vedono le modifiche istantaneamente.

---

## Architettura Proposta

### Concetti Chiave

1. **Room-based Collaboration**: Ogni template ha una "room" SignalR dedicata
2. **Operational Transformation Semplificata**: Invece di OT completo (complesso), usiamo un modello "last-write-wins" con sync frequente
3. **Presence Awareness**: Gli utenti vedono chi altro sta modificando il template
4. **Cursor/Selection Sharing**: Mostra quale elemento sta selezionando ogni utente

### Flusso Dati

```
User A modifica elemento
       ↓
Template locale aggiornato (come ora)
       ↓
SignalR invia delta alla room
       ↓
Backend riceve e ritrasmette a tutti nella room (eccetto mittente)
       ↓
User B/C/... ricevono delta
       ↓
Applicano delta al loro template locale
       ↓
Canvas si aggiorna automaticamente (già funziona così)
```

---

## Implementazione Dettagliata

### FASE 1: Backend - ReportCollaborationHub

**File:** `/src/Apollinare.API/Hubs/ReportCollaborationHub.cs`

Nuovo Hub dedicato per la collaborazione sui report:

```csharp
public class ReportCollaborationHub : Hub
{
    // Stato in-memory degli utenti per template
    private static ConcurrentDictionary<string, HashSet<CollaboratorInfo>> _templateUsers = new();

    // Join a template room
    public async Task JoinTemplate(int templateId, string userName, string userColor)

    // Leave template room
    public async Task LeaveTemplate(int templateId)

    // Broadcast element change to room
    public async Task ElementChanged(int templateId, ElementChangeDto change)

    // Broadcast element added
    public async Task ElementAdded(int templateId, AprtElement element)

    // Broadcast element deleted
    public async Task ElementDeleted(int templateId, string elementId)

    // Broadcast page changes
    public async Task PageChanged(int templateId, PageChangeDto change)

    // Broadcast selection change (which element user is editing)
    public async Task SelectionChanged(int templateId, string? elementId)

    // Broadcast cursor position (optional, for live cursors)
    public async Task CursorMoved(int templateId, float x, float y)

    // Request full template sync (when joining late or after reconnect)
    public async Task RequestSync(int templateId)

    // Send full template state (host responds to sync requests)
    public async Task SendSync(int templateId, string connectionId, AprtTemplate template)
}
```

**DTOs:**

```csharp
public record CollaboratorInfo(string ConnectionId, string UserName, string Color, string? SelectedElementId);

public record ElementChangeDto(
    string ElementId,
    string ChangeType,  // "position", "style", "content", "visibility", etc.
    object NewValue
);

public record PageChangeDto(
    string PageId,
    string ChangeType,  // "added", "deleted", "reordered", "renamed", "settings"
    object? Data
);
```

### FASE 2: Frontend - Servizio Collaborazione

**File:** `/frontend/src/services/reportCollaboration.ts`

```typescript
class ReportCollaborationService {
  private connection: HubConnection | null = null;
  private templateId: number | null = null;
  private listeners: Map<string, Set<Function>> = new Map();

  // Connessione e gestione room
  async joinTemplate(
    templateId: number,
    userName: string,
    userColor: string,
  ): Promise<void>;
  async leaveTemplate(): Promise<void>;

  // Invio modifiche (chiamati dal ReportEditorPage)
  sendElementChange(elementId: string, changeType: string, newValue: any): void;
  sendElementAdded(element: AprtElement): void;
  sendElementDeleted(elementId: string): void;
  sendPageChange(pageId: string, changeType: string, data?: any): void;
  sendSelectionChange(elementId: string | null): void;

  // Sottoscrizione eventi (per ricevere modifiche da altri)
  onElementChanged(callback: (change: ElementChange) => void): () => void;
  onElementAdded(callback: (element: AprtElement) => void): () => void;
  onElementDeleted(callback: (elementId: string) => void): () => void;
  onPageChanged(callback: (change: PageChange) => void): () => void;
  onCollaboratorsChanged(
    callback: (collaborators: Collaborator[]) => void,
  ): () => void;
  onSelectionChanged(
    callback: (userId: string, elementId: string | null) => void,
  ): () => void;
  onSyncRequested(callback: (requesterId: string) => void): () => void;

  // Sync
  requestSync(): void;
  sendSync(connectionId: string, template: AprtTemplate): void;
}
```

### FASE 3: Frontend - Integrazione in ReportEditorPage

**Modifiche a:** `/frontend/src/pages/ReportEditorPage.tsx`

1. **Nuovo State per collaborazione:**

```typescript
const [collaborators, setCollaborators] = useState<Collaborator[]>([]);
const [remoteSelections, setRemoteSelections] = useState<Map<string, string>>(
  new Map(),
);
const [isCollaborating, setIsCollaborating] = useState(false);
```

2. **Join/Leave room al mount/unmount:**

```typescript
useEffect(() => {
  if (!isNew && id) {
    const userName = getCurrentUserName(); // Da auth context
    const userColor = generateUserColor(userName);

    reportCollaborationService
      .joinTemplate(Number(id), userName, userColor)
      .then(() => setIsCollaborating(true));

    return () => {
      reportCollaborationService.leaveTemplate();
    };
  }
}, [id, isNew]);
```

3. **Sottoscrizione eventi remoti:**

```typescript
useEffect(() => {
  if (!isCollaborating) return;

  const unsubscribers = [
    reportCollaborationService.onElementChanged((change) => {
      // Applica modifica senza creare history entry
      historyActions.setWithoutHistory((prev) => {
        // ... applica change.newValue a element con change.elementId
      });
    }),

    reportCollaborationService.onElementAdded((element) => {
      historyActions.setWithoutHistory((prev) => ({
        ...prev,
        elements: [...prev.elements, element],
      }));
    }),

    // ... altri handler

    reportCollaborationService.onCollaboratorsChanged(setCollaborators),
  ];

  return () => unsubscribers.forEach((unsub) => unsub());
}, [isCollaborating]);
```

4. **Invio modifiche locali:**

Modificare `handleUpdateElement` per inviare anche via SignalR:

```typescript
const handleUpdateElement = useCallback(
  (elementId: string, updates: Partial<AprtElement>) => {
    // Aggiorna stato locale (come ora)
    historyActions.set((prev) => ({...}));

    // Invia a collaboratori
    if (isCollaborating) {
      reportCollaborationService.sendElementChange(elementId, "update", updates);
    }
  },
  [historyActions, isCollaborating],
);
```

### FASE 4: UI Collaborazione

**File:** `/frontend/src/components/reportEditor/CollaboratorsBar.tsx`

Barra che mostra gli utenti connessi:

```typescript
interface CollaboratorsBarProps {
  collaborators: Collaborator[];
  remoteSelections: Map<string, string>; // userId -> elementId
}

// Mostra:
// - Avatar circolari colorati per ogni collaboratore
// - Tooltip con nome utente
// - Indicatore "sta modificando [elemento]"
// - Badge con conteggio totale collaboratori
```

**Modifiche a EditorCanvas:** Evidenziare elementi selezionati da altri utenti con bordo colorato.

### FASE 5: Gestione Conflitti

Per semplicità, usiamo strategia **"last-write-wins"** con alcune ottimizzazioni:

1. **Elementi diversi**: Nessun conflitto, modifiche applicate indipendentemente
2. **Stesso elemento, proprietà diverse**: Merge delle proprietà
3. **Stesso elemento, stessa proprietà**: Ultima modifica vince
4. **Lock visivo**: Quando un utente seleziona un elemento, gli altri vedono un indicatore

**Opzionale (Fase futura):** Lock pessimistico - solo un utente alla volta può modificare un elemento.

---

## File da Creare/Modificare

### Nuovi File

| File                                                        | Descrizione                         |
| ----------------------------------------------------------- | ----------------------------------- |
| `src/Apollinare.API/Hubs/ReportCollaborationHub.cs`         | Hub SignalR per collaborazione      |
| `src/Apollinare.API/Models/CollaborationDtos.cs`            | DTOs per messaggi collaborazione    |
| `frontend/src/services/reportCollaboration.ts`              | Client SignalR per collaborazione   |
| `frontend/src/components/reportEditor/CollaboratorsBar.tsx` | UI collaboratori connessi           |
| `frontend/src/types/collaboration.ts`                       | Types TypeScript per collaborazione |

### File da Modificare

| File                                                     | Modifiche                                         |
| -------------------------------------------------------- | ------------------------------------------------- |
| `src/Apollinare.API/Program.cs`                          | Registrare nuovo hub `/hubs/report-collaboration` |
| `frontend/src/pages/ReportEditorPage.tsx`                | Integrare collaborazione, stato collaboratori     |
| `frontend/src/components/reportEditor/EditorCanvas.tsx`  | Mostrare selezioni remote                         |
| `frontend/src/components/reportEditor/EditorToolbar.tsx` | Mostrare CollaboratorsBar                         |

---

## Stima Complessità

| Fase                   | Complessità | Note                                     |
| ---------------------- | ----------- | ---------------------------------------- |
| 1. Backend Hub         | Media       | SignalR groups, gestione stato in-memory |
| 2. Frontend Service    | Media       | Gestione connessione, eventi             |
| 3. Integrazione Editor | Alta        | Molti handler da modificare              |
| 4. UI Collaboratori    | Bassa       | Componente semplice                      |
| 5. Gestione Conflitti  | Media       | Merge logic                              |

---

## Considerazioni Aggiuntive

### Performance

- Throttling degli aggiornamenti durante drag (ogni 50-100ms invece di ogni frame)
- Debounce per modifiche testo (300ms)
- Batch di modifiche multiple in singolo messaggio

### Scalabilità

- Per deployment multi-server: usare Redis backplane per SignalR
- Considerare Azure SignalR Service per produzione

### Autenticazione

- Aggiungere autenticazione al hub (verificare che utente abbia accesso al template)
- Usare JWT token per identificare utente

### Edge Cases

- Reconnessione dopo disconnessione: richiedere sync completo
- Template eliminato mentre utenti connessi: notificare e chiudere
- Conflitto salvataggio: merge o "force save" con conferma

---

## Ordine di Implementazione Consigliato

1. **Backend Hub base** - Join/Leave room, broadcast semplice
2. **Frontend service base** - Connessione, invio/ricezione messaggi
3. **Integrazione minima** - Solo sync modifiche elementi (no UI collaboratori)
4. **Test funzionale** - Verificare che modifiche si propaghino
5. **UI Collaboratori** - Mostrare chi è connesso
6. **Selezioni remote** - Evidenziare elementi selezionati da altri
7. **Ottimizzazioni** - Throttling, batching, gestione conflitti

---

## Domande per l'Utente

Prima di procedere, confermare:

1. **Autenticazione**: Il sistema ha già autenticazione utenti? Devo usare un sistema mock per ora?
2. **Persistenza stato**: Le modifiche devono essere salvate automaticamente o solo quando l'utente clicca "Salva"?
3. **Lock elementi**: Vuoi che solo un utente alla volta possa modificare un elemento (lock pessimistico)?
4. **Cursori live**: Vuoi vedere il cursore degli altri utenti in tempo reale (come Google Docs)?
5. **Nome utente**: Da dove prendo il nome utente da mostrare? (localStorage, auth context, prompt?)
