sahges-sdk 0.1.15
SDK Python officiel de l'écosystème SAHGES
License: MIT
Requires Python: >=3.10
Keywords: sahges, sdk, api, authentication, documents

# SAHGES SDK

SDK Python officiel pour l'écosystème SAHGES. Facilite l'intégration avec les services SAHGES via une interface Python simple et robuste avec types stricts.

[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)

## 📋 Prérequis

- Python 3.10 ou supérieur
- pip pour l'installation des dépendances

## 🚀 Installation

```bash
pip install sahges-sdk
```

Ou depuis les sources :

```bash
git clone https://gitlab.com/florianogomez/sahges-sdk.git
cd sahges-sdk
pip install -e .
```

## 🎯 Démarrage rapide

### Authentification

```python
from sahges_sdk.auth import SahgesAuthClient

# Initialiser le client
client = SahgesAuthClient(
    client_id="votre_client_id",
    client_secret="votre_client_secret"
)

# Se connecter - retourne un objet SahgesLoginResponse
response = client.login(payload={
    "credential": "user@example.com",
    "password": "mot_de_passe"
})

# ✅ Accès par attribut (dataclass)
print(f"Token: {response.access_token}")
print(f"Email: {response.user.email}")
print(f"Nom: {response.user.first_name} {response.user.last_name}")
print(f"Organisation: {response.user.organization.name}")
print(f"Rôle: {response.user.role}")
```

### Gestion de documents

```python
from sahges_sdk.docs import SahgesDocsClient
from pathlib import Path

# Initialiser le client
docs = SahgesDocsClient(
    client_id="votre_client_id",
    client_secret="votre_client_secret"
)

# Uploader un document - retourne un objet SahgesDocument
document = docs.create(
    title="Mon document",
    file_path=Path("document.pdf"),
    visibility="ORGANIZATION",
    status="DRAFT"
)

# ✅ Accès par attribut
print(f"ID: {document.id}")
print(f"Titre: {document.title}")
print(f"Taille: {document.file_size} bytes")
print(f"Format: {document.file_format}")
print(f"Créé le: {document.created_at}")

# Lister les documents - retourne List[SahgesDocumentListItem]
documents = docs.list(payload={"status": "VALIDATED"})

for doc in documents:
    print(f"- {doc.title} ({doc.file_format})")
```

## ⚙️ Configuration

### Variables d'environnement

Créez un fichier `.env` :

```env
# Credentials API (obligatoire)
SAHGES_CLIENT_ID=votre_client_id
SAHGES_CLIENT_SECRET=votre_client_secret

# URLs des services (optionnel)
SAHGES_AUTHENTICATION_BASE_URL=https://api.sahges.com
SAHGES_DOCS_BASE_URL=https://docs.sahges.com
```

### Utilisation avec .env

```python
import os
from dotenv import load_dotenv
from sahges_sdk.auth import SahgesAuthClient

load_dotenv()

client = SahgesAuthClient(
    client_id=os.getenv('SAHGES_CLIENT_ID'),
    client_secret=os.getenv('SAHGES_CLIENT_SECRET')
)
```

## 📖 Guide d'utilisation

### Module Auth - Authentification

Le module Auth fournit toutes les fonctionnalités d'authentification et de gestion de session.

#### 1. Connexion (Login)

```python
from sahges_sdk.auth import SahgesAuthClient

client = SahgesAuthClient(
    client_id="votre_client_id",
    client_secret="votre_client_secret"
)

# Se connecter
response = client.login(payload={
    "credential": "user@example.com",  # Email ou téléphone
    "password": "votre_mot_de_passe"
})

# SahgesLoginResponse est une dataclass avec:
# - access_token: str
# - refresh_token: Optional[str]
# - user: AuthUser

print(f"Token d'accès: {response.access_token}")
print(f"Token de rafraîchissement: {response.refresh_token}")
print(f"Utilisateur: {response.user.email}")
```

#### 2. Rafraîchissement de token (Refresh)

```python
# Rafraîchir le token sans redemander le mot de passe
new_tokens = client.refresh(payload={
    "refresh_token": response.refresh_token
})

# SahgesRefreshResponse est identique à SahgesLoginResponse
print(f"Nouveau token: {new_tokens.access_token}")
print(f"Utilisateur: {new_tokens.user.email}")  # Inclus dans la réponse
```

#### 3. Introspection

```python
# Obtenir les informations de l'utilisateur connecté
user = client.introspect(access_token=response.access_token)

# AuthUser est une dataclass avec:
# - id: UUID
# - email: str
# - first_name: str
# - last_name: str
# - phone: Optional[str]
# - is_active: bool
# - is_using_default_password: bool
# - role: SahgesAuthUserRoleEnum (USER, ADMIN, SUPERADMIN)
# - organization: SahgesAuthOrganization

print(f"ID: {user.id}")
print(f"Email: {user.email}")
print(f"Nom complet: {user.first_name} {user.last_name}")
print(f"Téléphone: {user.phone}")
print(f"Actif: {user.is_active}")
print(f"Rôle: {user.role}")
print(f"Organisation: {user.organization.name}")
print(f"Type org: {user.organization.type}")
```

