"""
Module de gestion des backends de stockage adaptatifs
Fournit une abstraction pour gérer différents backends de stockage de fichiers
"""

import logging
from django.conf import settings
from django.db import models

# Constants pour Cloudinary
CLOUDINARY_QUALITY_BEST = "auto:best"
CLOUDINARY_QUALITY_ECO = "auto:eco"

logger = logging.getLogger(__name__)


def get_storage_type():
    """
    Détermine le type de stockage à utiliser selon la configuration

    Priorité :
    1. CLOUDINARY si IS_CLOUDINARY=True
    2. S3-compatible (MinIO, AWS S3, etc.) si configuré
    3. FileField local en fallback

    Returns:
        str: Type de stockage ('cloudinary', 's3', 'local')
    """

    # Si Cloudinary est explicitement activé
    if getattr(settings, "IS_CLOUDINARY", False):
        try:
            from cloudinary.models import CloudinaryField

            logger.info("Cloudinary backend détecté et configuré")
            return "cloudinary"
        except ImportError:
            logger.warning("Cloudinary requis mais non installé. Fallback vers S3.")

    # Vérifier si S3 est configuré
    if hasattr(settings, "AWS_STORAGE_BUCKET_NAME") or hasattr(
        settings, "DEFAULT_FILE_STORAGE"
    ):
        storage_backend = getattr(settings, "DEFAULT_FILE_STORAGE", "")
        if "s3" in storage_backend.lower() or "minio" in storage_backend.lower():
            logger.info(f"Backend S3 compatible détecté: {storage_backend}")
            return "s3"

    # Fallback vers stockage local
    logger.info("Utilisation du stockage local par défaut")
    return "local"


# Déterminer le type de stockage au démarrage
STORAGE_TYPE = get_storage_type()


def create_file_field(upload_path, resource_type="auto", folder_prefix="amifond"):
    """
    Factory pour créer le bon type de champ fichier selon l'environnement

    Args:
        upload_path: fonction de chemin d'upload pour local/S3
        resource_type: type de ressource Cloudinary ("auto", "image", "video", etc.)
        folder_prefix: préfixe du dossier Cloudinary

    Returns:
        Field: Le champ approprié selon la configuration
    """

    if STORAGE_TYPE == "cloudinary":
        from cloudinary.models import CloudinaryField

        # Générer le nom du dossier à partir du nom de la fonction
        folder_name = upload_path.__name__.replace("get_", "").replace(
            "_upload_path", ""
        )
        folder_path = f"{folder_prefix}/{folder_name}"

        logger.debug(f"Création CloudinaryField avec dossier: {folder_path}")

        return CloudinaryField(
            resource_type=resource_type,
            folder=folder_path,
            transformation=[
                {"quality": CLOUDINARY_QUALITY_BEST},
                {"fetch_format": "auto"},
            ],
            help_text=f"Fichier stocké sur Cloudinary (dossier: {folder_path})",
        )

    elif STORAGE_TYPE == "s3":
        logger.debug(
            f"Création FileField pour S3 avec upload_path: {upload_path.__name__}"
        )
        return models.FileField(
            upload_to=upload_path,
            help_text="Fichier stocké sur S3/MinIO (utilise DEFAULT_FILE_STORAGE)",
        )

    else:  # local
        logger.debug(
            f"Création FileField local avec upload_path: {upload_path.__name__}"
        )
        return models.FileField(
            upload_to=upload_path, help_text="Fichier stocké localement"
        )


def create_image_field(upload_path, folder_prefix="amifond"):
    """
    Factory spécialisée pour créer un champ image adaptatif

    Args:
        upload_path: fonction de chemin d'upload pour local/S3
        folder_prefix: préfixe du dossier Cloudinary

    Returns:
        Field: Le champ approprié selon la configuration
    """
    return create_file_field(
        upload_path=upload_path, resource_type="image", folder_prefix=folder_prefix
    )


def create_video_field(upload_path, folder_prefix="amifond"):
    """
    Factory spécialisée pour créer un champ vidéo adaptatif

    Args:
        upload_path: fonction de chemin d'upload pour local/S3
        folder_prefix: préfixe du dossier Cloudinary

    Returns:
        Field: Le champ approprié selon la configuration
    """
    return create_file_field(
        upload_path=upload_path, resource_type="video", folder_prefix=folder_prefix
    )


def get_file_url(file_field):
    """
    Récupère l'URL d'un fichier de manière uniforme

    Args:
        file_field: Le champ fichier

    Returns:
        str: URL du fichier ou chaîne vide
    """
    try:
        if hasattr(file_field, "url"):
            return file_field.url
        elif hasattr(file_field, "build_url"):  # CloudinaryField
            return file_field.build_url()
    except Exception as e:
        logger.warning(f"Erreur lors de la récupération de l'URL du fichier: {e}")

    return ""


