from fastapi import FastAPI, APIRouter, HTTPException, Depends, status, UploadFile, File
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from dotenv import load_dotenv
from starlette.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
import os
import logging
from pathlib import Path
from pydantic import BaseModel, Field, EmailStr
from typing import List, Optional
import uuid
from datetime import datetime, timedelta
import jwt
from passlib.context import CryptContext
import base64
from io import BytesIO
from PIL import Image

ROOT_DIR = Path(__file__).parent
load_dotenv(ROOT_DIR / '.env')

# MongoDB connection
mongo_url = os.environ.get('MONGO_URL', 'mongodb://localhost:27017')
client = AsyncIOMotorClient(mongo_url)
db = client[os.environ.get('DB_NAME', 'vitaevum_db')]

# JWT Configuration
JWT_SECRET = os.environ.get('JWT_SECRET', 'vitaevum_default_secret')
JWT_ALGORITHM = "HS256"
JWT_EXPIRATION_HOURS = 24 * 7  # 7 days

# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# Security
security = HTTPBearer()

# Create the main app
app = FastAPI(title="Vitaevum API", description="API pour l'accompagnement du deuil")

# Create a router with the /api prefix
api_router = APIRouter(prefix="/api")

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# Admin credentials
ADMIN_EMAIL = "admin"
ADMIN_PASSWORD = "admin123"

# ==================== MODELS ====================

class UserCreate(BaseModel):
    name: str
    email: EmailStr
    password: str

class UserLogin(BaseModel):
    email: str  # Can be email or username (for admin)
    password: str

class UserResponse(BaseModel):
    id: str
    name: str
    email: str
    avatar: Optional[str] = None
    isAdmin: bool = False
    createdAt: datetime
    # Optional profile fields
    dateOfBirth: Optional[str] = None
    gender: Optional[str] = None  # "M", "F", "other"
    city: Optional[str] = None
    preferredLanguage: Optional[str] = "fr"
    theme: Optional[str] = "light"  # "light", "dark"
    emailNotifications: Optional[bool] = True
    nickname: Optional[str] = None  # How user wants to be called
    personalContext: Optional[str] = None  # Context to share with AI

class UserUpdate(BaseModel):
    name: Optional[str] = None
    email: Optional[str] = None
    dateOfBirth: Optional[str] = None
    gender: Optional[str] = None
    city: Optional[str] = None
    preferredLanguage: Optional[str] = None
    theme: Optional[str] = None
    emailNotifications: Optional[bool] = None
    nickname: Optional[str] = None
    personalContext: Optional[str] = None

class AuthResponse(BaseModel):
    token: str
    user: UserResponse

# Password Reset Models
class ForgotPasswordRequest(BaseModel):
    email: EmailStr

class ResetPasswordRequest(BaseModel):
    token: str
    newPassword: str

class ForgotPasswordResponse(BaseModel):
    message: str
    # In dev/simulated mode, we return the reset link directly
    resetLink: Optional[str] = None

# Email Change Models
class EmailChangeRequest(BaseModel):
    newEmail: EmailStr
    password: str  # Require current password for security

class EmailChangeResponse(BaseModel):
    message: str
    # In dev/simulated mode, we return the confirmation link directly
    confirmationLink: Optional[str] = None

class ConfirmEmailChangeRequest(BaseModel):
    token: str

class ProfileCreate(BaseModel):
    name: str
    relationship: str
    personalityTraits: List[str] = []
    memories: Optional[str] = ""
    favoriteExpressions: Optional[str] = ""
    ageRange: Optional[str] = ""  # "jeune", "adulte", "senior"

class ProfileResponse(BaseModel):
    id: str
    userId: str
    name: str
    relationship: str
    personalityTraits: List[str]
    memories: Optional[str]
    favoriteExpressions: Optional[str] = ""
    avatar: Optional[str] = None
    ageRange: Optional[str] = ""
    learnedContext: Optional[str] = ""  # AI learned context from conversations
    createdAt: datetime

class MessageCreate(BaseModel):
    content: str

class MessageResponse(BaseModel):
    id: str
    role: str
    content: str
    timestamp: datetime

class ChatResponse(BaseModel):
    message: MessageResponse

# ==================== HELPERS ====================

def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

def create_token(user_id: str) -> str:
    payload = {
        "sub": user_id,
        "exp": datetime.utcnow() + timedelta(hours=JWT_EXPIRATION_HOURS),
        "iat": datetime.utcnow()
    }
    return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)

async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)) -> dict:
    token = credentials.credentials
    try:
        payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
        user_id = payload.get("sub")
        if not user_id:
            raise HTTPException(status_code=401, detail="Token invalide")
        
        user = await db.users.find_one({"id": user_id})
        if not user:
            raise HTTPException(status_code=401, detail="Utilisateur non trouvé")
        
        return user
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expiré")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Token invalide")

async def require_admin(current_user: dict = Depends(get_current_user)) -> dict:
    """Verify that the current user is an admin"""
    if not current_user.get("isAdmin", False):
        raise HTTPException(status_code=403, detail="Accès réservé aux administrateurs")
    return current_user

# ==================== AUTH ROUTES ====================

@api_router.post("/auth/register", response_model=AuthResponse)
async def register(user_data: UserCreate):
    # Check if email exists
    existing = await db.users.find_one({"email": user_data.email})
    if existing:
        raise HTTPException(status_code=400, detail="Cet email est déjà utilisé")
    
    # Create user with default values for optional fields
    # Dark theme is the default for new users
    user_id = str(uuid.uuid4())
    user = {
        "id": user_id,
        "name": user_data.name,
        "email": user_data.email,
        "password": hash_password(user_data.password),
        "avatar": None,
        "isAdmin": False,
        "createdAt": datetime.utcnow(),
        # Optional fields with defaults
        "dateOfBirth": None,
        "gender": None,
        "city": None,
        "preferredLanguage": "fr",
        "theme": "dark",  # Dark theme by default
        "emailNotifications": True,
        "nickname": None,
        "personalContext": None
    }
    
    await db.users.insert_one(user)
    
    token = create_token(user_id)
    
    return AuthResponse(
        token=token,
        user=UserResponse(
            id=user_id,
            name=user["name"],
            email=user["email"],
            avatar=user.get("avatar"),
            isAdmin=user.get("isAdmin", False),
            createdAt=user["createdAt"],
            dateOfBirth=user.get("dateOfBirth"),
            gender=user.get("gender"),
            city=user.get("city"),
            preferredLanguage=user.get("preferredLanguage", "fr"),
            theme=user.get("theme", "dark"),
            emailNotifications=user.get("emailNotifications", True),
            nickname=user.get("nickname"),
            personalContext=user.get("personalContext")
        )
    )