#### 4. Déconnexion (Logout)

```python
# Se déconnecter - retourne True en cas de succès
result = client.logout(access_token=response.access_token)

print(result)  # True

# En cas d'erreur, lève SahgesAuthenticationError
```

#### 5. Mot de passe oublié (Forgot Password)

```python
# Demander un lien de réinitialisation par email
response = client.forgot_password(payload={
    "credential": "user@example.com"  # Email ou téléphone
})

# SahgesForgotPasswordResponse contient:
# - message: str
print(response.message)  # "Email de réinitialisation envoyé"
```

#### 6. Valider le token de réinitialisation

```python
# Vérifier que le token reçu par email est valide
token = "token_reçu_par_email"

challenge = client.reset_password_challenge(token=token)

# SahgesResetPasswordChallengeResponse contient:
# - valid: bool
# - message: Optional[str]

if challenge.valid:
    print("Token valide, vous pouvez réinitialiser")
else:
    print(f"Token invalide: {challenge.message}")
```

#### 7. Réinitialiser le mot de passe

```python
# Changer le mot de passe avec le token valide
result = client.reset_password(payload={
    "token": token,
    "new_password": "nouveau_mot_de_passe_securise",
    "new_password_confirmation": "nouveau_mot_de_passe_securise"
})

# SahgesResetPasswordResponse contient:
# - user: SahgesResetPasswordUser (version simplifiée)
# - redirect_url: Optional[str]

print(f"Mot de passe changé pour: {result.user.id}")
if result.redirect_url:
    print(f"Redirection vers: {result.redirect_url}")
```

#### Cycle de vie complet d'une session

```python
from sahges_sdk.auth import SahgesAuthClient

client = SahgesAuthClient(client_id="...", client_secret="...")

# 1. Connexion
login = client.login({"credential": "user@example.com", "password": "pass"})
access_token = login.access_token
refresh_token = login.refresh_token

# 2. Utiliser le token pour les requêtes...
user = client.introspect(access_token=access_token)
print(f"Connecté: {user.email}")

# 3. Rafraîchir avant expiration
refreshed = client.refresh({"refresh_token": refresh_token})
access_token = refreshed.access_token
refresh_token = refreshed.refresh_token

# 4. Déconnexion
client.logout(access_token=access_token)
```

### Module Docs - Gestion documentaire

Le module Docs gère l'upload, le stockage et le partage de documents avec traitement IA.

#### 1. Créer un document (Upload)

```python
from sahges_sdk.docs import SahgesDocsClient
from pathlib import Path

docs = SahgesDocsClient(client_id="...", client_secret="...")

# Créer avec upload de fichier
document = docs.create(
    title="Contrat client XYZ",
    file_path=Path("/path/to/contrat.pdf"),
    description="Contrat commercial 2025",
    visibility="ORGANIZATION",  # PRIVATE, ORGANIZATION, PUBLIC
    status="DRAFT",              # DRAFT, PENDING, VALIDATED, ARCHIVED
    category="legal",
    tags=["contrat", "2025", "client-xyz"]
)

# SahgesDocument est une dataclass complète avec:
# - id: UUID
# - title: str
# - visibility: SahgesDocumentVisibilityEnum
# - status: SahgesDocumentStatusEnum
# - file_name: str
# - file_size: int
# - file_mime_type: str
# - file_format: SahgesDocumentFileFormatEnum
# - sha256_hash: str
# - preview_generated: bool
# - created_at: datetime
# - updated_at: datetime
# + 15+ champs optionnels (description, tags, ocr_text, ai_summary, etc.)

print(f"SahgesDocument créé: {document.id}")
print(f"Fichier: {document.file_name} ({document.file_size} bytes)")
print(f"Format: {document.file_format}")
print(f"Hash SHA-256: {document.sha256_hash}")

# Vérifier les résultats du traitement IA
if document.ocr_text:
    print(f"Texte OCR disponible: {len(document.ocr_text)} caractères")
if document.ai_summary:
    print(f"Résumé IA: {document.ai_summary}")
if document.ai_metadata:
    print(f"Métadonnées IA: {document.ai_metadata}")
```

#### 2. Lister les documents

```python
# Liste simple - retourne List[SahgesDocumentListItem]
documents = docs.list(payload={})

for doc in documents:
    # SahgesDocumentListItem est une version simplifiée
    print(f"- {doc.title}")
    print(f"  ID: {doc.id}")
    print(f"  Format: {doc.file_format}")
    print(f"  Taille: {doc.file_size} bytes")
    print(f"  Créé: {doc.created_at}")

# Avec filtres
documents = docs.list(payload={
    "page": 1,
    "search": "contrat",           # Recherche textuelle
    "visibility": "ORGANIZATION",   # Filtre par visibilité
    "status": "VALIDATED",          # Filtre par statut
    "category": "legal",            # Filtre par catégorie
    "owner_only": True,             # Mes documents uniquement
    "shared_with_me": False         # Exclure documents partagés
})

print(f"Trouvé {len(documents)} document(s)")
```

