# app/modules/catalog/services/product_service.py
"""
Product service for store catalog management.

This module provides:
- Product catalog CRUD operations
- Product publishing from marketplace staging
- Product search and filtering
"""

import logging
from datetime import UTC, datetime

from sqlalchemy import func
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session

from app.modules.catalog.exceptions import (
    InvalidProductDataException,
    ProductAlreadyExistsException,
    ProductNotFoundException,
    ProductValidationException,
)
from app.modules.catalog.models import Product
from app.modules.catalog.schemas import ProductCreate, ProductUpdate

logger = logging.getLogger(__name__)


class ProductService:
    """Service for store catalog product operations."""

    def get_product(self, db: Session, store_id: int, product_id: int) -> Product:
        """
        Get a product from store catalog.

        Args:
            db: Database session
            store_id: Store ID
            product_id: Product ID

        Returns:
            Product object

        Raises:
            ProductNotFoundException: If product not found
        """
        try:
            product = (
                db.query(Product)
                .filter(Product.id == product_id, Product.store_id == store_id)
                .first()
            )

            if not product:
                raise ProductNotFoundException(f"Product {product_id} not found")

            return product

        except ProductNotFoundException:
            raise
        except SQLAlchemyError as e:
            logger.error(f"Error getting product: {str(e)}")
            raise ProductValidationException("Failed to retrieve product")

    def create_product(
        self, db: Session, store_id: int, product_data: ProductCreate
    ) -> Product:
        """
        Add a product from marketplace to store catalog.

        Args:
            db: Database session
            store_id: Store ID
            product_data: Product creation data

        Returns:
            Created Product object

        Raises:
            ProductAlreadyExistsException: If product already in catalog
            InvalidProductDataException: If marketplace product not found
        """
        try:
            # Verify marketplace product exists
            from app.modules.marketplace.models import MarketplaceProduct

            marketplace_product = (
                db.query(MarketplaceProduct)
                .filter(MarketplaceProduct.id == product_data.marketplace_product_id)
                .first()
            )

            if not marketplace_product:
                raise InvalidProductDataException(
                    f"Marketplace product {product_data.marketplace_product_id} not found"
                )

            # Check if already in catalog
            existing = (
                db.query(Product)
                .filter(
                    Product.store_id == store_id,
                    Product.marketplace_product_id
                    == product_data.marketplace_product_id,
                )
                .first()
            )

            if existing:
                raise ProductAlreadyExistsException("Product already exists in catalog")

            # Create product
            product = Product(
                store_id=store_id,
                marketplace_product_id=product_data.marketplace_product_id,
                store_sku=product_data.store_sku,
                price=product_data.price,
                sale_price=product_data.sale_price,
                currency=product_data.currency,
                availability=product_data.availability,
                condition=product_data.condition,
                is_featured=product_data.is_featured,
                is_active=True,
                min_quantity=product_data.min_quantity,
                max_quantity=product_data.max_quantity,
            )

            db.add(product)
            db.flush()
            db.refresh(product)

            logger.info(f"Added product {product.id} to store {store_id} catalog")
            return product

        except (ProductAlreadyExistsException, InvalidProductDataException):
            raise
        except SQLAlchemyError as e:
            logger.error(f"Error creating product: {str(e)}")
            raise ProductValidationException("Failed to create product")

    def update_product(
        self,
        db: Session,
        store_id: int,
        product_id: int,
        product_update: ProductUpdate,
    ) -> Product:
        """
        Update product in store catalog.

        Args:
            db: Database session
            store_id: Store ID
            product_id: Product ID
            product_update: Update data

        Returns:
            Updated Product object
        """
        try:
            product = self.get_product(db, store_id, product_id)

            # Update fields
            update_data = product_update.model_dump(exclude_unset=True)
            for key, value in update_data.items():
                setattr(product, key, value)

            product.updated_at = datetime.now(UTC)
            db.flush()
            db.refresh(product)

            logger.info(f"Updated product {product_id} in store {store_id} catalog")
            return product

        except ProductNotFoundException:
            raise
        except SQLAlchemyError as e:
            logger.error(f"Error updating product: {str(e)}")
            raise ProductValidationException("Failed to update product")

    def delete_product(self, db: Session, store_id: int, product_id: int) -> bool:
        """
        Remove product from store catalog.

        Args:
            db: Database session
            store_id: Store ID
            product_id: Product ID

        Returns:
            True if deleted
        """
        try:
            from app.core.soft_delete import soft_delete

            product = self.get_product(db, store_id, product_id)

            soft_delete(db, product, deleted_by_id=None)

            logger.info(f"Deleted product {product_id} from store {store_id} catalog")
            return True

        except ProductNotFoundException:
            raise
        except SQLAlchemyError as e:
            logger.error(f"Error deleting product: {str(e)}")
            raise ProductValidationException("Failed to delete product")

    def get_store_products(
        self,
        db: Session,
        store_id: int,
        skip: int = 0,
        limit: int = 100,
        is_active: bool | None = None,
        is_featured: bool | None = None,
    ) -> tuple[list[Product], int]:
        """
        Get products in store catalog with filtering.

        Args:
            db: Database session
            store_id: Store ID
            skip: Pagination offset
            limit: Pagination limit
            is_active: Filter by active status
            is_featured: Filter by featured status

        Returns:
            Tuple of (products, total_count)
        """
        try:
            query = db.query(Product).filter(Product.store_id == store_id)

            if is_active is not None:
                query = query.filter(Product.is_active == is_active)

            if is_featured is not None:
                query = query.filter(Product.is_featured == is_featured)

            total = query.count()
            products = query.offset(skip).limit(limit).all()

            return products, total

        except SQLAlchemyError as e:
            logger.error(f"Error getting store products: {str(e)}")
            raise ProductValidationException("Failed to retrieve products")

    def search_products(
        self,
        db: Session,
        store_id: int,
        query: str,
        skip: int = 0,
        limit: int = 50,
        language: str = "en",
    ) -> tuple[list[Product], int]:
        """
        Search products in store catalog.

        Searches across:
        - Product title and description (from translations)
        - Product SKU, brand, and GTIN

        Args:
            db: Database session
            store_id: Store ID
            query: Search query string
            skip: Pagination offset
            limit: Pagination limit
            language: Language for translation search (default: 'en')

        Returns:
            Tuple of (products, total_count)
        """
        from sqlalchemy import or_
        from sqlalchemy.orm import joinedload

        from app.modules.catalog.models import ProductTranslation

        try:
            # Prepare search pattern for LIKE queries
            search_pattern = f"%{query}%"

            # Use subquery to get distinct IDs (PostgreSQL can't compare JSON for DISTINCT)
            id_subquery = (
                db.query(Product.id)
                .outerjoin(
                    ProductTranslation,
                    (Product.id == ProductTranslation.product_id)
                    & (ProductTranslation.language == language),
                )
                .filter(
                    Product.store_id == store_id,
                    Product.is_active == True,
                )
                .filter(
                    or_(
                        # Search in translations
                        ProductTranslation.title.ilike(search_pattern),
                        ProductTranslation.description.ilike(search_pattern),
                        ProductTranslation.short_description.ilike(search_pattern),
                        # Search in product fields
                        Product.store_sku.ilike(search_pattern),
                        Product.brand.ilike(search_pattern),
                        Product.gtin.ilike(search_pattern),
                    )
                )
                .distinct()
                .subquery()
            )

            base_query = db.query(Product).filter(
                Product.id.in_(db.query(id_subquery.c.id))
            )

            # Get total count
            total = base_query.count()

            # Get paginated results with eager loading for performance
            products = (
                base_query.options(joinedload(Product.translations))
                .offset(skip)
                .limit(limit)
                .all()
            )

            logger.debug(
                f"Search '{query}' for store {store_id}: {total} results"
            )
            return products, total

        except SQLAlchemyError as e:
            logger.error(f"Error searching products: {str(e)}")
            raise ProductValidationException("Failed to search products")


    # ========================================================================
    # Cross-module public API methods
    # ========================================================================

    def get_product_by_id(self, db: Session, product_id: int) -> Product | None:
        """
        Get product by ID without store scope.

        Args:
            db: Database session
            product_id: Product ID

        Returns:
            Product object or None
        """
        return db.query(Product).filter(Product.id == product_id).first()

    def get_products_with_gtin(
        self, db: Session, store_id: int
    ) -> list[Product]:
        """Get all products with a GTIN for a store."""
        return (
            db.query(Product)
            .filter(
                Product.store_id == store_id,
                Product.gtin.isnot(None),
            )
            .all()
        )

    def get_store_product_count(
        self,
        db: Session,
        store_id: int,
        active_only: bool = False,
        featured_only: bool = False,
    ) -> int:
        """
        Count products for a store with optional filters.

        Args:
            db: Database session
            store_id: Store ID
            active_only: Only count active products
            featured_only: Only count featured products

        Returns:
            Product count
        """
        query = db.query(func.count(Product.id)).filter(Product.store_id == store_id)
        if active_only:
            query = query.filter(Product.is_active == True)  # noqa: E712
        if featured_only:
            query = query.filter(Product.is_featured == True)  # noqa: E712
        return query.scalar() or 0

    def get_total_product_count(self, db: Session) -> int:
        """
        Get total product count across all stores.

        Args:
            db: Database session

        Returns:
            Total product count
        """
        return db.query(func.count(Product.id)).scalar() or 0


# Create service instance
product_service = ProductService()
