# app/modules/catalog/services/product_media_service.py
"""
Product media service for managing product-media associations.

This service handles the catalog-specific logic for attaching and
detaching media files to products. It uses the generic MediaFile
from CMS but owns the ProductMedia association.

This follows the principle that:
- CMS provides generic media storage (agnostic to consumers)
- Catalog defines how it uses media (ProductMedia association)
"""

import logging

from sqlalchemy.orm import Session

from app.modules.catalog.exceptions import ProductMediaException
from app.modules.catalog.models import Product, ProductMedia

logger = logging.getLogger(__name__)


class ProductMediaService:
    """Service for product-media association operations."""

    def attach_media_to_product(
        self,
        db: Session,
        store_id: int,
        product_id: int,
        media_id: int,
        usage_type: str = "gallery",
        display_order: int = 0,
    ) -> ProductMedia | None:
        """
        Attach a media file to a product.

        Args:
            db: Database session
            store_id: Store ID (for ownership verification)
            product_id: Product ID
            media_id: Media file ID
            usage_type: How the media is used (main_image, gallery, etc.)
            display_order: Order for galleries

        Returns:
            Created or updated ProductMedia association

        Raises:
            ProductMediaException: If product or media doesn't belong to store
        """
        # Verify product belongs to store
        product = (
            db.query(Product)
            .filter(Product.id == product_id, Product.store_id == store_id)
            .first()
        )
        if not product:
            raise ProductMediaException(
                product_id=product_id,
                message=f"Product {product_id} not found for store {store_id}",
            )

        # Verify media belongs to store
        from app.modules.cms.models import MediaFile

        media = (
            db.query(MediaFile)
            .filter(MediaFile.id == media_id, MediaFile.store_id == store_id)
            .first()
        )
        if not media:
            raise ProductMediaException(
                product_id=product_id,
                message=f"Media {media_id} not found for store {store_id}",
            )

        # Check if already attached with same usage type
        existing = (
            db.query(ProductMedia)
            .filter(
                ProductMedia.product_id == product_id,
                ProductMedia.media_id == media_id,
                ProductMedia.usage_type == usage_type,
            )
            .first()
        )

        if existing:
            # Update display order if association exists
            existing.display_order = display_order
            db.flush()
            return existing

        # Create new association
        product_media = ProductMedia(
            product_id=product_id,
            media_id=media_id,
            usage_type=usage_type,
            display_order=display_order,
        )

        db.add(product_media)

        # Update usage count on media
        media.usage_count = (media.usage_count or 0) + 1

        db.flush()

        logger.info(
            f"Attached media {media_id} to product {product_id} as {usage_type}"
        )

        return product_media

    def detach_media_from_product(
        self,
        db: Session,
        store_id: int,
        product_id: int,
        media_id: int,
        usage_type: str | None = None,
    ) -> int:
        """
        Detach a media file from a product.

        Args:
            db: Database session
            store_id: Store ID (for ownership verification)
            product_id: Product ID
            media_id: Media file ID
            usage_type: Specific usage type to remove (None = all usages)

        Returns:
            Number of associations removed

        Raises:
            ProductMediaException: If product doesn't belong to store
        """
        # Verify product belongs to store
        product = (
            db.query(Product)
            .filter(Product.id == product_id, Product.store_id == store_id)
            .first()
        )
        if not product:
            raise ProductMediaException(
                product_id=product_id,
                message=f"Product {product_id} not found for store {store_id}",
            )

        # Build query
        query = db.query(ProductMedia).filter(
            ProductMedia.product_id == product_id,
            ProductMedia.media_id == media_id,
        )

        if usage_type:
            query = query.filter(ProductMedia.usage_type == usage_type)

        deleted_count = query.delete()

        # Update usage count on media
        if deleted_count > 0:
            from app.modules.cms.models import MediaFile

            media = db.query(MediaFile).filter(MediaFile.id == media_id).first()
            if media:
                media.usage_count = max(0, (media.usage_count or 0) - deleted_count)

        db.flush()

        logger.info(
            f"Detached {deleted_count} media association(s) from product {product_id}"
        )

        return deleted_count

    def get_product_media(
        self,
        db: Session,
        product_id: int,
        usage_type: str | None = None,
    ) -> list[ProductMedia]:
        """
        Get media associations for a product.

        Args:
            db: Database session
            product_id: Product ID
            usage_type: Filter by usage type (None = all)

        Returns:
            List of ProductMedia associations
        """
        query = (
            db.query(ProductMedia)
            .filter(ProductMedia.product_id == product_id)
            .order_by(ProductMedia.display_order)
        )

        if usage_type:
            query = query.filter(ProductMedia.usage_type == usage_type)

        return query.all()

    def get_media_usage_for_product(
        self,
        db: Session,
        product_id: int,
    ) -> dict:
        """
        Get all media usage information for a product.

        Args:
            db: Database session
            product_id: Product ID

        Returns:
            Dict with media organized by usage type
        """
        associations = self.get_product_media(db, product_id)

        usage_by_type: dict[str, list] = {}
        for assoc in associations:
            usage_type = assoc.usage_type
            if usage_type not in usage_by_type:
                usage_by_type[usage_type] = []

            media = assoc.media
            usage_by_type[usage_type].append({
                "association_id": assoc.id,
                "media_id": media.id if media else None,
                "filename": media.filename if media else None,
                "file_url": media.file_url if media else None,
                "thumbnail_url": media.thumbnail_url if media else None,
                "display_order": assoc.display_order,
            })

        return {
            "product_id": product_id,
            "media_by_type": usage_by_type,
            "total_count": len(associations),
        }

    def get_media_usage(self, db: Session, media_id: int) -> list[dict]:
        """Implement MediaUsageProviderProtocol.

        Returns list of usage records for a given media file across products.
        """
        rows = (
            db.query(ProductMedia)
            .filter(ProductMedia.media_id == media_id)
            .all()
        )
        return [
            {
                "entity_type": "product",
                "entity_id": r.product_id,
                "entity_name": r.product.name if r.product else "Unknown",
                "usage_type": r.usage_type,
            }
            for r in rows
        ]

    def set_main_image(
        self,
        db: Session,
        store_id: int,
        product_id: int,
        media_id: int,
    ) -> ProductMedia | None:
        """
        Set the main image for a product.

        Removes any existing main_image association and creates a new one.

        Args:
            db: Database session
            store_id: Store ID
            product_id: Product ID
            media_id: Media file ID to set as main image

        Returns:
            The new ProductMedia association
        """
        # Remove existing main image
        self.detach_media_from_product(
            db, store_id, product_id, media_id=0, usage_type="main_image"
        )

        # Actually, we need to remove ALL main_image associations, not just for media_id=0
        db.query(ProductMedia).filter(
            ProductMedia.product_id == product_id,
            ProductMedia.usage_type == "main_image",
        ).delete()

        # Attach new main image
        return self.attach_media_to_product(
            db,
            store_id=store_id,
            product_id=product_id,
            media_id=media_id,
            usage_type="main_image",
            display_order=0,
        )


# Singleton instance
product_media_service = ProductMediaService()

__all__ = ["ProductMediaService", "product_media_service"]