#### 3. Récupérer un document complet

```python
# Obtenir tous les détails d'un document
document = docs.find(payload={
    "document_id": "550e8400-e29b-41d4-a716-446655440000"
})

# SahgesDocument complet avec tous les champs
print(f"Titre: {document.title}")
print(f"Description: {document.description}")
print(f"Catégorie: {document.category}")
print(f"Tags: {document.tags}")
print(f"Propriétaire: {document.owner_auth_user_id}")
print(f"Organisation: {document.organization_id}")
print(f"Métadonnées: {document.document_metadata}")

# Traitement IA
if document.ocr_text:
    print(f"OCR: {document.ocr_text[:200]}...")
if document.audio_transcription:
    print(f"Transcription: {document.audio_transcription[:200]}...")
```

#### 4. Mettre à jour un document

```python
# Modifier les métadonnées d'un document
updated = docs.update(payload={
    "document_id": document.id,
    "title": "Nouveau titre",
    "description": "Nouvelle description",
    "status": "VALIDATED",
    "category": "finance",
    "tags": ["important", "2025"]
})

print(f"Mis à jour: {updated.title}")

# Changer uniquement la visibilité
updated = docs.update_visibility(payload={
    "document_id": document.id,
    "visibility": "PUBLIC"
})

print(f"Nouvelle visibilité: {updated.visibility}")
```

#### 5. Télécharger un document

```python
# Télécharger vers un fichier
docs.download(
    document_id=document.id,
    output_path="/path/to/save/document.pdf"
)

# Ou obtenir le contenu en bytes
file_bytes = docs.download(document_id=document.id)
# Traiter les bytes directement...
print(f"Téléchargé {len(file_bytes)} bytes")
```

#### 6. Supprimer un document

```python
# Supprimer définitivement
result = docs.delete(document_id=document.id)

print(result)  # True

# En cas d'erreur, lève SahgesRequestError
```

#### 7. Partager un document

```python
from datetime import datetime, timedelta

# Créer un partage avec permissions
share = docs.share_create(payload={
    "document_id": document.id,
    "shared_with_auth_user_id": "user-uuid-du-collegue",
    "permission": "VIEW",  # VIEW, EDIT, MANAGE
    "expires_at": datetime.now() + timedelta(days=30)
})

# SahgesDocumentShareCreateResponse contient:
# - id: UUID
# - document_id: UUID
# - shared_with_auth_user_id: UUID
# - shared_by_auth_user_id: UUID
# - permission: SahgesDocumentSharePermissionEnum
# - created_at: datetime
# - expires_at: Optional[datetime]

print(f"Partage créé: {share.id}")
print(f"Permission: {share.permission}")
print(f"Expire le: {share.expires_at}")
```

#### 8. Gérer les partages

```python
# Lister tous les partages d'un document
shares = docs.share_list(payload={
    "document_id": document.id
})

# List[SahgesDocumentShare]
for share in shares:
    print(f"Partagé avec: {share.shared_with.email}")
    print(f"  Permission: {share.permission}")
    print(f"  Par: {share.shared_by.email}")
    print(f"  Le: {share.created_at}")

# Révoquer un partage
if shares:
    result = docs.share_delete(payload={
        "document_id": document.id,
        "share_id": shares[0].id
    })
    print(result)  # True
```

#### 9. Endpoints clients (accès restreint)

Pour les applications tierces avec permissions limitées :

```python
# Liste (ORGANIZATION + PUBLIC uniquement)
documents = docs.clients_list(payload={
    "search": "facture",
    "category": "comptabilite"
})

# Créer (visibilité forcée à ORGANIZATION)
document = docs.clients_create(
    title="Facture janvier",
    file_path=Path("facture.pdf"),
    status="VALIDATED"
)

# Récupérer
doc = docs.clients_find(payload={"document_id": document.id})

# Télécharger
docs.clients_download(
    document_id=document.id,
    output_path="facture_downloaded.pdf"
)
```

### Module Auth - Gestion des Clients API

Le module Auth permet également de gérer les clients API (applications tierces).

#### 1. Lister les clients

```python
from sahges_sdk.auth import SahgesAuthClient

client = SahgesAuthClient(client_id="...", client_secret="...")

# Liste avec filtres
response = client.list_clients(payload={
    "page": 1,
    "per_page": 20,
    "is_active": True,
    "organization_id": "uuid-org"
})

# SahgesClientListResponse contient:
# - total: int
# - page: int
# - per_page: int
# - data: List[SahgesClient]

print(f"Total: {response.total} clients")
for api_client in response.data:
    print(f"- {api_client.name} ({api_client.client_id})")
    print(f"  Actif: {api_client.is_active}")
    print(f"  Redirect URI: {api_client.redirect_uri}")
```