def get_thumbnail_url(file_field, width=300, height=200):
    """
    Génère l'URL d'une miniature de manière adaptative

    Args:
        file_field: Le champ fichier
        width: Largeur de la miniature
        height: Hauteur de la miniature

    Returns:
        str: URL de la miniature
    """

    if STORAGE_TYPE == "cloudinary":
        try:
            from cloudinary import CloudinaryImage

            if hasattr(file_field, "public_id"):
                return CloudinaryImage(file_field.public_id).build_url(
                    width=width,
                    height=height,
                    crop="fill",
                    quality=CLOUDINARY_QUALITY_ECO,
                )
        except Exception as e:
            logger.warning(f"Erreur lors de la génération de miniature Cloudinary: {e}")

    # Pour S3 et local, retourner l'image originale
    return get_file_url(file_field)


def get_large_image_url(file_field, max_width=1920, max_height=1280):
    """
    Génère l'URL d'une image en grande taille de manière adaptative

    Args:
        file_field: Le champ fichier
        max_width: Largeur maximale
        max_height: Hauteur maximale

    Returns:
        str: URL de l'image en grande taille
    """

    if STORAGE_TYPE == "cloudinary":
        try:
            from cloudinary import CloudinaryImage

            if hasattr(file_field, "public_id"):
                return CloudinaryImage(file_field.public_id).build_url(
                    width=max_width,
                    height=max_height,
                    crop="limit",
                    quality=CLOUDINARY_QUALITY_BEST,
                )
        except Exception as e:
            logger.warning(
                f"Erreur lors de la génération d'image large Cloudinary: {e}"
            )

    # Pour S3 et local, retourner l'image originale
    return get_file_url(file_field)


def get_storage_info():
    """
    Retourne des informations sur le type de stockage actuel

    Returns:
        dict: Informations sur le stockage
    """
    return {
        "type": STORAGE_TYPE,
        "is_cloudinary": STORAGE_TYPE == "cloudinary",
        "is_s3": STORAGE_TYPE == "s3",
        "is_local": STORAGE_TYPE == "local",
        "cloudinary_available": STORAGE_TYPE == "cloudinary",
        "s3_configured": STORAGE_TYPE == "s3",
    }


def validate_storage_configuration():
    """
    Valide la configuration du stockage actuel

    Returns:
        tuple: (bool, list) - (is_valid, error_messages)
    """
    errors = []

    if STORAGE_TYPE == "cloudinary":
        # Vérifier la configuration Cloudinary
        try:
            import cloudinary

            config = cloudinary.config()
            if not all([config.cloud_name, config.api_key, config.api_secret]):
                errors.append(
                    "Configuration Cloudinary incomplète (cloud_name, api_key, api_secret)"
                )
        except ImportError:
            errors.append("Cloudinary n'est pas installé")

    elif STORAGE_TYPE == "s3":
        # Vérifier la configuration S3
        required_settings = [
            "AWS_ACCESS_KEY_ID",
            "AWS_SECRET_ACCESS_KEY",
            "AWS_STORAGE_BUCKET_NAME",
        ]
        missing_settings = []

        for setting in required_settings:
            if not hasattr(settings, setting) or not getattr(settings, setting):
                missing_settings.append(setting)

        if missing_settings:
            errors.append(f"Configuration S3 incomplète: {', '.join(missing_settings)}")

    # Le stockage local ne nécessite pas de validation particulière

    return len(errors) == 0, errors


class StorageBackendMixin:
    """
    Mixin pour ajouter des méthodes utilitaires aux modèles avec des champs fichiers
    """

    @property
    def storage_info(self):
        """Information sur le type de stockage utilisé"""
        return get_storage_info()

    def get_file_url(self, field_name):
        """Récupère l'URL d'un champ fichier"""
        field = getattr(self, field_name, None)
        if field:
            return get_file_url(field)
        return ""

    def get_thumbnail_url(self, field_name, width=300, height=200):
        """Récupère l'URL de miniature d'un champ image"""
        field = getattr(self, field_name, None)
        if field:
            return get_thumbnail_url(field, width, height)
        return ""

    def get_large_image_url(self, field_name, max_width=1920, max_height=1280):
        """Récupère l'URL d'image en grande taille"""
        field = getattr(self, field_name, None)
        if field:
            return get_large_image_url(field, max_width, max_height)
        return ""


# Fonctions de chemin d'upload pour différents types de fichiers
def get_document_upload_path(instance, filename):
    """Chemin d'upload pour les documents"""
    return f"documents/{filename}"


def get_image_upload_path(instance, filename):
    """Chemin d'upload pour les images"""
    return f"images/{filename}"


def get_video_upload_path(instance, filename):
    """Chemin d'upload pour les vidéos"""
    return f"videos/{filename}"


def get_audio_upload_path(instance, filename):
    """Chemin d'upload pour les fichiers audio"""
    return f"audio/{filename}"


def get_archive_upload_path(instance, filename):
    """Chemin d'upload pour les archives"""
    return f"archives/{filename}"


def get_media_upload_path(instance, filename):
    """Chemin d'upload dynamique selon le type de média"""
    if hasattr(instance, "media_type"):
        if instance.media_type == "image":
            return f"images/{filename}"
        elif instance.media_type == "video":
            return f"videos/{filename}"
        elif instance.media_type == "audio":
            return f"audio/{filename}"
        elif instance.media_type == "archive":
            return f"archives/{filename}"
        elif instance.media_type == "document":
            return f"documents/{filename}"

    return f"other/{filename}"