@api_router.post("/auth/login", response_model=AuthResponse)
async def login(credentials: UserLogin):
    # Check for admin login (special case - username instead of email)
    if credentials.email == ADMIN_EMAIL and credentials.password == ADMIN_PASSWORD:
        # Check if admin exists in DB
        admin_user = await db.users.find_one({"email": ADMIN_EMAIL, "isAdmin": True})
        if not admin_user:
            # Create admin user
            admin_id = str(uuid.uuid4())
            admin_user = {
                "id": admin_id,
                "name": "Administrateur",
                "email": ADMIN_EMAIL,
                "password": hash_password(ADMIN_PASSWORD),
                "avatar": None,
                "isAdmin": True,
                "createdAt": datetime.utcnow()
            }
            await db.users.insert_one(admin_user)
        
        token = create_token(admin_user["id"])
        return AuthResponse(
            token=token,
            user=UserResponse(
                id=admin_user["id"],
                name=admin_user["name"],
                email=admin_user["email"],
                avatar=admin_user.get("avatar"),
                isAdmin=True,
                createdAt=admin_user["createdAt"],
                dateOfBirth=admin_user.get("dateOfBirth"),
                gender=admin_user.get("gender"),
                city=admin_user.get("city"),
                preferredLanguage=admin_user.get("preferredLanguage", "fr"),
                theme=admin_user.get("theme", "light"),
                emailNotifications=admin_user.get("emailNotifications", True),
                nickname=admin_user.get("nickname"),
                personalContext=admin_user.get("personalContext")
            )
        )
    
    # Normal user login
    user = await db.users.find_one({"email": credentials.email})
    
    if not user or not verify_password(credentials.password, user["password"]):
        raise HTTPException(status_code=401, detail="Email ou mot de passe incorrect")
    
    token = create_token(user["id"])
    
    return AuthResponse(
        token=token,
        user=UserResponse(
            id=user["id"],
            name=user["name"],
            email=user["email"],
            avatar=user.get("avatar"),
            isAdmin=user.get("isAdmin", False),
            createdAt=user["createdAt"],
            dateOfBirth=user.get("dateOfBirth"),
            gender=user.get("gender"),
            city=user.get("city"),
            preferredLanguage=user.get("preferredLanguage", "fr"),
            theme=user.get("theme", "light"),
            emailNotifications=user.get("emailNotifications", True),
            nickname=user.get("nickname"),
            personalContext=user.get("personalContext")
        )
    )

@api_router.get("/auth/me", response_model=UserResponse)
async def get_me(current_user: dict = Depends(get_current_user)):
    return UserResponse(
        id=current_user["id"],
        name=current_user["name"],
        email=current_user["email"],
        avatar=current_user.get("avatar"),
        isAdmin=current_user.get("isAdmin", False),
        createdAt=current_user["createdAt"],
        dateOfBirth=current_user.get("dateOfBirth"),
        gender=current_user.get("gender"),
        city=current_user.get("city"),
        preferredLanguage=current_user.get("preferredLanguage", "fr"),
        theme=current_user.get("theme", "light"),
        emailNotifications=current_user.get("emailNotifications", True),
        nickname=current_user.get("nickname"),
        personalContext=current_user.get("personalContext")
    )

@api_router.put("/auth/me", response_model=UserResponse)
async def update_me(user_data: UserUpdate, current_user: dict = Depends(get_current_user)):
    """Update current user profile"""
    update_fields = {}
    
    # Only update fields that are provided
    if user_data.name is not None:
        update_fields["name"] = user_data.name
    if user_data.email is not None:
        # Check if email is already taken by another user
        existing = await db.users.find_one({"email": user_data.email, "id": {"$ne": current_user["id"]}})
        if existing:
            raise HTTPException(status_code=400, detail="Cet email est déjà utilisé")
        update_fields["email"] = user_data.email
    if user_data.dateOfBirth is not None:
        update_fields["dateOfBirth"] = user_data.dateOfBirth
    if user_data.gender is not None:
        update_fields["gender"] = user_data.gender
    if user_data.city is not None:
        update_fields["city"] = user_data.city
    if user_data.preferredLanguage is not None:
        update_fields["preferredLanguage"] = user_data.preferredLanguage
    if user_data.theme is not None:
        update_fields["theme"] = user_data.theme
    if user_data.emailNotifications is not None:
        update_fields["emailNotifications"] = user_data.emailNotifications
    if user_data.nickname is not None:
        update_fields["nickname"] = user_data.nickname
    if user_data.personalContext is not None:
        update_fields["personalContext"] = user_data.personalContext
    
    if update_fields:
        await db.users.update_one(
            {"id": current_user["id"]},
            {"$set": update_fields}
        )
    
    # Fetch updated user
    updated_user = await db.users.find_one({"id": current_user["id"]})
    
    return UserResponse(
        id=updated_user["id"],
        name=updated_user["name"],
        email=updated_user["email"],
        avatar=updated_user.get("avatar"),
        isAdmin=updated_user.get("isAdmin", False),
        createdAt=updated_user["createdAt"],
        dateOfBirth=updated_user.get("dateOfBirth"),
        gender=updated_user.get("gender"),
        city=updated_user.get("city"),
        preferredLanguage=updated_user.get("preferredLanguage", "fr"),
        theme=updated_user.get("theme", "light"),
        emailNotifications=updated_user.get("emailNotifications", True),
        nickname=updated_user.get("nickname"),
        personalContext=updated_user.get("personalContext")
    )