#### 2. Créer un client

```python
# Créer un nouveau client API
new_client = client.create_client(payload={
    "name": "Mon Application Mobile",
    "redirect_uri": "https://mon-app.com/callback",
    "organization_id": "uuid-org"  # optionnel
})

# SahgesClient contient:
# - client_id: str (UUID)
# - secret: str (généré automatiquement)
# - name: str
# - redirect_uri: Optional[str]
# - is_active: bool
# - organization_id: Optional[UUID]
# - created_at: datetime

print(f"Client créé: {new_client.client_id}")
print(f"Secret: {new_client.secret}")  # À sauvegarder immédiatement !
```

#### 3. Récupérer et mettre à jour

```python
# Récupérer un client
api_client = client.find_client(client_id="uuid-here")

# Mettre à jour
updated = client.update_client(
    client_id=api_client.client_id,
    payload={
        "name": "Nouveau nom",
        "redirect_uri": "https://new-url.com/callback"
    }
)

# Activer/Désactiver
activated = client.activate_client(client_id=api_client.client_id)
deactivated = client.deactivate_client(client_id=api_client.client_id)

# Réinitialiser le secret
new_secret = client.reset_client_secret(client_id=api_client.client_id)
print(f"Nouveau secret: {new_secret.secret}")

# Supprimer
result = client.delete_client(client_id=api_client.client_id)
print(result)  # True
```

### Module Auth - Gestion des Utilisateurs

Gestion complète des utilisateurs avec CRUD et permissions.

#### 1. Lister les utilisateurs

```python
# Liste avec filtres
response = client.list_users(payload={
    "page": 1,
    "per_page": 50,
    "is_active": True,
    "role": "USER",  # USER, ADMIN, SUPERADMIN
    "organization_id": "uuid-org"
})

# SahgesUserListResponse contient:
# - total: int
# - data: List[SahgesUser]

for user in response.data:
    print(f"{user.first_name} {user.last_name} ({user.email})")
    print(f"  Rôle: {user.role}, Organisation: {user.organization.name}")
```

#### 2. Créer un utilisateur

```python
# Créer un nouvel utilisateur
new_user = client.create_user(payload={
    "email": "john.doe@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "phone": "+22912345678",
    "role": "USER",
    "organization_id": "uuid-org"
})

# SahgesUser contient tous les champs (id, email, first_name, etc.)
print(f"Utilisateur créé: {new_user.id}")
print(f"Mot de passe par défaut: utilisé={new_user.is_using_default_password}")
```

#### 3. Gérer les utilisateurs

```python
# Récupérer un utilisateur
user = client.find_user(user_id="uuid-here")

# Récupérer l'utilisateur actuellement connecté
current = client.get_current_user(access_token="token")

# Mettre à jour
updated = client.update_user(
    user_id=user.id,
    payload={
        "first_name": "Jane",
        "phone": "+22987654321"
    }
)

# Activer/Désactiver
activated = client.activate_user(user_id=user.id)
deactivated = client.deactivate_user(user_id=user.id)

# Réinitialiser le mot de passe (envoyer email avec nouveau mdp)
reset = client.reset_user(user_id=user.id)

# Supprimer
result = client.delete_user(user_id=user.id)
```

#### 4. Routes client (accès restreint)

Les clients API peuvent gérer les utilisateurs selon leurs permissions :

```python
# Liste pour le client (limité à son organisation)
users = client.client_list_users(payload={"is_active": True})

# Créer (permissions configurables)
new_user = client.client_create_user(payload={
    "email": "user@example.com",
    "first_name": "John",
    "last_name": "Doe"
})

# Récupérer et mettre à jour
user = client.client_find_user(user_id="uuid")
updated = client.client_update_user(
    user_id=user.id,
    payload={"first_name": "Jane"}
)

# Réinitialiser
reset = client.client_reset_user(user_id=user.id)
```

### Module Auth - Gestion des Organisations

Gestion hiérarchique des organisations (INTERNAL, EXTERNAL, PARTNER).

#### 1. Lister et créer

```python
# Lister avec filtres
response = client.list_organizations(payload={
    "page": 1,
    "type": "PARTNER",  # INTERNAL, EXTERNAL, PARTNER
    "is_active": True
})

# SahgesOrganizationListResponse contient:
# - total: int
# - data: List[SahgesOrganization]

for org in response.data:
    print(f"{org.name} ({org.type})")
    if org.parent_organization_id:
        print(f"  Parent: {org.parent_organization_id}")

# Créer une organisation
new_org = client.create_organization(payload={
    "name": "ACME Corporation",
    "type": "PARTNER",
    "description": "Organisation partenaire",
    "parent_organization_id": "uuid-parent"  # optionnel
})

print(f"Organisation créée: {new_org.id}")
```

