from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils import timezone
from django.utils.text import slugify
from django.utils import timezone
from datetime import timedelta
import secrets
from django.core.validators import FileExtensionValidator
import qrcode
from io import BytesIO
from django.core.files import File
import uuid
from django.contrib.sites.models import Site
from django.conf import settings

# ==================== USER MODEL ====================




class User(AbstractUser):
    """Utilisateur personnalisé avec rôles"""
    ROLE_CHOICES = [
        ('visitor', 'Visiteur'),
        ('content_moderator', 'Modérateur Contenu'),
        ('museum_admin', 'Administrateur Musée'),
    ]

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='visitor')
    date_joined = models.DateTimeField(auto_now_add=True)
    is_active = models.BooleanField(default=False)
    email_verified = models.BooleanField(default=False)
    preferred_language = models.CharField(max_length=5, default='fr')

    class Meta:
        verbose_name = 'Utilisateur'
        verbose_name_plural = 'Utilisateurs'

    def authenticate(self):
        """Méthode d'authentification"""
        return self.is_active

    def get_role(self):
        """Retourne le rôle de l'utilisateur"""
        return self.role

    def send_activation_email(self):
        """Envoie l'email d'activation"""
        from django.core.mail import send_mail
        from django.template.loader import render_to_string
        from django.conf import settings

        # Créer le token
        activation_token = ActivationToken.create_for_user(self)

        # DEBUG
        print(f"Token créé: {activation_token.token}")
        print(f"Expire le: {activation_token.expires_at}")

        # URL d'activation
        activation_url = f"{settings.SITE_URL}/activate/{activation_token.token}/"

        # DEBUG
        print(f"URL d'activation: {activation_url}")

        # URL d'activation
        activation_url = f"{settings.SITE_URL}/activate/{activation_token.token}/"

        # Contexte pour le template
        context = {
            'user': self,
            'activation_url': activation_url,
            'site_name': 'MCN Museum',
            'expiry_hours': settings.ACTIVATION_TOKEN_EXPIRY_HOURS,
        }

        # Rendu du template
        subject = 'Activez votre compte MCN Museum'
        html_message = render_to_string('emails/activation_email.html', context)
        plain_message = render_to_string('emails/activation_email.txt', context)

        # Envoi de l'email
        send_mail(
            subject=subject,
            message=plain_message,
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[self.email],
            html_message=html_message,
            fail_silently=False,
        )


class ActivationToken(models.Model):
    """Token d'activation de compte"""
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='activation_token')
    token = models.CharField(max_length=64, unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField()

    class Meta:
        verbose_name = 'Token d\'activation'
        verbose_name_plural = 'Tokens d\'activation'

    def __str__(self):
        return f"Token - {self.user.username}"

    @staticmethod
    def generate_token():
        """Génère un token sécurisé"""
        return secrets.token_urlsafe(48)

    def is_valid(self):
        """Vérifie si le token est encore valide"""
        return timezone.now() < self.expires_at

    @classmethod
    def create_for_user(cls, user):
        """Crée un token pour un utilisateur"""
        # Supprimer l'ancien token s'il existe
        cls.objects.filter(user=user).delete()

        expiry_hours = getattr(settings, 'ACTIVATION_TOKEN_EXPIRY_HOURS', 24)

        return cls.objects.create(
            user=user,
            token=cls.generate_token(),
            expires_at=timezone.now() + timedelta(hours=expiry_hours)
        )


# ==================== LANGUAGE MODEL ====================
class Language(models.Model):
    """Langues disponibles dans le système"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    code = models.CharField(max_length=5, unique=True)  # fr, en, wo
    name = models.CharField(max_length=50)  # Français, English, Wolof
    native_name = models.CharField(max_length=50)  # Français, English, Wolof
    is_active = models.BooleanField(default=True)
    order = models.IntegerField(default=0)

    class Meta:
        ordering = ['order', 'name']
        verbose_name = 'Langue'
        verbose_name_plural = 'Langues'

    def __str__(self):
        return f"{self.name} ({self.code})"

    def get_translations_count(self):
        """Nombre de traductions dans cette langue"""
        return ArtworkTranslation.objects.filter(language=self).count()


# ==================== CATEGORY MODEL ====================
class Category(models.Model):
    """Catégories d'œuvres"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=100)  # Nom par défaut (français)
    slug = models.SlugField(unique=True)
    icon = models.ImageField(upload_to='categories/', blank=True, null=True)
    order = models.IntegerField(default=0)

    class Meta:
        ordering = ['order', 'name']
        verbose_name = 'Catégorie'
        verbose_name_plural = 'Catégories'

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)

    def get_artworks(self):
        """Retourne les œuvres de cette catégorie"""
        return self.artworks.filter(is_published=True)