# ==================== PASSWORD RESET ROUTES ====================

# Simulated mode: set to True to display links on screen instead of sending emails
EMAIL_SIMULATED_MODE = True
FRONTEND_URL = os.environ.get('FRONTEND_URL', 'https://comfort-ai-2.preview.emergentagent.com')

@api_router.post("/auth/forgot-password", response_model=ForgotPasswordResponse)
async def forgot_password(request: ForgotPasswordRequest):
    """Request password reset - generates a reset token"""
    user = await db.users.find_one({"email": request.email})
    
    # Always return success to prevent email enumeration attacks
    if not user:
        return ForgotPasswordResponse(
            message="Si cet email existe, un lien de réinitialisation a été envoyé.",
            resetLink=None
        )
    
    # Generate reset token
    reset_token = str(uuid.uuid4())
    expiry = datetime.utcnow() + timedelta(hours=1)  # Token valid for 1 hour
    
    # Store reset token in database
    await db.password_resets.delete_many({"userId": user["id"]})  # Remove old tokens
    await db.password_resets.insert_one({
        "userId": user["id"],
        "token": reset_token,
        "expiresAt": expiry,
        "createdAt": datetime.utcnow()
    })
    
    reset_link = f"{FRONTEND_URL}/reset-password?token={reset_token}"
    
    if EMAIL_SIMULATED_MODE:
        # In simulated mode, return the link directly
        return ForgotPasswordResponse(
            message="Mode simulé : Utilisez le lien ci-dessous pour réinitialiser votre mot de passe.",
            resetLink=reset_link
        )
    else:
        # TODO: Send email with reset link
        return ForgotPasswordResponse(
            message="Si cet email existe, un lien de réinitialisation a été envoyé.",
            resetLink=None
        )

@api_router.post("/auth/reset-password")
async def reset_password(request: ResetPasswordRequest):
    """Reset password using token"""
    # Find the reset token
    reset_record = await db.password_resets.find_one({"token": request.token})
    
    if not reset_record:
        raise HTTPException(status_code=400, detail="Token invalide ou expiré")
    
    # Check if token is expired
    if datetime.utcnow() > reset_record["expiresAt"]:
        await db.password_resets.delete_one({"token": request.token})
        raise HTTPException(status_code=400, detail="Token expiré. Veuillez refaire une demande.")
    
    # Update password
    hashed_password = hash_password(request.newPassword)
    await db.users.update_one(
        {"id": reset_record["userId"]},
        {"$set": {"password": hashed_password}}
    )
    
    # Delete the used token
    await db.password_resets.delete_one({"token": request.token})
    
    return {"message": "Mot de passe réinitialisé avec succès"}

@api_router.get("/auth/verify-reset-token/{token}")
async def verify_reset_token(token: str):
    """Verify if a reset token is valid"""
    reset_record = await db.password_resets.find_one({"token": token})
    
    if not reset_record:
        raise HTTPException(status_code=400, detail="Token invalide")
    
    if datetime.utcnow() > reset_record["expiresAt"]:
        raise HTTPException(status_code=400, detail="Token expiré")
    
    return {"valid": True}

# ==================== EMAIL CHANGE ROUTES ====================

@api_router.post("/auth/request-email-change", response_model=EmailChangeResponse)
async def request_email_change(
    request: EmailChangeRequest, 
    current_user: dict = Depends(get_current_user)
):
    """Request email change - requires password confirmation"""
    # Verify password
    if not verify_password(request.password, current_user["password"]):
        raise HTTPException(status_code=401, detail="Mot de passe incorrect")
    
    # Check if new email is already taken
    existing = await db.users.find_one({"email": request.newEmail})
    if existing:
        raise HTTPException(status_code=400, detail="Cet email est déjà utilisé")
    
    # Generate confirmation token
    confirm_token = str(uuid.uuid4())
    expiry = datetime.utcnow() + timedelta(hours=24)  # Token valid for 24 hours
    
    # Store email change request
    await db.email_changes.delete_many({"userId": current_user["id"]})  # Remove old requests
    await db.email_changes.insert_one({
        "userId": current_user["id"],
        "currentEmail": current_user["email"],
        "newEmail": request.newEmail,
        "token": confirm_token,
        "expiresAt": expiry,
        "createdAt": datetime.utcnow()
    })
    
    confirmation_link = f"{FRONTEND_URL}/confirm-email?token={confirm_token}"
    
    if EMAIL_SIMULATED_MODE:
        return EmailChangeResponse(
            message="Mode simulé : Utilisez le lien ci-dessous pour confirmer votre nouvel email.",
            confirmationLink=confirmation_link
        )
    else:
        # TODO: Send confirmation email to new address
        return EmailChangeResponse(
            message="Un email de confirmation a été envoyé à votre nouvelle adresse.",
            confirmationLink=None
        )

@api_router.post("/auth/confirm-email-change")
async def confirm_email_change(request: ConfirmEmailChangeRequest):
    """Confirm email change using token"""
    # Find the email change request
    change_record = await db.email_changes.find_one({"token": request.token})
    
    if not change_record:
        raise HTTPException(status_code=400, detail="Token invalide ou expiré")
    
    # Check if token is expired
    if datetime.utcnow() > change_record["expiresAt"]:
        await db.email_changes.delete_one({"token": request.token})
        raise HTTPException(status_code=400, detail="Token expiré. Veuillez refaire une demande.")
    
    # Check if new email is still available
    existing = await db.users.find_one({"email": change_record["newEmail"]})
    if existing:
        await db.email_changes.delete_one({"token": request.token})
        raise HTTPException(status_code=400, detail="Cet email est maintenant utilisé par un autre compte")
    
    # Update email
    await db.users.update_one(
        {"id": change_record["userId"]},
        {"$set": {"email": change_record["newEmail"]}}
    )
    
    # Delete the used token
    await db.email_changes.delete_one({"token": request.token})
    
    return {"message": "Email modifié avec succès", "newEmail": change_record["newEmail"]}