#### 2. Gérer les organisations

```python
# Récupérer
org = client.find_organization(organization_id="uuid-here")

# Mettre à jour
updated = client.update_organization(
    organization_id=org.id,
    payload={
        "name": "Nouveau nom",
        "description": "Nouvelle description"
    }
)

# Activer/Désactiver
activated = client.activate_organization(organization_id=org.id)
deactivated = client.deactivate_organization(organization_id=org.id)

# Supprimer
result = client.delete_organization(organization_id=org.id)
```

### Module Auth - Gestion des Notifications

Gestion complète des notifications avec envoi email/SMS.

#### 1. Envoyer une notification

```python
# Envoyer une notification à un utilisateur
notification = client.send_notification(payload={
    "to_user_id": "uuid-user",
    "summary": "Vous avez un nouveau message",
    "category": "INFO",  # INFO, SUCCESS, WARNING, ERROR
    "send_email": True,
    "send_sms": False,
    "details": "Message détaillé optionnel"
})

# SahgesNotification contient:
# - id: UUID
# - to_user: SahgesNotificationUser
# - summary: str
# - category: str
# - is_read: bool
# - send_email: bool
# - created_at: datetime

print(f"Notification envoyée: {notification.id}")
```

#### 2. Lister et consulter

```python
# Lister les notifications (requiert access_token utilisateur)
response = client.list_notifications(
    access_token="user_token",
    payload={
        "page": 1,
        "is_read": False,  # Seulement les non lues
        "category": "INFO"
    }
)

# SahgesNotificationListResponse contient:
# - total: int
# - data: List[SahgesNotification]

print(f"{response.total} notification(s)")
for notif in response.data:
    print(f"- {notif.summary} ({notif.category})")
    print(f"  Lu: {notif.is_read}, Le: {notif.created_at}")

# Récupérer une notification
notification = client.find_notification(
    access_token="user_token",
    notification_id="uuid-here"
)
```

#### 3. Marquer comme lu

```python
# Marquer une notification comme lue
read_notif = client.read_notification(
    access_token="user_token",
    notification_id="uuid-here"
)

print(f"Lue: {read_notif.is_read}")  # True

# Marquer toutes comme lues
result = client.read_all_notifications(access_token="user_token")
print(result)  # True

# Compter les non lues
count = client.count_unread_notifications(access_token="user_token")
print(f"Non lues: {count.count}")
```

## 🏗️ Architecture du SDK

### Structure des modules

```
src/sahges_sdk/
├── auth/              # Module d'authentification
│   ├── auth_client.py # Client avec 40+ méthodes
│   ├── types.py       # Types dataclass (SahgesLoginResponse, SahgesClient, etc.)
│   ├── routes.py      # Définition des routes API
│   ├── enums.py       # Énumérations (rôles, types org...)
│   └── [login|logout|clients|users|organizations|notifications]/
├── docs/              # Module de gestion documentaire
│   ├── docs_client.py # Client avec 10+ méthodes
│   ├── types.py       # Types dataclass (SahgesDocument, etc.)
│   ├── enums.py       # Énumérations (visibilité, statut...)
│   ├── routes.py      # Définition des routes API
│   └── [documents|shares|clients]/
├── base/              # Classes de base partagées
│   ├── client.py      # BaseSahgesApiClient (HTTP)
│   ├── endpoint.py    # Définition des endpoints
│   ├── decorators.py  # @sahges_endpoint pour validation
│   ├── enums.py       # SahgesAuthUserRoleEnum, etc.
│   ├── error.py       # Exceptions personnalisées
│   └── logger.py      # Configuration logging
└── config/            # Configuration centralisée
```

### Types et dataclasses

Le SDK utilise **marshmallow-dataclass** pour générer automatiquement :
- Des dataclasses Python avec types stricts
- Des schémas Marshmallow pour validation
- Autocomplétion IDE complète
- Type checking statique (mypy)

```python
# Exemple de type généré automatiquement
@dataclass
class SahgesLoginResponse:
    """Type pour la réponse de login"""
    access_token: str
    user: AuthUser
    refresh_token: Optional[str] = None
    
    class Meta:
        unknown = EXCLUDE  # Ignore les champs inconnus

# Utilisation
response = client.login(...)
print(response.access_token)  # ✅ Accès par attribut
print(response.user.email)    # ✅ Navigation type-safe
```

### Énumérations