# ==================== CATEGORY TRANSLATION MODEL ====================
class CategoryTranslation(models.Model):
    """Traductions des catégories"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='translations')
    language = models.ForeignKey(Language, on_delete=models.CASCADE, to_field='code')  # CORRECTION
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True)

    class Meta:
        unique_together = ['category', 'language']
        verbose_name = 'Traduction de catégorie'
        verbose_name_plural = 'Traductions de catégories'

    def __str__(self):
        return f"{self.category.name} - {self.language}"


# ==================== EXHIBITION MODEL ====================
class Exhibition(models.Model):
    """Expositions temporaires"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    start_date = models.DateField()
    end_date = models.DateField()
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-start_date']
        verbose_name = 'Exposition'
        verbose_name_plural = 'Expositions'

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        super().save(*args, **kwargs)

    def is_ongoing(self):
        """Vérifie si l'exposition est en cours"""
        from django.utils import timezone
        today = timezone.now().date()
        return self.start_date <= today <= self.end_date

    def get_artworks(self):
        """Retourne les œuvres de cette exposition"""
        return self.artworks.filter(is_published=True)


# ==================== EXHIBITION TRANSLATION MODEL ====================
class ExhibitionTranslation(models.Model):
    """Traductions des expositions"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    exhibition = models.ForeignKey(Exhibition, on_delete=models.CASCADE, related_name='translations')
    language = models.ForeignKey(Language, on_delete=models.CASCADE, to_field='code')  # CORRECTION
    title = models.CharField(max_length=200)
    description = models.TextField()

    class Meta:
        unique_together = ['exhibition', 'language']
        verbose_name = 'Traduction d\'exposition'
        verbose_name_plural = 'Traductions d\'expositions'

    def __str__(self):
        return f"{self.exhibition.title} - {self.language}"


# ==================== ARTWORK MODEL ====================
class Artwork(models.Model):
    """Œuvre d'art - Entité principale"""
    STATUS_CHOICES = [
        ('draft', 'Brouillon'),
        ('published', 'Publié'),
        ('archived', 'Archivé'),
    ]

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    reference_code = models.CharField(max_length=50, unique=True)
    qr_code = models.ImageField(upload_to='artworks/qr_codes/', blank=True, null=True)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='artworks')
    origin = models.CharField(max_length=200, blank=True)
    period = models.CharField(max_length=100, blank=True)
    acquisition_date = models.DateField(null=True, blank=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
    is_published = models.BooleanField(default=False)
    is_featured = models.BooleanField(default=False)
    view_count = models.IntegerField(default=0)
    favorites_count = models.IntegerField(default=0)
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='created_artworks')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    # Relations
    exhibitions = models.ManyToManyField(Exhibition, related_name='artworks', blank=True)

    class Meta:
        ordering = ['-created_at']
        verbose_name = 'Œuvre'
        verbose_name_plural = 'Œuvres'
        indexes = [
            models.Index(fields=['reference_code']),
            models.Index(fields=['status', 'is_published']),
            models.Index(fields=['-view_count']),
        ]

    def __str__(self):
        return self.reference_code

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        if not self.qr_code:
            self.generate_qr_code()
            super().save(update_fields=['qr_code'])

    def get_featured_image(self):
        """Retourne l'image à la une ou la première image"""
        featured = self.media.filter(media_type='image', is_featured=True).first()
        if featured:
            return featured
        return self.media.filter(media_type='image').first()

    def generate_qr_code(self):
        """Génère le QR code de l'œuvre"""
        qr = qrcode.QRCode(
            version=1,
            error_correction=qrcode.constants.ERROR_CORRECT_L,
            box_size=10,
            border=4
        )

        # Méthode 1: Utiliser le framework Sites de Django (recommandé)
        try:
            current_site = Site.objects.get_current()
            domain = current_site.domain
            protocol = 'https' if settings.SECURE_SSL_REDIRECT else 'http'
            url = f"{protocol}://{domain}/artwork/{self.id}/"
        except:
            # Fallback: utiliser un paramètre du settings
            base_url = getattr(settings, 'BASE_URL', 'http://localhost:8000')
            url = f"{base_url}/artwork/{self.id}/"

        qr.add_data(url)
        qr.make(fit=True)
        img = qr.make_image(fill_color="black", back_color="white")

        buffer = BytesIO()
        img.save(buffer, format='PNG')
        file_name = f'qr_{self.reference_code}.png'
        self.qr_code.save(file_name, File(buffer), save=False)
        buffer.close()

    def get_description(self, language='fr'):
        """Retourne la description dans la langue demandée"""
        try:
            translation = self.translations.get(language=language)
            return translation.description
        except ArtworkTranslation.DoesNotExist:
            # Fallback vers le français
            try:
                return self.translations.get(language='fr').description
            except ArtworkTranslation.DoesNotExist:
                return ""

    def get_statistics(self):
        """Retourne les statistiques de l'œuvre"""
        return {
            'view_count': self.view_count,
            'favorites_count': self.favorites_count,
            'comments_count': self.comments.filter(is_approved=True).count(),
            'visits_count': self.visits.count(),
        }