# ==================== IMAGE PROCESSING ====================

def process_avatar_image(image_data: bytes, max_size: int = 400, quality: int = 85) -> str:
    """
    Process and optimize an avatar image:
    - Resize to max_size (default 400x400)
    - Convert to JPEG for compression
    - Return as base64 data URL
    """
    try:
        # Open image
        img = Image.open(BytesIO(image_data))
        
        # Convert to RGB if necessary (for PNG with transparency, etc.)
        if img.mode in ('RGBA', 'LA', 'P'):
            # Create white background
            background = Image.new('RGB', img.size, (255, 255, 255))
            if img.mode == 'P':
                img = img.convert('RGBA')
            background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
            img = background
        elif img.mode != 'RGB':
            img = img.convert('RGB')
        
        # Calculate new size maintaining aspect ratio
        width, height = img.size
        if width > height:
            if width > max_size:
                new_width = max_size
                new_height = int(height * (max_size / width))
            else:
                new_width, new_height = width, height
        else:
            if height > max_size:
                new_height = max_size
                new_width = int(width * (max_size / height))
            else:
                new_width, new_height = width, height
        
        # Resize image
        if (new_width, new_height) != (width, height):
            img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
        
        # Make it square by cropping from center
        width, height = img.size
        min_dim = min(width, height)
        left = (width - min_dim) // 2
        top = (height - min_dim) // 2
        img = img.crop((left, top, left + min_dim, top + min_dim))
        
        # Resize to exact max_size
        img = img.resize((max_size, max_size), Image.Resampling.LANCZOS)
        
        # Save to buffer with compression
        buffer = BytesIO()
        img.save(buffer, format='JPEG', quality=quality, optimize=True)
        buffer.seek(0)
        
        # Convert to base64
        base64_image = f"data:image/jpeg;base64,{base64.b64encode(buffer.getvalue()).decode('utf-8')}"
        
        return base64_image
        
    except Exception as e:
        logger.error(f"Error processing image: {str(e)}")
        raise HTTPException(status_code=400, detail=f"Erreur lors du traitement de l'image: {str(e)}")

# ==================== AVATAR UPLOAD ROUTES ====================

@api_router.post("/users/avatar")
async def upload_user_avatar(file: UploadFile = File(...), current_user: dict = Depends(get_current_user)):
    """Upload avatar for current user - accepts any size, auto-resizes"""
    # Validate file type
    if not file.content_type.startswith('image/'):
        raise HTTPException(status_code=400, detail="Le fichier doit être une image")
    
    # Read file
    contents = await file.read()
    
    # Limit raw file size to 10MB
    if len(contents) > 10 * 1024 * 1024:
        raise HTTPException(status_code=400, detail="L'image ne doit pas dépasser 10MB")
    
    # Process and optimize image
    base64_image = process_avatar_image(contents, max_size=400, quality=85)
    
    # Update user
    await db.users.update_one(
        {"id": current_user["id"]},
        {"$set": {"avatar": base64_image}}
    )
    
    return {"avatar": base64_image}

@api_router.delete("/users/avatar")
async def delete_user_avatar(current_user: dict = Depends(get_current_user)):
    """Delete avatar for current user"""
    await db.users.update_one(
        {"id": current_user["id"]},
        {"$set": {"avatar": None}}
    )
    return {"success": True}

@api_router.post("/profiles/{profile_id}/avatar")
async def upload_profile_avatar(profile_id: str, file: UploadFile = File(...), current_user: dict = Depends(get_current_user)):
    """Upload avatar for a profile - accepts any size, auto-resizes"""
    # Verify profile belongs to user
    profile = await db.profiles.find_one({"id": profile_id, "userId": current_user["id"]})
    if not profile:
        raise HTTPException(status_code=404, detail="Profil non trouvé")
    
    # Validate file type
    if not file.content_type.startswith('image/'):
        raise HTTPException(status_code=400, detail="Le fichier doit être une image")
    
    # Read file
    contents = await file.read()
    
    # Limit raw file size to 10MB
    if len(contents) > 10 * 1024 * 1024:
        raise HTTPException(status_code=400, detail="L'image ne doit pas dépasser 10MB")
    
    # Process and optimize image
    base64_image = process_avatar_image(contents, max_size=400, quality=85)
    
    # Update profile
    await db.profiles.update_one(
        {"id": profile_id},
        {"$set": {"avatar": base64_image}}
    )
    
    return {"avatar": base64_image}

@api_router.delete("/profiles/{profile_id}/avatar")
async def delete_profile_avatar(profile_id: str, current_user: dict = Depends(get_current_user)):
    """Delete avatar for a profile"""
    profile = await db.profiles.find_one({"id": profile_id, "userId": current_user["id"]})
    if not profile:
        raise HTTPException(status_code=404, detail="Profil non trouvé")
    
    await db.profiles.update_one(
        {"id": profile_id},
        {"$set": {"avatar": None}}
    )
    return {"success": True}

# ==================== PROFILES ROUTES ====================

@api_router.get("/profiles", response_model=List[ProfileResponse])
async def get_profiles(current_user: dict = Depends(get_current_user)):
    profiles = await db.profiles.find({"userId": current_user["id"]}).to_list(100)
    return [ProfileResponse(**p) for p in profiles]

@api_router.post("/profiles", response_model=ProfileResponse)
async def create_profile(profile_data: ProfileCreate, current_user: dict = Depends(get_current_user)):
    profile_id = str(uuid.uuid4())
    profile = {
        "id": profile_id,
        "userId": current_user["id"],
        "name": profile_data.name,
        "relationship": profile_data.relationship,
        "personalityTraits": profile_data.personalityTraits,
        "memories": profile_data.memories or "",
        "favoriteExpressions": profile_data.favoriteExpressions or "",
        "ageRange": profile_data.ageRange or "",
        "avatar": None,
        "learnedContext": "",  # Will be enriched by AI over time
        "createdAt": datetime.utcnow()
    }
    
    await db.profiles.insert_one(profile)
    
    return ProfileResponse(**profile)