```python
from sahges_sdk.base.enums import SahgesAuthUserRoleEnum, SahgesAuthOrganizationTypeEnum
from sahges_sdk.docs.enums import (
    SahgesDocumentVisibilityEnum,
    SahgesDocumentStatusEnum,
    SahgesDocumentSharePermissionEnum,
    SahgesDocumentFileFormatEnum
)

# Valeurs possibles
SahgesAuthUserRoleEnum          # USER, ADMIN, SUPERADMIN
SahgesAuthOrganizationTypeEnum  # INTERNAL, EXTERNAL, PARTNER

SahgesDocumentVisibilityEnum       # PRIVATE, ORGANIZATION, PUBLIC, SHARED
SahgesDocumentStatusEnum           # DRAFT, ACTIVE, ARCHIVED, DELETED
SahgesDocumentSharePermissionEnum  # VIEW, COMMENT, EDIT, MANAGE
SahgesDocumentFileFormatEnum       # PDF, DOCX, XLSX, PNG, JPG, MP4, etc.
```

### Types disponibles

Tous les types suivants sont disponibles à l'import depuis `sahges_sdk.auth` et `sahges_sdk.docs` :

**Types Auth:**
```python
from sahges_sdk.auth import (
    # Authentification
    SahgesLoginResponse,
    SahgesRefreshResponse,
    SahgesAuthUser,
    SahgesForgotPasswordResponse,
    SahgesResetPasswordResponse,
    SahgesResetPasswordChallengeResponse,
    
    # Clients API
    SahgesClient,
    SahgesClientListResponse,
    SahgesClientSecretResponse,
    
    # Utilisateurs
    SahgesUser,
    SahgesUserListResponse,
    
    # Organisations
    SahgesOrganization,
    SahgesOrganizationListResponse,
    
    # Notifications
    SahgesNotification,
    SahgesNotificationListResponse,
    SahgesNotificationCountResponse,
    SahgesNotificationUser,
    
    # Authentification Client
    SahgesClientAuthResponse,
    SahgesClientIntrospectResponse
)
```

**Types Docs:**
```python
from sahges_sdk.docs import (
    SahgesDocument,
    SahgesDocumentListItem,
    SahgesDocumentListResponse,
    SahgesDocumentShare,
    SahgesDocumentShareCreateResponse
)
```

## 🔒 Gestion des erreurs

Le SDK fournit des exceptions spécifiques héritant de `SahgesError` :

```python
from sahges_sdk.base.error import (
    SahgesError,                 # Erreur de base
    SahgesClientConfigError,     # Configuration invalide
    SahgesRequestError,          # Erreur HTTP générale
    SahgesAuthenticationError,   # Erreur d'authentification (401/403)
    SahgesValidationError        # Erreur de validation de données
)

# Gestion complète des erreurs
try:
    response = client.login(payload={
        "credential": "user@example.com",
        "password": "wrong_password"
    })
    print(f"Connecté: {response.user.email}")
    
except SahgesAuthenticationError as e:
    # Erreur 401/403
    print(f"Authentification échouée: {e}")
    print(f"Status: {e.status_code}")
    print(f"Réponse: {e.response_data}")
    
except SahgesValidationError as e:
    # Données invalides
    print(f"Erreur de validation: {e}")
    
except SahgesRequestError as e:
    # Autre erreur HTTP (400, 404, 500...)
    print(f"Erreur HTTP: {e}")
    print(f"Status: {e.status_code}")
    
except SahgesClientConfigError as e:
    # client_id ou client_secret manquant
    print(f"Configuration incorrecte: {e}")
    
except SahgesError as e:
    # Erreur SAHGES générique
    print(f"Erreur SAHGES: {e}")
```

### Codes HTTP et exceptions

| Code HTTP | Exception | Description |
|-----------|-----------|-------------|
| 400 | `SahgesValidationError` | Données invalides |
| 401 | `SahgesAuthenticationError` | Non authentifié |
| 403 | `SahgesAuthenticationError` | Permissions insuffisantes |
| 404 | `SahgesRequestError` | Ressource introuvable |
| 429 | `SahgesRequestError` | Rate limit dépassé |
| 500 | `SahgesRequestError` | Erreur serveur |

## 🧪 Tests

Le projet comprend 33 tests unitaires + 16 tests manuels.

### Lancer les tests

```bash
# Tous les tests
pytest tests/ -v

# Tests avec couverture
pytest --cov=src tests/
pytest --cov=src --cov-report=html tests/

# Tests spécifiques
pytest tests/test_auth_login.py -v
pytest tests/test_auth_complete.py -v
pytest tests/test_docs_complete.py -v

# Tests manuels (nécessitent vraie API)
python tests/test_manual_login.py
python tests/test_manual_docs.py
```

### Utilisation du script manage.sh

```bash
# Lancer les tests
./manage.sh test

# Statistiques du projet
./manage.sh stats

# Publier sur PyPI
./manage.sh publish pypi
```

### Configuration pour tests

Créez un fichier `.env.test` :

```env
SAHGES_CLIENT_ID=test_client_id
SAHGES_CLIENT_SECRET=test_client_secret
SAHGES_TEST_EMAIL=test@example.com
SAHGES_TEST_PASSWORD=test_password
```

## 📝 Développement

### Installation développeur