# ==================== ARTWORK TRANSLATION MODEL ====================
class ArtworkTranslation(models.Model):
    """Traductions des œuvres"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    artwork = models.ForeignKey(Artwork, on_delete=models.CASCADE, related_name='translations')
    language = models.ForeignKey(Language, on_delete=models.CASCADE, to_field='code')
    title = models.CharField(max_length=200)
    description = models.TextField()
    historical_context = models.TextField(blank=True)
    cultural_significance = models.TextField(blank=True)
    artist = models.CharField(max_length=200, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)


    class Meta:
        unique_together = ['artwork', 'language']
        verbose_name = 'Traduction d\'œuvre'
        verbose_name_plural = 'Traductions d\'œuvres'
        indexes = [
            models.Index(fields=['artwork', 'language']),
        ]

    def __str__(self):
        return f"{self.artwork.reference_code} - {self.language.code}"

    def is_complete(self):
        """Vérifie si la traduction est complète"""
        return bool(self.title and self.description)

    def validate_translation(self):
        """Valide le contenu de la traduction"""
        errors = []
        if len(self.title) < 3:
            errors.append("Le titre doit contenir au moins 3 caractères")
        if len(self.description) < 50:
            errors.append("La description doit contenir au moins 50 caractères")
        return errors

    def has_3d_view(self):
        """Vérifie si l'œuvre a une vue 3D disponible"""
        return self.view_3d_enabled and bool(self.panorama_360)