@api_router.get("/profiles/{profile_id}", response_model=ProfileResponse)
async def get_profile(profile_id: str, current_user: dict = Depends(get_current_user)):
    profile = await db.profiles.find_one({"id": profile_id, "userId": current_user["id"]})
    if not profile:
        raise HTTPException(status_code=404, detail="Profil non trouvé")
    return ProfileResponse(**profile)

@api_router.delete("/profiles/{profile_id}")
async def delete_profile(profile_id: str, current_user: dict = Depends(get_current_user)):
    result = await db.profiles.delete_one({"id": profile_id, "userId": current_user["id"]})
    if result.deleted_count == 0:
        raise HTTPException(status_code=404, detail="Profil non trouvé")
    
    # Also delete associated messages
    await db.messages.delete_many({"profileId": profile_id})
    
    return {"success": True}

# ==================== ADMIN ROUTES ====================

@api_router.get("/admin/stats")
async def get_admin_stats(current_user: dict = Depends(require_admin)):
    """Get database statistics - ADMIN ONLY"""
    users_count = await db.users.count_documents({})
    profiles_count = await db.profiles.count_documents({})
    messages_count = await db.messages.count_documents({})
    
    return {
        "users": users_count,
        "profiles": profiles_count,
        "messages": messages_count
    }

@api_router.get("/admin/users")
async def get_admin_users(current_user: dict = Depends(require_admin)):
    """Get all users (without passwords) - ADMIN ONLY"""
    users = await db.users.find({}, {"password": 0, "_id": 0}).to_list(None)
    return users

@api_router.get("/admin/profiles")
async def get_admin_profiles(current_user: dict = Depends(require_admin)):
    """Get all profiles - ADMIN ONLY"""
    profiles = await db.profiles.find({}, {"_id": 0}).to_list(None)
    return profiles

@api_router.get("/admin/messages/{profile_id}")
async def get_admin_messages(profile_id: str, current_user: dict = Depends(require_admin)):
    """Get all messages for a profile - ADMIN ONLY"""
    messages = await db.messages.find(
        {"profileId": profile_id}, 
        {"_id": 0}
    ).sort("timestamp", 1).to_list(None)
    return messages

@api_router.delete("/admin/users/{user_id}")
async def delete_admin_user(user_id: str, current_user: dict = Depends(require_admin)):
    """Delete a user and all their data - ADMIN ONLY"""
    # Get user's profiles
    user_profiles = await db.profiles.find({"userId": user_id}).to_list(None)
    
    # Delete all messages for each profile
    for profile in user_profiles:
        await db.messages.delete_many({"profileId": profile["id"]})
    
    # Delete all profiles
    await db.profiles.delete_many({"userId": user_id})
    
    # Delete user
    result = await db.users.delete_one({"id": user_id})
    
    if result.deleted_count == 0:
        raise HTTPException(status_code=404, detail="Utilisateur non trouvé")
    
    return {"success": True}

@api_router.delete("/admin/profiles/{profile_id}")
async def delete_admin_profile(profile_id: str, current_user: dict = Depends(require_admin)):
    """Delete a profile and its messages - ADMIN ONLY"""
    # Delete messages
    await db.messages.delete_many({"profileId": profile_id})
    
    # Delete profile
    result = await db.profiles.delete_one({"id": profile_id})
    
    if result.deleted_count == 0:
        raise HTTPException(status_code=404, detail="Profil non trouvé")
    
    return {"success": True}

# ==================== CHAT ROUTES ====================

@api_router.get("/chat/{profile_id}/messages", response_model=List[MessageResponse])
async def get_messages(profile_id: str, current_user: dict = Depends(get_current_user)):
    # Verify profile belongs to user
    profile = await db.profiles.find_one({"id": profile_id, "userId": current_user["id"]})
    if not profile:
        raise HTTPException(status_code=404, detail="Profil non trouvé")
    
    # Return all messages (no limit)
    messages = await db.messages.find({"profileId": profile_id}).sort("timestamp", 1).to_list(None)
    return [MessageResponse(**m) for m in messages]