```bash
git clone https://gitlab.com/florianogomez/sahges-sdk.git
cd sahges-sdk
python -m venv venv
source venv/bin/activate  # ou venv\Scripts\activate sur Windows
pip install -e ".[dev]"
```

### Dépendances

**Production:**
- `httpx>=0.27` - Client HTTP moderne
- `marshmallow>=3.20` - Validation et sérialisation
- `marshmallow-dataclass>=8.6` - Génération auto dataclass + schéma

**Développement:**
- `pytest` - Framework de tests
- `pytest-cov` - Couverture de code
- `python-dotenv` - Variables d'environnement

### Conventions de code

- **Python 3.10+** : Type hints modernes (`str | None` au lieu de `Union[str, None]`)
- **Dataclasses** : Pour tous les types de réponse
- **Marshmallow** : Pour validation des schémas
- **Docstrings** : Format Google pour toutes les fonctions publiques
- **Type hints** : Obligatoires partout
- **Exceptions** : Utiliser les exceptions spécifiques du SDK

### Ajouter une nouvelle fonctionnalité

Exemple pour le module Auth :

```python
# 1. Créer la fonction (src/sahges_sdk/auth/new_feature/feature.py)
from sahges_sdk.base.decorators import sahges_endpoint
from sahges_sdk.auth.routes import SahgesAuthenticationRoutes
from sahges_sdk.auth.types import NewFeatureResponse

@sahges_endpoint(
    request_schema=NewFeatureRequestSchema,
    response_schema=NewFeatureResponseSchema
)
def sahges_auth_new_feature(self, payload: dict) -> NewFeatureResponse:
    """Description de la fonctionnalité."""
    endpoint = SahgesAuthenticationRoutes.new_feature.value
    response = self.request(
        method=endpoint.method,
        path=endpoint.path,
        json=payload
    )
    return response

# 2. Définir le type de réponse (src/sahges_sdk/auth/types.py)
from marshmallow_dataclass import dataclass
from marshmallow import EXCLUDE

@dataclass
class NewFeatureResponse:
    """Type pour la réponse de new_feature"""
    result: str
    success: bool
    
    class Meta:
        unknown = EXCLUDE

# 3. Ajouter la route (src/sahges_sdk/auth/routes.py)
class SahgesAuthenticationRoutes(Enum):
    new_feature = Endpoint(
        path=f"/{BASE}/new-feature",
        method=HTTPMethod.POST
    )

# 4. Attacher au client (src/sahges_sdk/auth/auth_client.py)
from types import MethodType
from sahges_sdk.auth.types import NewFeatureResponse

class SahgesAuthClient(BaseSahgesApiClient):
    def __init__(self, client_id, client_secret):
        super().__init__(...)
        from sahges_sdk.auth.new_feature.feature import sahges_auth_new_feature
        self.new_feature = MethodType(sahges_auth_new_feature, self)
    
    def new_feature(self, payload: Dict[str, Any]) -> NewFeatureResponse:
        """
        Description publique de la fonctionnalité
        
        Args:
            payload: Paramètres de la requête
            
        Returns:
            NewFeatureResponse: Résultat de l'opération
        """
        # Implémentation liée dans __init__
        pass

# 5. Écrire les tests (tests/test_new_feature.py)
def test_new_feature_success():
    client = SahgesAuthClient("test_id", "test_secret")
    # Mock et assertions...
```

## 🤝 Contribution

Les contributions sont les bienvenues ! Processus :

1. Fork le projet
2. Créer une branche (`git checkout -b feature/AmazingFeature`)
3. Committer les changements (`git commit -m 'Add AmazingFeature'`)
4. Pusher vers la branche (`git push origin feature/AmazingFeature`)
5. Ouvrir une Pull Request

### Checklist avant PR

- [ ] Tests passent (`./manage.sh test`)
- [ ] Type hints ajoutés partout
- [ ] Docstrings présentes (format Google)
- [ ] Types dataclass pour les réponses
- [ ] Gestion d'erreurs appropriée
- [ ] CHANGELOG.md mis à jour

## 📄 Licence

Ce projet est sous licence MIT. Voir [LICENSE](LICENSE) pour détails.

## 📧 Contact

SAHGES - floriano.gomez@bj.sanlamallianz.com

Repository : https://gitlab.com/florianogomez/sahges-sdk

## 🔗 Liens utiles