# ==================== MEDIA MODEL ====================
class Media(models.Model):
    """Gestion avancée des médias (images/vidéos)"""
    MEDIA_TYPE_CHOICES = [
        ('image', 'Image'),
        ('video', 'Vidéo'),
        ('audio', 'Audio'),
    ]

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    artwork = models.ForeignKey(Artwork, on_delete=models.CASCADE, related_name='media')
    media_type = models.CharField(max_length=10, choices=MEDIA_TYPE_CHOICES)
    file = models.FileField(upload_to='artworks/media/')
    thumbnail = models.ImageField(upload_to='artworks/thumbnails/', blank=True, null=True)
    caption = models.CharField(max_length=200, blank=True)
    size = models.IntegerField(default=0)  # En bytes
    order = models.IntegerField(default=0)
    language = models.CharField(max_length=5, blank=True)  # Pour les vidéos multilingues
    uploaded_at = models.DateTimeField(auto_now_add=True)
    is_featured = models.BooleanField(default=False, help_text="Image à la une de l'œuvre")

    class Meta:
        ordering = ['order', 'uploaded_at']
        verbose_name = 'Média'
        verbose_name_plural = 'Médias'

    def __str__(self):
        return f"{self.artwork.reference_code} - {self.media_type}"

    def get_url(self):
        """Retourne l'URL du fichier"""
        return self.file.url if self.file else None

    def generate_thumbnail(self):
        """Génère une miniature pour les images/vidéos"""
        # À implémenter avec Pillow ou FFmpeg
        pass

    def save(self, *args, **kwargs):
        # Si c'est la première image, la mettre automatiquement en vedette
        if self.is_featured and self.media_type == 'image':
            # Retirer le flag featured des autres images
            Media.objects.filter(
                artwork=self.artwork,
                media_type='image',
                is_featured=True
            ).update(is_featured=False)

        # Si c'est la première image et qu'il n'y a pas d'image à la une
        if self.media_type == 'image' and not self.artwork.media.filter(media_type='image', is_featured=True).exists():
            self.is_featured = True

        super().save(*args, **kwargs)


# ==================== AUDIO GUIDE MODEL ====================
class AudioGuide(models.Model):
    """Audioguides pour les œuvres"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    artwork_translation = models.ForeignKey(ArtworkTranslation, on_delete=models.CASCADE, related_name='audio_guides')
    audio_file = models.FileField(upload_to='artworks/audio/',
                                  validators=[FileExtensionValidator(['mp3', 'wav', 'ogg'])])
    duration = models.IntegerField(default=0)  # En secondes
    narrator = models.CharField(max_length=100, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = 'Guide audio'
        verbose_name_plural = 'Guides audio'

    def __str__(self):
        return f"Audio - {self.artwork_translation.artwork.reference_code} ({self.artwork_translation.language})"

    def get_duration_formatted(self):
        """Retourne la durée formatée (mm:ss)"""
        minutes = self.duration // 60
        seconds = self.duration % 60
        return f"{minutes:02d}:{seconds:02d}"


# ==================== VISIT MODEL ====================
class Visit(models.Model):
    """Tracking détaillé des visites"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    artwork = models.ForeignKey(Artwork, on_delete=models.CASCADE, related_name='visits')
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='visits')
    session_id = models.CharField(max_length=100, blank=True)  # Pour visiteurs anonymes
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    user_agent = models.TextField(blank=True)
    source = models.CharField(max_length=20, default='web')  # web, qr, mobile
    language_viewed = models.CharField(max_length=5, default='fr')
    visited_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-visited_at']
        verbose_name = 'Visite'
        verbose_name_plural = 'Visites'
        indexes = [
            models.Index(fields=['artwork', '-visited_at']),
            models.Index(fields=['user', '-visited_at']),
        ]

    def __str__(self):
        return f"{self.artwork.reference_code} - {self.visited_at}"

    def is_unique(self):
        """Vérifie si c'est une visite unique (24h)"""
        from django.utils import timezone
        from datetime import timedelta
        yesterday = timezone.now() - timedelta(days=1)
        return not Visit.objects.filter(
            artwork=self.artwork,
            ip_address=self.ip_address,
            visited_at__gte=yesterday
        ).exclude(id=self.id).exists()