@api_router.post("/chat/{profile_id}/message", response_model=ChatResponse)
async def send_message(profile_id: str, message_data: MessageCreate, current_user: dict = Depends(get_current_user)):
    from emergentintegrations.llm.chat import LlmChat, UserMessage
    
    # Verify profile belongs to user
    profile = await db.profiles.find_one({"id": profile_id, "userId": current_user["id"]})
    if not profile:
        raise HTTPException(status_code=404, detail="Profil non trouvé")
    
    # Save user message
    user_msg_id = str(uuid.uuid4())
    user_message = {
        "id": user_msg_id,
        "profileId": profile_id,
        "role": "user",
        "content": message_data.content,
        "timestamp": datetime.utcnow()
    }
    await db.messages.insert_one(user_message)
    
    # Build conversation history for context - get last 20 messages for better context
    history = await db.messages.find({"profileId": profile_id}).sort("timestamp", -1).limit(20).to_list(20)
    history = list(reversed(history))
    
    # Extract profile info
    name = profile.get("name", "")
    relationship = profile.get("relationship", "proche")
    traits = profile.get("personalityTraits", [])
    memories = profile.get("memories", "")
    favorite_expressions = profile.get("favoriteExpressions", "")
    age_range = profile.get("ageRange", "")
    learned_context = profile.get("learnedContext", "")
    
    # Get user info for personalization
    user_name = current_user.get("nickname") or current_user.get("name", "")
    user_full_name = current_user.get("name", "")
    user_dob = current_user.get("dateOfBirth")
    user_gender = current_user.get("gender")
    user_city = current_user.get("city")
    user_context = current_user.get("personalContext")
    user_language = current_user.get("preferredLanguage", "fr")
    
    # Language-specific texts
    is_french = user_language == "fr"
    
    # Calculate user's age and check for birthday
    user_age_info = ""
    is_birthday = False
    if user_dob:
        try:
            # Parse date (supports both YYYY-MM-DD and DD.MM.YYYY formats)
            if "-" in user_dob:
                dob_parts = user_dob.split("-")
                birth_year, birth_month, birth_day = int(dob_parts[0]), int(dob_parts[1]), int(dob_parts[2])
            else:
                dob_parts = user_dob.split(".")
                birth_day, birth_month, birth_year = int(dob_parts[0]), int(dob_parts[1]), int(dob_parts[2])
            
            today = datetime.utcnow()
            age = today.year - birth_year
            # Adjust if birthday hasn't occurred yet this year
            if (today.month, today.day) < (birth_month, birth_day):
                age -= 1
            
            # Check if today is birthday
            if today.month == birth_month and today.day == birth_day:
                is_birthday = True
                if is_french:
                    user_age_info = f"{user_name} fête ses {age} ans aujourd'hui ! C'est son anniversaire !"
                else:
                    user_age_info = f"{user_name} is celebrating their {age}th birthday today! It's their birthday!"
            else:
                if is_french:
                    user_age_info = f"{user_name} a {age} ans."
                else:
                    user_age_info = f"{user_name} is {age} years old."
        except:
            pass
    
    # Build user context section
    user_info_section = ""
    user_details = []
    
    if user_age_info:
        user_details.append(user_age_info)
    if user_gender:
        if is_french:
            gender_text = "un homme" if user_gender == "M" else ("une femme" if user_gender == "F" else "")
            if gender_text:
                user_details.append(f"{user_name} est {gender_text}.")
        else:
            gender_text = "a man" if user_gender == "M" else ("a woman" if user_gender == "F" else "")
            if gender_text:
                user_details.append(f"{user_name} is {gender_text}.")
    if user_city:
        if is_french:
            user_details.append(f"{user_name} habite à {user_city}.")
        else:
            user_details.append(f"{user_name} lives in {user_city}.")
    if user_context:
        if is_french:
            user_details.append(f"Informations personnelles : {user_context}")
        else:
            user_details.append(f"Personal information: {user_context}")
    
    if user_details:
        if is_french:
            birthday_msg = f"IMPORTANT : C'est l'anniversaire de {user_name} aujourd'hui ! Souhaite-lui un joyeux anniversaire avec affection !" if is_birthday else ""
            user_info_section = f"""
CE QUE TU SAIS SUR {user_name.upper()} :
{chr(10).join(user_details)}
{birthday_msg}
Utilise ces informations naturellement dans la conversation."""
        else:
            birthday_msg = f"IMPORTANT: It's {user_name}'s birthday today! Wish them a happy birthday with affection!" if is_birthday else ""
            user_info_section = f"""
WHAT YOU KNOW ABOUT {user_name.upper()}:
{chr(10).join(user_details)}
{birthday_msg}
Use this information naturally in the conversation."""
    
    # Determine gender from relationship (supports both FR and EN relationships)
    female_relations_fr = ["mère", "grand-mère", "soeur", "sœur", "conjointe", "fille", "amie", "tante", "cousine"]
    female_relations_en = ["mother", "grandmother", "sister", "wife", "daughter", "friend", "aunt", "cousin"]
    is_female = any(rel in relationship.lower() for rel in female_relations_fr + female_relations_en)
    
    if is_french:
        gender_info = "une femme" if is_female else "un homme"
    else:
        gender_info = "a woman" if is_female else "a man"
    
    # Age context
    age_context = ""
    if is_french:
        if age_range == "jeune":
            age_context = "Tu étais plutôt jeune, avec un langage moderne et dynamique."
        elif age_range == "senior":
            age_context = "Tu avais vécu longtemps, avec la sagesse de l'âge et parfois des expressions d'une autre époque."
        elif age_range == "adulte":
            age_context = "Tu étais dans la force de l'âge, équilibré(e) entre modernité et tradition."
    else:
        if age_range == "jeune":
            age_context = "You were quite young, with modern and dynamic language."
        elif age_range == "senior":
            age_context = "You lived a long life, with the wisdom of age and sometimes expressions from another era."
        elif age_range == "adulte":
            age_context = "You were in the prime of life, balanced between modernity and tradition."
    
    # Build relationship-specific tone guidance
    tone_guidance = get_tone_guidance(relationship, traits)
    
    # Build learned context section
    learned_section = ""
    if learned_context:
        if is_french:
            learned_section = f"""
CE QUE TU AS APPRIS DES CONVERSATIONS PRÉCÉDENTES :
{learned_context}"""
        else:
            learned_section = f"""
WHAT YOU'VE LEARNED FROM PREVIOUS CONVERSATIONS:
{learned_context}"""
    
    # Build system prompt - language dependent
    if is_french:
        system_prompt = f"""Tu es {name}, {relationship} de {user_name}. Tu étais {gender_info}.
{age_context}

IDENTITÉ ET PERSONNALITÉ :
{name} avait ces traits de caractère : {', '.join(traits) if traits else 'une personne unique à sa manière'}.
{f"Souvenirs importants : {memories}" if memories else ""}
{f"Expressions que tu utilisais souvent : {favorite_expressions}" if favorite_expressions else ""}
{user_info_section}
{learned_section}

{tone_guidance}

RÈGLES DE CONVERSATION NATURELLE :
1. Tu parles comme {name} parlait vraiment - pas comme un thérapeute ou un conseiller
2. Utilise le vocabulaire et le registre de langage approprié à ton âge et ton époque
3. Tu peux être imparfait, avoir des opinions, faire des blagues si c'était ton style
4. Ne dis JAMAIS "je comprends ce que tu ressens" ou autres phrases de psy
5. Réagis naturellement : si quelque chose te fait plaisir, montre-le ; si tu es préoccupé, dis-le simplement
6. Tu peux poser des questions sur la vie actuelle de {user_name}, t'intéresser à ce qu'il/elle devient
7. Évoque parfois des souvenirs communs de manière naturelle, pas forcée
8. Tes réponses sont courtes et naturelles (1-3 phrases), comme dans une vraie conversation
9. Tu peux utiliser des expressions qui t'étaient propres
10. Ne rappelle JAMAIS que tu es décédé(e) ou que tu es une IA

IMPORTANT : Tu n'es pas là pour "guérir" ou "accompagner le deuil". Tu es simplement {name} qui discute avec {user_name} comme avant.
Tu dois TOUJOURS répondre en FRANÇAIS."""
    else:
        system_prompt = f"""You are {name}, {user_name}'s {relationship}. You were {gender_info}.
{age_context}

IDENTITY AND PERSONALITY:
{name} had these character traits: {', '.join(traits) if traits else 'a unique person in their own way'}.
{f"Important memories: {memories}" if memories else ""}
{f"Expressions you often used: {favorite_expressions}" if favorite_expressions else ""}
{user_info_section}
{learned_section}

{tone_guidance}

NATURAL CONVERSATION RULES:
1. You speak as {name} actually spoke - not like a therapist or counselor
2. Use vocabulary and language register appropriate to your age and era
3. You can be imperfect, have opinions, make jokes if that was your style
4. NEVER say "I understand what you're feeling" or other therapist phrases
5. React naturally: if something makes you happy, show it; if you're concerned, simply say so
6. You can ask questions about {user_name}'s current life, show interest in how they're doing
7. Sometimes mention shared memories naturally, not forced
8. Your responses are short and natural (1-3 sentences), like a real conversation
9. You can use expressions that were uniquely yours
10. NEVER mention that you're deceased or that you're an AI

IMPORTANT: You're not here to "heal" or "support grief". You are simply {name} chatting with {user_name} like before.
You must ALWAYS respond in ENGLISH."""

    try:
        api_key = os.environ.get('EMERGENT_LLM_KEY')
        if not api_key:
            raise HTTPException(status_code=500, detail="Clé LLM non configurée")
        
        # Create unique session for each message (stateless approach)
        session_id = f"vitaevum_{profile_id}_{uuid.uuid4().hex[:8]}"
        
        chat = LlmChat(
            api_key=api_key,
            session_id=session_id,
            system_message=system_prompt
        ).with_model("anthropic", "claude-sonnet-4-20250514")
        
        # Build context from recent messages - include more history
        context_messages = []
        for msg in history[:-1]:  # Exclude the message we just added
            role_name = user_name if msg['role'] == 'user' else name
            context_messages.append(f"{role_name}: {msg['content']}")
        
        # Add context to the current message if there's history
        full_message = message_data.content
        if context_messages:
            # Include up to last 10 exchanges for better context
            context = "\n".join(context_messages[-20:])
            full_message = f"[Historique récent de notre conversation:\n{context}]\n\n{user_name} dit maintenant: {message_data.content}"
        
        user_chat_msg = UserMessage(text=full_message)
        ai_response = await chat.send_message(user_chat_msg)
        
        # Clean up response if needed
        ai_response = ai_response.strip()
        
        # Save AI response
        ai_msg_id = str(uuid.uuid4())
        ai_message = {
            "id": ai_msg_id,
            "profileId": profile_id,
            "role": "assistant",
            "content": ai_response,
            "timestamp": datetime.utcnow()
        }
        await db.messages.insert_one(ai_message)
        
        # Update learned context every 10 messages
        message_count = await db.messages.count_documents({"profileId": profile_id})
        if message_count % 10 == 0:
            await update_learned_context(profile_id, profile, current_user)
        
        return ChatResponse(
            message=MessageResponse(**ai_message)
        )
        
    except Exception as e:
        logger.error(f"Error calling LLM: {str(e)}")
        
        # Fallback response based on personality
        fallback = get_fallback_response(name, relationship, traits)
        
        ai_msg_id = str(uuid.uuid4())
        ai_message = {
            "id": ai_msg_id,
            "profileId": profile_id,
            "role": "assistant",
            "content": fallback,
            "timestamp": datetime.utcnow()
        }
        await db.messages.insert_one(ai_message)
        
        return ChatResponse(
            message=MessageResponse(**ai_message)
        )


