# 📞 Tenant VoIP Configuration System - Guide Complet

## 🎯 Vue d'ensemble

Ce système permet à chaque tenant d'avoir sa **propre configuration VoIP Telnyx**, incluant l'ID de l'assistant vocal IA, le type d'IA, et d'autres paramètres. Auparavant, toutes ces configurations étaient globales via variables d'environnement.

### ✨ Avantages

- **Configuration par tenant** : Chaque tenant peut avoir son propre assistant IA Telnyx
- **Gestion dynamique** : Modification via API sans redémarrage de l'application
- **Fallback automatique** : Si aucune configuration en base, utilise les variables `.env` (backward compatible)
- **Multi-provider** : Support pour TELNYX, TWILIO, ZIWO
- **Cache intelligent** : Performance optimisée avec TTL de 5 minutes

---

## 🏗️ Architecture

### Base de données

**Table**: `tenant_voip_configs` (dans le schéma admin `saas_db`)

```sql
CREATE TABLE tenant_voip_configs (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    tenant_id VARCHAR(100) NOT NULL,
    provider VARCHAR(50) NOT NULL,
    ai_assistant_id VARCHAR(255),
    ai_type VARCHAR(100),
    messaging_profile_id VARCHAR(255),
    stream_url VARCHAR(500),
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_tenant_provider (tenant_id, provider)
);
```

### Flux de lookup

```
Appel téléphonique Telnyx
    ↓
TelnyxTeXMLController identifie le tenant (via numéro de téléphone)
    ↓
TenantVoipConfigService.getVoipConfig(tenantId, TELNYX)
    ↓
1. Cherche dans la base de données (cache 5min)
2. Si non trouvé → Fallback vers variables .env
3. Retourne TenantVoipConfig
    ↓
Génère TeXML avec la configuration appropriée
```

---

## 📡 API Endpoints

**Base URL**: `/api/admin/tenants/{tenantId}/voip-configs`

**Sécurité**: Tous les endpoints requièrent le rôle `SYSTEM_ADMIN`

### 1. Créer une configuration VoIP

**POST** `/api/admin/tenants/{tenantId}/voip-configs`

**Request Body**:
```json
{
  "provider": "TELNYX",
  "aiAssistantId": "asst_abc123xyz",
  "aiType": "TELNYX_NATIVE_AI",
  "messagingProfileId": "msg_profile_456",
  "streamUrl": "wss://example.com/stream",
  "isActive": true
}
```

**Response**: `201 Created`
```json
{
  "id": 1,
  "tenantId": "tenant_1",
  "provider": "TELNYX",
  "aiAssistantId": "asst_abc123xyz",
  "aiType": "TELNYX_NATIVE_AI",
  "messagingProfileId": "msg_profile_456",
  "streamUrl": "wss://example.com/stream",
  "isActive": true,
  "createdAt": "2025-10-17T23:00:00",
  "updatedAt": "2025-10-17T23:00:00"
}
```

---

### 2. Lire toutes les configurations d'un tenant

**GET** `/api/admin/tenants/{tenantId}/voip-configs`

**Response**: `200 OK`
```json
[
  {
    "id": 1,
    "tenantId": "tenant_1",
    "provider": "TELNYX",
    "aiAssistantId": "asst_abc123xyz",
    "aiType": "TELNYX_NATIVE_AI",
    "messagingProfileId": "msg_profile_456",
    "streamUrl": "wss://example.com/stream",
    "isActive": true,
    "createdAt": "2025-10-17T23:00:00",
    "updatedAt": "2025-10-17T23:00:00"
  }
]
```

---

### 3. Lire la configuration d'un provider spécifique

**GET** `/api/admin/tenants/{tenantId}/voip-configs/{provider}`

**Exemple**: `GET /api/admin/tenants/tenant_1/voip-configs/TELNYX`

**Response**: 
- `200 OK` si configuration en base de données
- `404 Not Found` si aucune configuration (tenant utilise les variables `.env`)

---

### 4. Mettre à jour une configuration

**PUT** `/api/admin/tenants/{tenantId}/voip-configs/{provider}`