# ==================== FAVORITE MODEL ====================
class Favorite(models.Model):
    """Favoris des utilisateurs"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='favorites')
    artwork = models.ForeignKey(Artwork, on_delete=models.CASCADE, related_name='favorited_by')
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ['user', 'artwork']
        ordering = ['-created_at']
        verbose_name = 'Favori'
        verbose_name_plural = 'Favoris'

    def __str__(self):
        return f"{self.user.username} - {self.artwork.reference_code}"

    def remove(self):
        """Supprime le favori"""
        self.delete()


# ==================== COMMENT MODEL ====================
class Comment(models.Model):
    """Commentaires sur les œuvres (avec modération)"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    artwork = models.ForeignKey(Artwork, on_delete=models.CASCADE, related_name='comments')
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comments')
    content = models.TextField()
    is_approved = models.BooleanField(default=False)
    is_flagged = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']
        verbose_name = 'Commentaire'
        verbose_name_plural = 'Commentaires'

    def __str__(self):
        return f"{self.user.username} - {self.artwork.reference_code}"

    def approve(self):
        """Approuve le commentaire"""
        self.is_approved = True
        self.save()

    def reject(self):
        """Rejette/supprime le commentaire"""
        self.delete()


# ==================== STATISTICS MODEL ====================
class Statistics(models.Model):
    """Statistiques quotidiennes du musée"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    date = models.DateField(unique=True)
    total_visits = models.IntegerField(default=0)
    unique_visitors = models.IntegerField(default=0)
    qr_scans = models.IntegerField(default=0)
    most_viewed_artwork = models.ForeignKey(Artwork, on_delete=models.SET_NULL, null=True, blank=True)
    avg_time_per_artwork = models.FloatField(default=0.0)  # En secondes

    class Meta:
        ordering = ['-date']
        verbose_name = 'Statistique'
        verbose_name_plural = 'Statistiques'

    def __str__(self):
        return f"Stats - {self.date}"

    def generate_report(self):
        """Génère un rapport statistique"""
        return {
            'date': self.date,
            'total_visits': self.total_visits,
            'unique_visitors': self.unique_visitors,
            'qr_scans': self.qr_scans,
            'most_viewed': self.most_viewed_artwork.reference_code if self.most_viewed_artwork else None,
        }


class VirtualRoom(models.Model):
    """Salles du musée pour le tour virtuel"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    description = models.TextField(blank=True)
    floor = models.IntegerField(default=0)  # Étage
    order = models.IntegerField(default=0)  # Ordre de visite

    # Images 360°
    panorama_image = models.ImageField(upload_to='virtual_tour/panoramas/',
                                       help_text='Image panoramique 360°')
    thumbnail = models.ImageField(upload_to='virtual_tour/thumbnails/', blank=True)

    # Audio ambiance
    ambient_audio = models.FileField(upload_to='virtual_tour/audio/', blank=True)

    # Navigation
    is_entrance = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)

    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['floor', 'order']
        verbose_name = 'Salle virtuelle'
        verbose_name_plural = 'Salles virtuelles'

    def __str__(self):
        return f"{self.name} (Étage {self.floor})"

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)


