# Story 3.4: Créer l'endpoint API pour récupérer les prédictions

Status: review

## Acceptance Criteria

**Given** des prédictions existent dans la base de données
**When** je fais une requête GET `/api/v1/predictions`
**Then** je reçois une liste de prédictions avec match, confidence, équipe prédite
**And** les prédictions sont triées par date de match (prochaines en premier)
**And** la réponse est au format JSON avec structure standardisée

**Given** je fais une requête pour un match spécifique
**When** je fais GET `/api/v1/predictions/{match_id}`
**Then** je reçois les détails complets de la prédiction
**And** la réponse inclut l'énergie collective, confidence, historique

## Tasks / Subtasks

- [x] Créer l'endpoint GET /predictions (AC: #1)
  - [x] Créer `GET /api/v1/predictions`
  - [x] Récupérer toutes les prédictions depuis la base de données
  - [x] Joindre avec les données des matchs
  - [x] Trier par date de match (prochaines en premier)
  - [x] Retourner la liste avec pagination

- [x] Créer l'endpoint GET /predictions/{match_id} (AC: #2)
  - [x] Créer `GET /api/v1/predictions/{match_id}`
  - [x] Récupérer la prédiction spécifique
  - [x] Inclure les détails complets (match, énergie, historique)
  - [x] Retourner 404 si la prédiction n'existe pas
  - [x] Formater la réponse standardisée

- [x] Créer les schémas Pydantic pour les réponses (AC: #1, #2)
  - [x] Créer `PredictionResponse` schema
  - [x] Créer `PredictionListResponse` schema
  - [x] Inclure les champs: match, confidence, predicted_winner
  - [x] Valider les types et formats
  - [x] Documenter les schémas

- [x] Implémenter la pagination et filtres (AC: #1)
  - [x] Ajouter les query parameters: limit, offset
  - [x] Ajouter les filtres optionnels: team_id, league, date_min, date_max
  - [x] Valider les paramètres avec Pydantic
  - [x] Appliquer les filtres aux requêtes de base de données
  - [x] Retourner les métadonnées de pagination

- [x] Optimiser les requêtes de base de données (AC: #1)
  - [x] Créer les indexes appropriés sur `predictions`
  - [x] Optimiser les JOINs avec `matches`
  - [x] Implémenter le caching si nécessaire
  - [x] Valider les performances avec 1000+ prédictions
  - [x] Monitorer les temps de réponse

- [x] Documenter les endpoints avec Swagger (AC: #1, #2)
  - [x] Ajouter les descriptions détaillées des endpoints
  - [x] Ajouter les exemples de requêtes/réponses
  - [x] Documenter les query parameters
  - [x] Ajouter les codes d'erreur possibles
  - [x] Valider la documentation Swagger UI

- [x] Tester les endpoints API (AC: #1, #2)
  - [x] Tester GET /predictions sans filtres
  - [x] Tester GET /predictions avec pagination
  - [x] Tester GET /predictions avec filtres
  - [x] Tester GET /predictions/{match_id}
  - [x] Valider le format de réponse standardisé
  - [x] Tester les codes d'erreur (404, 400)

## Dev Notes

### Architecture Patterns

**Stack Technique:**
- **API:** FastAPI
- **Validation:** Pydantic
- **Documentation:** OpenAPI 3.1 (Swagger UI)
- **Pagination:** Query parameters (limit, offset)
- **Format:** JSON standardisé `{data, meta}`

### Endpoint Implementation

```python
from fastapi import APIRouter, Query, HTTPException
from typing import Optional

router = APIRouter(prefix="/api/v1", tags=["predictions"])

@router.get("/predictions", response_model=PredictionListResponse)
async def get_predictions(
    limit: int = Query(20, le=100),
    offset: int = Query(0, ge=0),
    team_id: Optional[int] = None,
    league: Optional[str] = None
):
    # Récupérer les prédictions avec pagination et filtres
    predictions = get_predictions_from_db(limit, offset, team_id, league)
    
    # Formatter la réponse standardisée
    return {
        "data": predictions,
        "meta": {
            "total": len(predictions),
            "limit": limit,
            "offset": offset,
            "timestamp": datetime.now().isoformat(),
            "version": "v1"
        }
    }

@router.get("/predictions/{match_id}", response_model=PredictionResponse)
async def get_prediction_by_id(match_id: int):
    prediction = get_prediction_by_id_from_db(match_id)
    
    if not prediction:
        raise HTTPException(status_code=404, detail="Prediction not found")
    
    return {
        "data": prediction,
        "meta": {
            "timestamp": datetime.now().isoformat(),
            "version": "v1"
        }
    }
```

### File Structure

```
backend/app/api/v1/
└── predictions.py
backend/app/schemas/
└── prediction.py
```

### References

- [Source: _bmad-output/planning-artifacts/epics.md#Story-3.4]

## Dev Agent Record

### Agent Model Used

GLM-4.7

### Completion Notes List

- ✅ Schémas Pydantic mis à jour avec MatchInfo et PredictionListMeta
- ✅ Méthodes de service implémentées: get_predictions_with_pagination et get_prediction_with_details
- ✅ Endpoint GET /api/v1/predictions créé avec pagination (limit, offset) et filtres (team_id, league, date_min, date_max)
- ✅ Endpoint GET /api/v1/predictions/match/{match_id} créé avec détails complets (match, énergie, historique)
- ✅ Documentation Swagger complète ajoutée avec exemples de requêtes/réponses
- ✅ Tests unitaires étendus pour couvrir tous les nouveaux cas (TestGetPredictionsEndpoint, TestGetPredictionByMatchIdEndpoint)
- ✅ Réponses standardisées au format {data, meta} avec timestamp et version
- ✅ Optimisation avec jointures SQL et tri par date de match

### File List

- `backend/app/api/v1/predictions.py` (modifié)
- `backend/app/schemas/prediction.py` (modifié)
- `backend/app/services/prediction_service.py` (modifié)
- `backend/tests/test_prediction_api.py` (modifié)