**Request Body** (seuls les champs non-null sont mis à jour):
```json
{
  "aiAssistantId": "asst_new_id_789",
  "aiType": "WEBSOCKET_STREAM",
  "streamUrl": "wss://new-stream.example.com",
  "isActive": true
}
```

**Response**: `200 OK` avec la configuration mise à jour

---

### 5. Supprimer une configuration

**DELETE** `/api/admin/tenants/{tenantId}/voip-configs/{provider}`

**Response**: `204 No Content`

⚠️ **Après suppression**, le tenant utilisera automatiquement les variables `.env` (fallback)

---

### 6. Activer une configuration

**PATCH** `/api/admin/tenants/{tenantId}/voip-configs/{provider}/activate`

**Response**: `200 OK`

---

### 7. Désactiver une configuration

**PATCH** `/api/admin/tenants/{tenantId}/voip-configs/{provider}/deactivate`

**Response**: `200 OK`

⚠️ **Comportement**: Si désactivée, le système retourne `null` et le tenant utilisera les variables `.env`

---

## 🧪 Tests avec Insomnia/Postman

### Scénario 1 : Créer une configuration pour tenant_1

```bash
# 1. Authentification (obtenir JWT)
POST http://localhost:8080/api/auth/login
Content-Type: application/json

{
  "email": "admin@example.com",
  "password": "admin123"
}

# Copier le token JWT de la réponse
```

```bash
# 2. Créer configuration Telnyx pour tenant_1
POST http://localhost:8080/api/admin/tenants/tenant_1/voip-configs
Authorization: Bearer {VOTRE_JWT_TOKEN}
Content-Type: application/json

{
  "provider": "TELNYX",
  "aiAssistantId": "asst_test_123",
  "aiType": "TELNYX_NATIVE_AI",
  "isActive": true
}
```

---

### Scénario 2 : Tester le fallback vers .env

```bash
# 1. Vérifier qu'il n'y a pas de config en DB
GET http://localhost:8080/api/admin/tenants/tenant_2/voip-configs/TELNYX
Authorization: Bearer {VOTRE_JWT_TOKEN}

# Devrait retourner 404 Not Found
```

```bash
# 2. Simuler un appel Telnyx pour tenant_2
# Le système utilisera automatiquement les variables .env :
# - TELNYX_AI_ASSISTANT_ID
# - TELNYX_AI_TYPE
# - TELNYX_STREAM_URL
```

---

### Scénario 3 : Modifier la configuration

```bash
# Mettre à jour l'assistant ID
PUT http://localhost:8080/api/admin/tenants/tenant_1/voip-configs/TELNYX
Authorization: Bearer {VOTRE_JWT_TOKEN}
Content-Type: application/json

{
  "aiAssistantId": "asst_updated_456"
}
```

---

## 🔄 Mécanisme de Fallback

Le service implémente une **stratégie DB-first avec fallback** :

```java
public TenantVoipConfig getVoipConfig(String tenantId, Provider provider) {
    // 1. Chercher en base de données (avec cache)
    Optional<TenantVoipConfig> dbConfig = repository
        .findByTenantIdAndProviderAndIsActive(tenantId, provider, true);
    
    if (dbConfig.isPresent()) {
        return dbConfig.get(); // ✅ Configuration trouvée en DB
    }
    
    // 2. Fallback vers variables d'environnement
    return buildConfigFromEnvironment(provider);
}
```

### Variables .env utilisées (fallback)

Pour **Telnyx** :
- `TELNYX_AI_ASSISTANT_ID` ou `TELNYX_VOICE_AI_ASSISTANT_ID`
- `TELNYX_AI_TYPE` (TELNYX_NATIVE_AI ou WEBSOCKET_STREAM)
- `TELNYX_STREAM_URL` (si WEBSOCKET_STREAM)

---

## 🚀 Démarrage et Test

### 1. Démarrer l'application

```bash
# Assurez-vous que MySQL est démarré
mysql.server start  # macOS
# ou
sudo service mysql start  # Linux

# Démarrer Spring Boot
mvn spring-boot:run
```

### 2. Vérifier la création de la table