class RoomConnection(models.Model):
    """Connexions entre les salles (portes, passages)"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    from_room = models.ForeignKey(VirtualRoom, on_delete=models.CASCADE, related_name='exits')
    to_room = models.ForeignKey(VirtualRoom, on_delete=models.CASCADE, related_name='entrances')

    # Position du hotspot dans l'image 360 (coordonnées)
    hotspot_x = models.FloatField(help_text='Position X du hotspot (0-360 degrés)')
    hotspot_y = models.FloatField(help_text='Position Y du hotspot (-90 à 90 degrés)')

    label = models.CharField(max_length=100, help_text='Ex: "Vers la salle des masques"')
    icon = models.CharField(max_length=50, default='arrow',
                            choices=[('arrow', 'Flèche'), ('door', 'Porte')])

    class Meta:
        verbose_name = 'Connexion entre salles'
        verbose_name_plural = 'Connexions entre salles'
        unique_together = ['from_room', 'to_room']

    def __str__(self):
        return f"{self.from_room.name} → {self.to_room.name}"


class ArtworkHotspot(models.Model):
    """Points d'intérêt (hotspots) des œuvres dans les salles"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    room = models.ForeignKey(VirtualRoom, on_delete=models.CASCADE, related_name='artwork_hotspots')
    artwork = models.ForeignKey(Artwork, on_delete=models.CASCADE, related_name='hotspots')

    # Position du hotspot dans l'image 360
    hotspot_x = models.FloatField(help_text='Position X (0-360 degrés)')
    hotspot_y = models.FloatField(help_text='Position Y (-90 à 90 degrés)')

    # Animation/effet
    animation_type = models.CharField(max_length=20, default='pulse',
                                      choices=[('pulse', 'Pulsation'),
                                               ('glow', 'Lueur'),
                                               ('bounce', 'Rebond')])

    is_active = models.BooleanField(default=True)

    class Meta:
        verbose_name = 'Hotspot d\'œuvre'
        verbose_name_plural = 'Hotspots d\'œuvres'
        unique_together = ['room', 'artwork']

    def __str__(self):
        return f"{self.artwork.reference_code} dans {self.room.name}"


class VirtualTourSession(models.Model):
    """Suivi des sessions de tour virtuel"""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
    session_key = models.CharField(max_length=100)

    started_at = models.DateTimeField(auto_now_add=True)
    ended_at = models.DateTimeField(null=True, blank=True)
    duration = models.IntegerField(default=0, help_text='Durée en secondes')

    rooms_visited = models.ManyToManyField(VirtualRoom, through='RoomVisit')
    artworks_viewed = models.ManyToManyField(Artwork, blank=True)

    device_type = models.CharField(max_length=20, choices=[
        ('desktop', 'Ordinateur'),
        ('mobile', 'Mobile'),
        ('tablet', 'Tablette'),
        ('vr', 'Casque VR'),
    ], default='desktop')

    class Meta:
        ordering = ['-started_at']
        verbose_name = 'Session de tour virtuel'
        verbose_name_plural = 'Sessions de tour virtuel'

    def __str__(self):
        return f"Session {self.session_key[:8]} - {self.started_at}"


class RoomVisit(models.Model):
    """Visite d'une salle pendant une session"""
    session = models.ForeignKey(VirtualTourSession, on_delete=models.CASCADE)
    room = models.ForeignKey(VirtualRoom, on_delete=models.CASCADE)
    entered_at = models.DateTimeField(auto_now_add=True)
    duration = models.IntegerField(default=0, help_text='Temps passé en secondes')

    class Meta:
        ordering = ['entered_at']


class Artwork3DView(models.Model):
    """Vue 3D/360° d'une œuvre individuelle"""

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    artwork = models.OneToOneField(Artwork, on_delete=models.CASCADE, related_name='view_3d')

    # Image panoramique 360° de l'œuvre
    panorama_image = models.ImageField(
        upload_to='artworks/360/',
        help_text='Image panoramique 360° autour de l\'œuvre'
    )

    # Position initiale de la caméra
    initial_yaw = models.FloatField(default=0, help_text='Rotation horizontale initiale (0-360°)')
    initial_pitch = models.FloatField(default=0, help_text='Rotation verticale initiale (-90 à 90°)')
    initial_fov = models.FloatField(default=75, help_text='Champ de vision initial (30-120°)')

    # Hotspots interactifs sur l'œuvre
    hotspots_data = models.JSONField(
        default=list,
        blank=True,
        help_text='Liste des points d\'intérêt sur l\'œuvre'
    )

    # Audio ambiance spécifique à cette vue
    ambient_audio = models.FileField(
        upload_to='artworks/audio/ambient/',
        blank=True,
        null=True,
        help_text='Son d\'ambiance pour cette vue 3D'
    )

    # Options d'affichage
    allow_zoom = models.BooleanField(default=True)
    allow_rotation = models.BooleanField(default=True)
    show_controls = models.BooleanField(default=True)
    auto_rotate = models.BooleanField(default=False)
    auto_rotate_speed = models.FloatField(default=2.0)

    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = 'Vue 3D d\'œuvre'
        verbose_name_plural = 'Vues 3D d\'œuvres'

    def __str__(self):
        return f"Vue 3D - {self.artwork.reference_code}"

    def add_hotspot(self, pitch, yaw, hotspot_type, title, content):
        """Ajoute un point d'intérêt sur la vue 3D"""
        if not isinstance(self.hotspots_data, list):
            self.hotspots_data = []

        hotspot = {
            'id': str(uuid.uuid4()),
            'pitch': pitch,
            'yaw': yaw,
            'type': hotspot_type,  # 'info', 'zoom', 'detail', 'video'
            'title': title,
            'content': content,
        }
        self.hotspots_data.append(hotspot)
        self.save()
        return hotspot