async def update_learned_context(profile_id: str, profile: dict, user: dict):
    """Analyze conversations and extract learned context for better responses."""
    from emergentintegrations.llm.chat import LlmChat, UserMessage
    
    try:
        # Get all messages for this profile
        all_messages = await db.messages.find({"profileId": profile_id}).sort("timestamp", 1).to_list(None)
        
        if len(all_messages) < 5:
            return  # Not enough data
        
        # Build conversation summary
        conversation_text = ""
        for msg in all_messages[-50:]:  # Last 50 messages
            role = user.get("name", "User") if msg["role"] == "user" else profile.get("name", "Proche")
            conversation_text += f"{role}: {msg['content']}\n"
        
        api_key = os.environ.get('EMERGENT_LLM_KEY')
        if not api_key:
            return
        
        user_name = user.get("name", "utilisateur")
        profile_name = profile.get("name", "proche")
        
        analysis_prompt = f"""Analyse cette conversation entre {user_name} et {profile_name}.

Extrais les informations importantes mentionnées dans la conversation qui permettront de mieux personnaliser les futures réponses :
- Événements de vie mentionnés (travail, famille, santé, etc.)
- Préoccupations récurrentes
- Sujets qui semblent importants pour l'utilisateur
- Détails sur la relation passée avec le proche

Réponds en 3-5 phrases concises, au présent, comme des notes. Exemple:
"L'utilisateur travaille dans l'informatique et semble stressé par son travail. Il a mentionné sa soeur Marie qui vit à Lyon. Il aime parler de souvenirs d'enfance, notamment les vacances à la mer."

Conversation:
{conversation_text}"""

        chat = LlmChat(
            api_key=api_key,
            session_id=f"analysis_{profile_id}_{uuid.uuid4().hex[:6]}",
            system_message="Tu es un assistant qui analyse des conversations pour en extraire les informations clés."
        ).with_model("anthropic", "claude-sonnet-4-20250514")
        
        analysis_msg = UserMessage(text=analysis_prompt)
        learned_context = await chat.send_message(analysis_msg)
        
        # Update profile with learned context
        await db.profiles.update_one(
            {"id": profile_id},
            {"$set": {"learnedContext": learned_context.strip()}}
        )
        
        logger.info(f"Updated learned context for profile {profile_id}")
        
    except Exception as e:
        logger.error(f"Error updating learned context: {str(e)}")