```sql
-- Se connecter à MySQL
mysql -u root -p

-- Utiliser la base admin
USE saas_db;

-- Vérifier la table
DESCRIBE tenant_voip_configs;
SHOW CREATE TABLE tenant_voip_configs;
```

### 3. Test manuel avec curl

```bash
# Créer config
curl -X POST http://localhost:8080/api/admin/tenants/tenant_1/voip-configs \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "TELNYX",
    "aiAssistantId": "asst_test_123",
    "aiType": "TELNYX_NATIVE_AI",
    "isActive": true
  }'

# Lire config
curl -X GET http://localhost:8080/api/admin/tenants/tenant_1/voip-configs/TELNYX \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

---

## 📊 Cache et Performance

- **Cache activé** : Spring Cache avec TTL de 5 minutes
- **Éviction automatique** : Lors des opérations de création/mise à jour/suppression/activation/désactivation
- **Monitoring** : Les logs indiquent si la config vient du cache, DB ou .env

```
🔍 Looking up VoIP config for tenant: tenant_1, provider: TELNYX
✅ VoIP config found in DB (cached): aiType=TELNYX_NATIVE_AI
```

---

## ⚠️ Notes importantes

1. **Unicité** : Un tenant ne peut avoir qu'**une seule configuration active par provider**
2. **Sécurité** : Seuls les `SYSTEM_ADMIN` peuvent gérer ces configurations
3. **Backward compatible** : Les tenants sans config en DB continuent d'utiliser `.env`
4. **Cache** : Après modification, attendre max 5 minutes pour propagation (ou redémarrer l'app)

---

## 🐛 Debugging

### Logs à surveiller

```
🔍 Looking up VoIP config for tenant: XXX, provider: YYY
✅ VoIP config found in DB
⚠️ No config in DB, falling back to environment variables
🔧 VoIP Config - Type: TELNYX_NATIVE_AI, AssistantID: asst_xxx, StreamURL: null
```

### Problèmes courants

| Problème | Solution |
|----------|----------|
| `404 Not Found` sur GET | Normal si pas de config en DB - vérifier les variables `.env` |
| Config pas mise à jour | Attendre 5min (cache) ou redémarrer l'application |
| `401 Unauthorized` | Vérifier que le JWT contient `userType=SYSTEM_ADMIN` |
| Table non créée | Vérifier `ddl-auto=update` dans `application.properties` |

---

## 📝 Exemple complet d'utilisation

```java
// Dans TelnyxTeXMLController
TenantVoipConfig config = voipConfigService.getVoipConfig("tenant_1", Provider.TELNYX);

// Si tenant_1 a une config en DB :
// config.getAiAssistantId() = "asst_abc123"
// config.getAiType() = "TELNYX_NATIVE_AI"
// config.getId() != null

// Si tenant_1 n'a PAS de config en DB :
// config.getAiAssistantId() = valeur de TELNYX_AI_ASSISTANT_ID (.env)
// config.getAiType() = valeur de TELNYX_AI_TYPE (.env)
// config.getId() == null (indique fallback .env)
```

---

## ✅ Checklist de validation

- [ ] La table `tenant_voip_configs` existe dans `saas_db`
- [ ] Un admin peut créer une config via POST
- [ ] Un admin peut lire une config via GET
- [ ] Un admin peut modifier une config via PUT
- [ ] Un admin peut supprimer une config via DELETE
- [ ] Un admin peut activer/désactiver une config via PATCH
- [ ] Le fallback vers `.env` fonctionne quand pas de config en DB
- [ ] Un appel Telnyx utilise la bonne config (DB ou .env)
- [ ] Les logs indiquent clairement la source de la config
- [ ] Le cache fonctionne (vérifier les logs de performance)

---

## 🎉 Conclusion

Le système est maintenant **entièrement dynamique** :
- Chaque tenant peut avoir sa propre configuration Telnyx
- Modification sans redémarrage de l'application
- Backward compatible avec l'ancien système de variables `.env`
- Performance optimisée avec cache intelligent

Pour toute question, vérifiez les logs de l'application ! 🚀