class ArtworkDetailPoint(models.Model):
    """Point de détail sur une œuvre (pour zoom sur partie spécifique)"""

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    view_3d = models.ForeignKey(Artwork3DView, on_delete=models.CASCADE, related_name='detail_points')

    title = models.CharField(max_length=200)
    description = models.TextField()

    # Position du hotspot dans la vue 360°
    pitch = models.FloatField(help_text='Position verticale (-90 à 90°)')
    yaw = models.FloatField(help_text='Position horizontale (0-360°)')

    # Image zoom de cette partie
    detail_image = models.ImageField(
        upload_to='artworks/details/',
        blank=True,
        null=True,
        help_text='Image haute résolution de ce détail'
    )

    # Type de point
    POINT_TYPE_CHOICES = [
        ('info', 'Information'),
        ('zoom', 'Zoom'),
        ('detail', 'Détail technique'),
        ('history', 'Contexte historique'),
        ('video', 'Vidéo'),
    ]
    point_type = models.CharField(max_length=20, choices=POINT_TYPE_CHOICES, default='info')

    # Ordre d'affichage
    order = models.IntegerField(default=0)

    is_active = models.BooleanField(default=True)

    class Meta:
        ordering = ['order']
        verbose_name = 'Point de détail'
        verbose_name_plural = 'Points de détail'

    def __str__(self):
        return f"{self.title} - {self.view_3d.artwork.reference_code}"


class Artwork3DSession(models.Model):
    """Session de visualisation 3D d'une œuvre"""

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    artwork = models.ForeignKey(Artwork, on_delete=models.CASCADE, related_name='view_3d_sessions')
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
    session_key = models.CharField(max_length=100)

    # Statistiques de la session
    started_at = models.DateTimeField(auto_now_add=True)
    ended_at = models.DateTimeField(null=True, blank=True)
    duration = models.IntegerField(default=0, help_text='Durée en secondes')

    # Interactions
    interactions_count = models.IntegerField(default=0)
    hotspots_viewed = models.JSONField(default=list, blank=True)
    zoom_count = models.IntegerField(default=0)

    # Device info
    device_type = models.CharField(max_length=20, choices=[
        ('desktop', 'Ordinateur'),
        ('mobile', 'Mobile'),
        ('tablet', 'Tablette'),
        ('vr', 'Casque VR'),
    ], default='desktop')

    class Meta:
        ordering = ['-started_at']
        verbose_name = 'Session 3D'
        verbose_name_plural = 'Sessions 3D'

    def __str__(self):
        return f"Session 3D - {self.artwork.reference_code} - {self.started_at}"

    def record_interaction(self, hotspot_id, interaction_type):
        """Enregistre une interaction avec un hotspot"""
        self.interactions_count += 1

        if not isinstance(self.hotspots_viewed, list):
            self.hotspots_viewed = []

        self.hotspots_viewed.append({
            'hotspot_id': hotspot_id,
            'type': interaction_type,
            'timestamp': timezone.now().isoformat()
        })

        if interaction_type == 'zoom':
            self.zoom_count += 1

        self.save()