- [SahgesDocumentation Auth complète](src/sahges_sdk/auth/readme.md)
- [SahgesDocumentation Docs complète](src/sahges_sdk/docs/readme.md)
- [Exemple d'utilisation](EXAMPLE_USAGE.py)
- [Guide de migration](MIGRATION_MARSHMALLOW_DATACLASS.md)
- [CHANGELOG](CHANGELOG.md)

## ❓ FAQ

### Comment obtenir des credentials API ?

Contactez floriano.gomez@bj.sanlamallianz.com pour obtenir `client_id` et `client_secret`.

### Quelle est la durée de vie des tokens ?

- **Access token** : 15 minutes
- **Refresh token** : 7 jours

### Pourquoi marshmallow-dataclass au lieu de TypedDict ?

- ✅ **Accès par attribut** : `response.access_token` au lieu de `response["access_token"]`
- ✅ **Validation automatique** : Les données invalides lèvent des erreurs
- ✅ **Autocomplétion IDE** : L'IDE connaît tous les attributs
- ✅ **Type checking** : mypy peut vérifier les types
- ✅ **Schémas réutilisables** : Partage avec le backend SAHGES

### Comment convertir un objet en dict ?

```python
from marshmallow_dataclass import class_schema

# Obtenir le schéma
schema = class_schema(type(response))()

# Convertir en dict
response_dict = schema.dump(response)
```

### Quels formats de fichiers sont supportés ?

Tous les formats. Traitement spécial pour :
- **PDF** : OCR et extraction de texte
- **Images** : OCR et analyse IA
- **Audio/Vidéo** : Transcription automatique
- **Office** : Conversion et prévisualisation

### Comment gérer les fichiers volumineux ?

Le système gère automatiquement les uploads en streaming. Vérifiez les limites avec votre administrateur.

### Puis-je utiliser le SDK en production ?

Oui, le SDK est conçu pour la production avec :
- Gestion d'erreurs robuste
- Logging approprié
- Validation stricte des données
- Tests complets (33 tests unitaires)
- Support Python 3.10+

## 📚 Exemples complets

### Workflow Auth + Docs combiné

```python
from sahges_sdk.auth import SahgesAuthClient
from sahges_sdk.docs import SahgesDocsClient
from pathlib import Path

# 1. Authentification
auth = SahgesAuthClient(client_id="...", client_secret="...")

login = auth.login({
    "credential": "user@example.com",
    "password": "password"
})

print(f"Connecté: {login.user.first_name} {login.user.last_name}")
print(f"Organisation: {login.user.organization.name}")
print(f"Rôle: {login.user.role}")

# 2. Gestion documentaire
docs = SahgesDocsClient(client_id="...", client_secret="...")

# Créer un document
doc = docs.create(
    title="Rapport mensuel",
    file_path=Path("rapport.pdf"),
    visibility="ORGANIZATION",
    status="VALIDATED",
    category="reporting",
    tags=["mensuel", "2025", "janvier"]
)

print(f"SahgesDocument créé: {doc.id}")
print(f"Fichier: {doc.file_name} ({doc.file_size} bytes)")

# Lister mes documents
my_docs = docs.list({"owner_only": True})
print(f"Vous avez {len(my_docs)} document(s)")

# Partager avec un collègue
from datetime import datetime, timedelta

share = docs.share_create({
    "document_id": doc.id,
    "shared_with_auth_user_id": "colleague-uuid",
    "permission": "VIEW",
    "expires_at": datetime.now() + timedelta(days=7)
})

print(f"Partagé avec permission {share.permission}")

# 3. Déconnexion
auth.logout(access_token=login.access_token)
print("Session terminée")
```

### Recherche et téléchargement

```python
from sahges_sdk.docs import SahgesDocsClient

docs = SahgesDocsClient(client_id="...", client_secret="...")

# Recherche avancée
results = docs.list({
    "search": "contrat client",
    "category": "legal",
    "status": "VALIDATED",
    "visibility": "ORGANIZATION"
})

print(f"Trouvé {len(results)} document(s)")

# Télécharger le premier résultat
if results:
    doc = results[0]
    print(f"Téléchargement de: {doc.title}")
    
    docs.download(
        document_id=doc.id,
        output_path=f"/tmp/{doc.file_name}"
    )
    
    print(f"Téléchargé dans /tmp/{doc.file_name}")
```

### Gestion des erreurs avancée

```python
from sahges_sdk.auth import SahgesAuthClient
from sahges_sdk.base.error import (
    SahgesAuthenticationError,
    SahgesValidationError,
    SahgesRequestError
)

client = SahgesAuthClient(client_id="...", client_secret="...")

try:
    response = client.login({
        "credential": "user@example.com",
        "password": "wrong_password"
    })
    
except SahgesAuthenticationError as e:
    if e.status_code == 401:
        print("Identifiants incorrects")
    elif e.status_code == 403:
        print("Compte désactivé ou accès refusé")
    else:
        print(f"Erreur d'authentification: {e}")
        
except SahgesValidationError as e:
    print(f"Données invalides: {e}")
    # Exemple : email mal formaté, mot de passe trop court
    
except SahgesRequestError as e:
    if e.status_code == 429:
        print("Trop de tentatives, réessayez plus tard")
    elif e.status_code >= 500:
        print("Erreur serveur, réessayez plus tard")
    else:
        print(f"Erreur HTTP {e.status_code}: {e}")
        
except Exception as e:
    print(f"Erreur inattendue: {e}")
```

---

**Version actuelle : 0.1.9**

Dernière mise à jour : Janvier 2026