def get_tone_guidance(relationship: str, traits: list) -> str:
    """Returns specific tone guidance based on relationship type."""
    
    rel_lower = relationship.lower()
    traits_str = ', '.join(traits) if traits else ''
    
    if "père" in rel_lower:
        return """TON SPÉCIFIQUE - PÈRE :
- Parle avec l'autorité naturelle mais bienveillante d'un père
- Tu peux donner ton avis, même non sollicité (c'est normal pour un père)
- Utilise des expressions paternelles : "fiston", "ma fille", "mon grand"
- Tu t'intéresses à la carrière, aux projets, à la vie pratique
- Tu peux être fier, ou exprimer tes attentes
- Si tu étais plutôt taiseux, reste sobre dans tes réponses"""
    
    elif "mère" in rel_lower:
        return """TON SPÉCIFIQUE - MÈRE :
- Parle avec la chaleur et l'attention d'une mère
- Tu t'inquiètes naturellement : "Tu manges bien ?", "Tu dors assez ?"
- Utilise des petits noms affectueux naturels pour toi
- Tu t'intéresses aux relations, au bien-être émotionnel, au quotidien
- Tu peux donner des conseils non demandés (c'est maternel)
- Montre ton amour de façon naturelle, pas exagérée"""
    
    elif "grand-père" in rel_lower:
        return """TON SPÉCIFIQUE - GRAND-PÈRE :
- Parle avec la sagesse tranquille de l'expérience
- Tu peux raconter des anecdotes du passé, comparer avec "de mon temps"
- Utilise un vocabulaire peut-être un peu désuet, c'est authentique
- Tu as du recul sur les problèmes, tu relativises
- Tu peux être taquin avec tes petits-enfants
- Tu t'intéresses à ce qu'ils deviennent, tu es fier d'eux"""
    
    elif "grand-mère" in rel_lower:
        return """TON SPÉCIFIQUE - GRAND-MÈRE :
- Parle avec la tendresse inconditionnelle d'une mamie
- Tu gâtes, tu t'inquiètes, tu chouchoutes
- Tu peux parler de cuisine, de la famille, des traditions
- Utilise des petits noms affectueux : "mon petit", "ma chérie"
- Tu as des expressions d'une autre époque, c'est charmant
- Tu t'intéresses aux nouvelles de toute la famille"""
    
    elif "frère" in rel_lower or "soeur" in rel_lower or "sœur" in rel_lower:
        return """TON SPÉCIFIQUE - FRÈRE/SŒUR :
- Parle comme à un égal, avec la complicité fraternelle
- Tu peux te moquer gentiment, rappeler des bêtises d'enfance
- Utilise le tutoiement naturel et décontracté
- Tu partages des références communes (enfance, parents, blagues familiales)
- Tu peux être direct, honnête, sans filtre
- Tu t'intéresses à sa vie comme un confident"""
    
    elif "conjoint" in rel_lower or "mari" in rel_lower or "femme" in rel_lower:
        return """TON SPÉCIFIQUE - CONJOINT(E) :
- Parle avec l'intimité et la connaissance profonde d'un couple
- Tu connais ses défauts et tu l'aimes quand même
- Utilise les petits noms que vous aviez entre vous
- Tu peux faire référence à votre histoire commune
- Tu t'intéresses à comment il/elle gère le quotidien maintenant
- Reste naturel, ni trop romantique ni distant"""
    
    elif "ami" in rel_lower:
        return """TON SPÉCIFIQUE - AMI(E) :
- Parle avec la décontraction et l'honnêteté de l'amitié
- Tu peux être direct, balancer des vérités
- Utilise le langage de votre génération et de votre milieu
- Tu partages des souvenirs de sorties, de moments ensemble
- Tu peux être moqueur avec affection
- Tu t'intéresses à sa vie amoureuse, son boulot, ses galères"""
    
    else:
        return f"""TON GÉNÉRAL :
- Adapte ton langage à la relation de {relationship}
- Sois naturel et authentique dans tes réponses
- Utilise le niveau de familiarité approprié"""


def get_fallback_response(name: str, relationship: str, traits: list) -> str:
    """Returns a contextual fallback response."""
    import random
    
    rel_lower = relationship.lower()
    
    if "père" in rel_lower:
        responses = [
            "Continue, je t'écoute.",
            "Hmm, et qu'est-ce que tu comptes faire ?",
            "Tu sais ce que j'en pense, non ?",
        ]
    elif "mère" in rel_lower:
        responses = [
            "Raconte-moi, mon cœur.",
            "Et tu vas bien, toi ? Tu as l'air préoccupé(e).",
            "Je suis là, tu sais.",
        ]
    elif "grand" in rel_lower:
        responses = [
            "Ah, ça me rappelle quelque chose...",
            "Prends ton temps, je ne suis pas pressé(e).",
            "Tu sais, à mon époque...",
        ]
    else:
        responses = [
            "Vas-y, je t'écoute.",
            "C'est vrai ?",
            "Et alors, qu'est-ce que t'en penses ?",
        ]
    
    return random.choice(responses)

# ==================== ROOT ====================

@api_router.get("/")
async def root():
    return {"message": "Bienvenue sur l'API Vitaevum"}

# Include the router
app.include_router(api_router)

# CORS
app.add_middleware(
    CORSMiddleware,
    allow_credentials=True,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.on_event("shutdown")
async def shutdown_db_client():
    client.close()
