# services/inventory/app/services/messaging.py
"""
Messaging service for inventory events
"""

from typing import Dict, Any, Optional
from uuid import UUID
import structlog

from shared.messaging.rabbitmq import MessagePublisher
from shared.messaging.events import (
    EVENT_TYPES,
    InventoryEvent,
    StockAlertEvent,
    StockMovementEvent
)

logger = structlog.get_logger()


class InventoryMessagingService:
    """Service for publishing inventory-related events"""

    def __init__(self):
        self.publisher = MessagePublisher()

    async def publish_ingredient_created(
        self, 
        tenant_id: UUID, 
        ingredient_id: UUID,
        ingredient_data: Dict[str, Any]
    ):
        """Publish ingredient creation event"""
        try:
            event = InventoryEvent(
                event_type=EVENT_TYPES.INVENTORY.INGREDIENT_CREATED,
                tenant_id=str(tenant_id),
                ingredient_id=str(ingredient_id),
                data=ingredient_data
            )
            
            await self.publisher.publish_event(
                routing_key="inventory.ingredient.created",
                event=event
            )
            
            logger.info(
                "Published ingredient created event",
                tenant_id=tenant_id,
                ingredient_id=ingredient_id
            )
            
        except Exception as e:
            logger.error(
                "Failed to publish ingredient created event",
                error=str(e),
                tenant_id=tenant_id,
                ingredient_id=ingredient_id
            )

    async def publish_stock_added(
        self, 
        tenant_id: UUID, 
        ingredient_id: UUID,
        stock_id: UUID,
        quantity: float,
        batch_number: Optional[str] = None
    ):
        """Publish stock addition event"""
        try:
            movement_event = StockMovementEvent(
                event_type=EVENT_TYPES.INVENTORY.STOCK_ADDED,
                tenant_id=str(tenant_id),
                ingredient_id=str(ingredient_id),
                stock_id=str(stock_id),
                quantity=quantity,
                movement_type="purchase",
                data={
                    "batch_number": batch_number,
                    "movement_type": "purchase"
                }
            )
            
            await self.publisher.publish_event(
                routing_key="inventory.stock.added",
                event=movement_event
            )
            
            logger.info(
                "Published stock added event",
                tenant_id=tenant_id,
                ingredient_id=ingredient_id,
                quantity=quantity
            )
            
        except Exception as e:
            logger.error(
                "Failed to publish stock added event",
                error=str(e),
                tenant_id=tenant_id,
                ingredient_id=ingredient_id
            )

    async def publish_stock_consumed(
        self, 
        tenant_id: UUID, 
        ingredient_id: UUID,
        consumed_items: list,
        total_quantity: float,
        reference_number: Optional[str] = None
    ):
        """Publish stock consumption event"""
        try:
            for item in consumed_items:
                movement_event = StockMovementEvent(
                    event_type=EVENT_TYPES.INVENTORY.STOCK_CONSUMED,
                    tenant_id=str(tenant_id),
                    ingredient_id=str(ingredient_id),
                    stock_id=item['stock_id'],
                    quantity=item['quantity_consumed'],
                    movement_type="production_use",
                    data={
                        "batch_number": item.get('batch_number'),
                        "reference_number": reference_number,
                        "movement_type": "production_use"
                    }
                )
                
                await self.publisher.publish_event(
                    routing_key="inventory.stock.consumed",
                    event=movement_event
                )
            
            logger.info(
                "Published stock consumed events",
                tenant_id=tenant_id,
                ingredient_id=ingredient_id,
                total_quantity=total_quantity,
                items_count=len(consumed_items)
            )
            
        except Exception as e:
            logger.error(
                "Failed to publish stock consumed event",
                error=str(e),
                tenant_id=tenant_id,
                ingredient_id=ingredient_id
            )

    async def publish_low_stock_alert(
        self, 
        tenant_id: UUID, 
        ingredient_id: UUID,
        ingredient_name: str,
        current_stock: float,
        threshold: float,
        needs_reorder: bool = False
    ):
        """Publish low stock alert event"""
        try:
            alert_event = StockAlertEvent(
                event_type=EVENT_TYPES.INVENTORY.LOW_STOCK_ALERT,
                tenant_id=str(tenant_id),
                ingredient_id=str(ingredient_id),
                alert_type="low_stock" if not needs_reorder else "reorder_needed",
                severity="medium" if not needs_reorder else "high",
                data={
                    "ingredient_name": ingredient_name,
                    "current_stock": current_stock,
                    "threshold": threshold,
                    "needs_reorder": needs_reorder
                }
            )
            
            await self.publisher.publish_event(
                routing_key="inventory.alerts.low_stock",
                event=alert_event
            )
            
            logger.info(
                "Published low stock alert",
                tenant_id=tenant_id,
                ingredient_id=ingredient_id,
                current_stock=current_stock
            )
            
        except Exception as e:
            logger.error(
                "Failed to publish low stock alert",
                error=str(e),
                tenant_id=tenant_id,
                ingredient_id=ingredient_id
            )

    async def publish_expiration_alert(
        self, 
        tenant_id: UUID, 
        ingredient_id: UUID,
        stock_id: UUID,
        ingredient_name: str,
        batch_number: Optional[str],
        expiration_date: str,
        days_to_expiry: int,
        quantity: float
    ):
        """Publish expiration alert event"""
        try:
            severity = "critical" if days_to_expiry <= 1 else "high"
            
            alert_event = StockAlertEvent(
                event_type=EVENT_TYPES.INVENTORY.EXPIRATION_ALERT,
                tenant_id=str(tenant_id),
                ingredient_id=str(ingredient_id),
                alert_type="expiring_soon",
                severity=severity,
                data={
                    "stock_id": str(stock_id),
                    "ingredient_name": ingredient_name,
                    "batch_number": batch_number,
                    "expiration_date": expiration_date,
                    "days_to_expiry": days_to_expiry,
                    "quantity": quantity
                }
            )
            
            await self.publisher.publish_event(
                routing_key="inventory.alerts.expiration",
                event=alert_event
            )
            
            logger.info(
                "Published expiration alert",
                tenant_id=tenant_id,
                ingredient_id=ingredient_id,
                days_to_expiry=days_to_expiry
            )
            
        except Exception as e:
            logger.error(
                "Failed to publish expiration alert",
                error=str(e),
                tenant_id=tenant_id,
                ingredient_id=ingredient_id
            )