# services/inventory/app/api/inventory_operations.py
"""
Inventory Operations API - Business operations for inventory management
"""

from typing import List, Optional, Dict, Any
from uuid import UUID, uuid4
from fastapi import APIRouter, Depends, HTTPException, Query, Path, status
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel, Field
import structlog

from app.core.database import get_db
from app.services.inventory_service import InventoryService
from app.services.product_classifier import ProductClassifierService, get_product_classifier
from shared.auth.decorators import get_current_user_dep
from shared.auth.access_control import require_user_role
from shared.routing import RouteBuilder

logger = structlog.get_logger()
route_builder = RouteBuilder('inventory')
router = APIRouter(tags=["inventory-operations"])


def get_current_user_id(current_user: dict = Depends(get_current_user_dep)) -> UUID:
    """Extract user ID from current user context"""
    user_id = current_user.get('user_id')
    if not user_id:
        if current_user.get('type') == 'service':
            return None
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="User ID not found in context"
        )
    try:
        return UUID(user_id)
    except (ValueError, TypeError):
        return None


# ===== Stock Operations =====

@router.post(
    route_builder.build_operations_route("consume-stock"),
    response_model=dict
)
@require_user_role(['admin', 'owner', 'member'])
async def consume_stock(
    tenant_id: UUID = Path(..., description="Tenant ID"),
    ingredient_id: UUID = Query(..., description="Ingredient ID to consume"),
    quantity: float = Query(..., gt=0, description="Quantity to consume"),
    reference_number: Optional[str] = Query(None, description="Reference number"),
    notes: Optional[str] = Query(None, description="Additional notes"),
    fifo: bool = Query(True, description="Use FIFO method"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Consume stock for production"""
    try:
        user_id = get_current_user_id(current_user)
        service = InventoryService()
        consumed_items = await service.consume_stock(
            ingredient_id, quantity, tenant_id, user_id, reference_number, notes, fifo
        )
        return {
            "ingredient_id": str(ingredient_id),
            "total_quantity_consumed": quantity,
            "consumed_items": consumed_items,
            "method": "FIFO" if fifo else "LIFO"
        }
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(e)
        )
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to consume stock"
        )


@router.get(
    route_builder.build_operations_route("stock/expiring"),
    response_model=List[dict]
)
async def get_expiring_stock(
    tenant_id: UUID = Path(..., description="Tenant ID"),
    days_ahead: int = Query(7, ge=1, le=365, description="Days ahead to check"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Get stock items expiring within specified days"""
    try:
        service = InventoryService()
        expiring_items = await service.check_expiration_alerts(tenant_id, days_ahead)
        return expiring_items
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to get expiring stock"
        )


@router.get(
    route_builder.build_operations_route("stock/low-stock"),
    response_model=List[dict]
)
async def get_low_stock(
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Get ingredients with low stock levels"""
    try:
        service = InventoryService()
        low_stock_items = await service.check_low_stock_alerts(tenant_id)
        return low_stock_items
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to get low stock items"
        )


@router.get(
    route_builder.build_operations_route("stock/summary"),
    response_model=dict
)
async def get_stock_summary(
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Get stock summary for tenant"""
    try:
        service = InventoryService()
        summary = await service.get_inventory_summary(tenant_id)
        return summary.dict()
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to get stock summary"
        )


# ===== Product Classification Operations =====

class ProductClassificationRequest(BaseModel):
    """Request for single product classification"""
    product_name: str = Field(..., description="Product name to classify")
    sales_volume: float = Field(None, description="Total sales volume for context")
    sales_data: Dict[str, Any] = Field(default_factory=dict, description="Additional sales context")


class BatchClassificationRequest(BaseModel):
    """Request for batch product classification"""
    products: List[ProductClassificationRequest] = Field(..., description="Products to classify")


class ProductSuggestionResponse(BaseModel):
    """Response with product classification suggestion"""
    suggestion_id: str
    original_name: str
    suggested_name: str
    product_type: str
    category: str
    unit_of_measure: str
    confidence_score: float
    estimated_shelf_life_days: Optional[int] = None
    requires_refrigeration: bool = False
    requires_freezing: bool = False
    is_seasonal: bool = False
    suggested_supplier: Optional[str] = None
    notes: Optional[str] = None


class BusinessModelAnalysisResponse(BaseModel):
    """Response with business model analysis"""
    model: str
    confidence: float
    ingredient_count: int
    finished_product_count: int
    ingredient_ratio: float
    recommendations: List[str]


class BatchClassificationResponse(BaseModel):
    """Response for batch classification"""
    suggestions: List[ProductSuggestionResponse]
    business_model_analysis: BusinessModelAnalysisResponse
    total_products: int
    high_confidence_count: int
    low_confidence_count: int


@router.post(
    route_builder.build_operations_route("classify-product"),
    response_model=ProductSuggestionResponse
)
async def classify_single_product(
    request: ProductClassificationRequest,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    classifier: ProductClassifierService = Depends(get_product_classifier)
):
    """Classify a single product for inventory creation"""
    try:
        suggestion = classifier.classify_product(
            request.product_name,
            request.sales_volume
        )

        response = ProductSuggestionResponse(
            suggestion_id=str(uuid4()),
            original_name=suggestion.original_name,
            suggested_name=suggestion.suggested_name,
            product_type=suggestion.product_type.value,
            category=suggestion.category,
            unit_of_measure=suggestion.unit_of_measure.value,
            confidence_score=suggestion.confidence_score,
            estimated_shelf_life_days=suggestion.estimated_shelf_life_days,
            requires_refrigeration=suggestion.requires_refrigeration,
            requires_freezing=suggestion.requires_freezing,
            is_seasonal=suggestion.is_seasonal,
            suggested_supplier=suggestion.suggested_supplier,
            notes=suggestion.notes
        )

        logger.info("Classified single product",
                   product=request.product_name,
                   classification=suggestion.product_type.value,
                   confidence=suggestion.confidence_score,
                   tenant_id=tenant_id)

        return response

    except Exception as e:
        logger.error("Failed to classify product",
                    error=str(e), product=request.product_name, tenant_id=tenant_id)
        raise HTTPException(status_code=500, detail=f"Classification failed: {str(e)}")


@router.post(
    route_builder.build_operations_route("classify-products-batch"),
    response_model=BatchClassificationResponse
)
async def classify_products_batch(
    request: BatchClassificationRequest,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    classifier: ProductClassifierService = Depends(get_product_classifier)
):
    """Classify multiple products for onboarding automation"""
    try:
        if not request.products:
            raise HTTPException(status_code=400, detail="No products provided for classification")

        product_names = [p.product_name for p in request.products]
        sales_volumes = {p.product_name: p.sales_volume for p in request.products if p.sales_volume}

        suggestions = classifier.classify_products_batch(product_names, sales_volumes)

        suggestion_responses = []
        for suggestion in suggestions:
            suggestion_responses.append(ProductSuggestionResponse(
                suggestion_id=str(uuid4()),
                original_name=suggestion.original_name,
                suggested_name=suggestion.suggested_name,
                product_type=suggestion.product_type.value,
                category=suggestion.category,
                unit_of_measure=suggestion.unit_of_measure.value,
                confidence_score=suggestion.confidence_score,
                estimated_shelf_life_days=suggestion.estimated_shelf_life_days,
                requires_refrigeration=suggestion.requires_refrigeration,
                requires_freezing=suggestion.requires_freezing,
                is_seasonal=suggestion.is_seasonal,
                suggested_supplier=suggestion.suggested_supplier,
                notes=suggestion.notes
            ))

        # Analyze business model
        ingredient_count = sum(1 for s in suggestions if s.product_type.value == 'INGREDIENT')
        finished_count = sum(1 for s in suggestions if s.product_type.value == 'FINISHED_PRODUCT')
        semi_finished_count = sum(1 for s in suggestions if 'semi' in s.suggested_name.lower() or 'frozen' in s.suggested_name.lower() or 'pre' in s.suggested_name.lower())
        total = len(suggestions)
        ingredient_ratio = ingredient_count / total if total > 0 else 0
        semi_finished_ratio = semi_finished_count / total if total > 0 else 0

        if ingredient_ratio >= 0.7:
            model = 'individual_bakery'
        elif ingredient_ratio <= 0.2 and semi_finished_ratio >= 0.3:
            model = 'central_baker_satellite'
        elif ingredient_ratio <= 0.3:
            model = 'retail_bakery'
        else:
            model = 'hybrid_bakery'

        if model == 'individual_bakery':
            confidence = min(ingredient_ratio * 1.2, 0.95)
        elif model == 'central_baker_satellite':
            confidence = min((semi_finished_ratio + (1 - ingredient_ratio)) / 2 * 1.2, 0.95)
        else:
            confidence = max(abs(ingredient_ratio - 0.5) * 2, 0.1)

        recommendations = {
            'individual_bakery': [
                'Set up raw ingredient inventory management',
                'Configure recipe cost calculation and production planning',
                'Enable supplier relationships for flour, yeast, sugar, etc.',
                'Set up full production workflow with proofing and baking schedules',
                'Enable waste tracking for overproduction'
            ],
            'central_baker_satellite': [
                'Configure central baker delivery schedules',
                'Set up semi-finished product inventory (frozen dough, par-baked items)',
                'Enable finish-baking workflow and timing optimization',
                'Track freshness and shelf-life for received products',
                'Focus on customer demand forecasting for final products'
            ],
            'retail_bakery': [
                'Set up finished product supplier relationships',
                'Configure delivery schedule tracking',
                'Enable freshness monitoring and expiration management',
                'Focus on sales forecasting and customer preferences'
            ],
            'hybrid_bakery': [
                'Configure both ingredient and semi-finished product management',
                'Set up flexible production workflows',
                'Enable both supplier and central baker relationships',
                'Configure multi-tier inventory categories'
            ]
        }

        business_model_analysis = BusinessModelAnalysisResponse(
            model=model,
            confidence=confidence,
            ingredient_count=ingredient_count,
            finished_product_count=finished_count,
            ingredient_ratio=ingredient_ratio,
            recommendations=recommendations.get(model, [])
        )

        high_confidence_count = sum(1 for s in suggestions if s.confidence_score >= 0.7)
        low_confidence_count = sum(1 for s in suggestions if s.confidence_score < 0.6)

        response = BatchClassificationResponse(
            suggestions=suggestion_responses,
            business_model_analysis=business_model_analysis,
            total_products=len(suggestions),
            high_confidence_count=high_confidence_count,
            low_confidence_count=low_confidence_count
        )

        logger.info("Batch classification complete",
                   total_products=len(suggestions),
                   business_model=model,
                   high_confidence=high_confidence_count,
                   low_confidence=low_confidence_count,
                   tenant_id=tenant_id)

        return response

    except Exception as e:
        logger.error("Failed batch classification",
                    error=str(e), products_count=len(request.products), tenant_id=tenant_id)
        raise HTTPException(status_code=500, detail=f"Batch classification failed: {str(e)}")


class BatchProductResolutionRequest(BaseModel):
    """Request for batch product resolution or creation"""
    products: List[Dict[str, Any]] = Field(..., description="Products to resolve or create")


class BatchProductResolutionResponse(BaseModel):
    """Response with product name to inventory ID mappings"""
    product_mappings: Dict[str, str] = Field(..., description="Product name to inventory product ID mapping")
    created_count: int = Field(..., description="Number of products created")
    resolved_count: int = Field(..., description="Number of existing products resolved")
    failed_count: int = Field(0, description="Number of products that failed")


@router.post(
    route_builder.build_operations_route("resolve-or-create-products-batch"),
    response_model=BatchProductResolutionResponse
)
async def resolve_or_create_products_batch(
    request: BatchProductResolutionRequest,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db),
    classifier: ProductClassifierService = Depends(get_product_classifier)
):
    """Resolve or create multiple products in a single optimized operation for sales import"""
    try:
        if not request.products:
            raise HTTPException(status_code=400, detail="No products provided")

        service = InventoryService()
        product_mappings = {}
        created_count = 0
        resolved_count = 0
        failed_count = 0

        for product_data in request.products:
            product_name = product_data.get('name', product_data.get('product_name', ''))
            if not product_name:
                failed_count += 1
                continue

            try:
                existing = await service.search_ingredients_by_name(product_name, tenant_id, db)

                if existing:
                    product_mappings[product_name] = str(existing.id)
                    resolved_count += 1
                    logger.debug("Resolved existing product", product=product_name, tenant_id=tenant_id)
                else:
                    # Use the product classifier to determine the appropriate type
                    suggestion = classifier.classify_product(product_name)
                    category = product_data.get('category', suggestion.category if hasattr(suggestion, 'category') else 'general')
                    
                    ingredient_data = {
                        'name': product_name,
                        'type': suggestion.product_type.value if hasattr(suggestion, 'product_type') else 'finished_product',
                        'unit': suggestion.unit_of_measure.value if hasattr(suggestion, 'unit_of_measure') else 'unit',
                        'current_stock': 0,
                        'reorder_point': 0,
                        'cost_per_unit': 0,
                        'category': category
                    }

                    created = await service.create_ingredient_fast(ingredient_data, tenant_id, db)
                    product_mappings[product_name] = str(created.id)
                    created_count += 1
                    logger.debug("Created new product", product=product_name, 
                                product_type=ingredient_data['type'], tenant_id=tenant_id)

            except Exception as e:
                logger.warning("Failed to resolve/create product",
                             product=product_name, error=str(e), tenant_id=tenant_id)
                failed_count += 1
                continue

        logger.info("Batch product resolution complete",
                   total=len(request.products),
                   created=created_count,
                   resolved=resolved_count,
                   failed=failed_count,
                   tenant_id=tenant_id)

        return BatchProductResolutionResponse(
            product_mappings=product_mappings,
            created_count=created_count,
            resolved_count=resolved_count,
            failed_count=failed_count
        )

    except Exception as e:
        logger.error("Batch product resolution failed",
                    error=str(e), tenant_id=tenant_id)
        raise HTTPException(status_code=500, detail=f"Batch resolution failed: {str(e)}")


# ================================================================
# NEW: BATCH API ENDPOINTS FOR ORCHESTRATOR
# ================================================================

class BatchIngredientsRequest(BaseModel):
    """Request for batch ingredient fetching"""
    ingredient_ids: List[UUID] = Field(..., description="List of ingredient IDs to fetch")


class BatchIngredientsResponse(BaseModel):
    """Response with ingredient data"""
    ingredients: List[Dict[str, Any]] = Field(..., description="List of ingredient data")
    found_count: int = Field(..., description="Number of ingredients found")
    missing_ids: List[str] = Field(default_factory=list, description="IDs not found")


@router.post(
    route_builder.build_operations_route("ingredients/batch"),
    response_model=BatchIngredientsResponse
)
async def get_ingredients_batch(
    request: BatchIngredientsRequest,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Fetch multiple ingredients in a single request (for Orchestrator).

    This endpoint reduces N API calls to 1, improving performance when
    the orchestrator needs ingredient data for production/procurement planning.
    """
    try:
        if not request.ingredient_ids:
            return BatchIngredientsResponse(
                ingredients=[],
                found_count=0,
                missing_ids=[]
            )

        service = InventoryService()
        ingredients = []
        found_ids = set()

        for ingredient_id in request.ingredient_ids:
            try:
                ingredient = await service.get_ingredient_by_id(ingredient_id, tenant_id, db)
                if ingredient:
                    ingredients.append({
                        'id': str(ingredient.id),
                        'name': ingredient.name,
                        'type': ingredient.type,
                        'unit': ingredient.unit,
                        'current_stock': float(ingredient.current_stock) if ingredient.current_stock else 0,
                        'reorder_point': float(ingredient.reorder_point) if ingredient.reorder_point else 0,
                        'cost_per_unit': float(ingredient.cost_per_unit) if ingredient.cost_per_unit else 0,
                        'category': ingredient.category,
                        'is_active': ingredient.is_active,
                        'shelf_life_days': ingredient.shelf_life_days
                    })
                    found_ids.add(str(ingredient_id))
            except Exception as e:
                logger.warning(
                    "Failed to fetch ingredient in batch",
                    ingredient_id=str(ingredient_id),
                    error=str(e)
                )
                continue

        missing_ids = [str(id) for id in request.ingredient_ids if str(id) not in found_ids]

        logger.info(
            "Batch ingredient fetch complete",
            requested=len(request.ingredient_ids),
            found=len(ingredients),
            missing=len(missing_ids),
            tenant_id=str(tenant_id)
        )

        return BatchIngredientsResponse(
            ingredients=ingredients,
            found_count=len(ingredients),
            missing_ids=missing_ids
        )

    except Exception as e:
        logger.error(
            "Batch ingredient fetch failed",
            error=str(e),
            tenant_id=str(tenant_id)
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Batch ingredient fetch failed: {str(e)}"
        )


class BatchStockLevelsRequest(BaseModel):
    """Request for batch stock level fetching"""
    ingredient_ids: List[UUID] = Field(..., description="List of ingredient IDs")


class BatchStockLevelsResponse(BaseModel):
    """Response with stock level data"""
    stock_levels: Dict[str, float] = Field(..., description="Ingredient ID to stock level mapping")
    found_count: int = Field(..., description="Number of stock levels found")


@router.post(
    route_builder.build_operations_route("stock-levels/batch"),
    response_model=BatchStockLevelsResponse
)
async def get_stock_levels_batch(
    request: BatchStockLevelsRequest,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Fetch stock levels for multiple ingredients in a single request.

    Optimized endpoint for Orchestrator to quickly check inventory levels
    without making individual API calls per ingredient.
    """
    try:
        if not request.ingredient_ids:
            return BatchStockLevelsResponse(
                stock_levels={},
                found_count=0
            )

        service = InventoryService()
        stock_levels = {}

        for ingredient_id in request.ingredient_ids:
            try:
                ingredient = await service.get_ingredient_by_id(ingredient_id, tenant_id, db)
                if ingredient:
                    stock_levels[str(ingredient_id)] = float(ingredient.current_stock) if ingredient.current_stock else 0.0
            except Exception as e:
                logger.warning(
                    "Failed to fetch stock level in batch",
                    ingredient_id=str(ingredient_id),
                    error=str(e)
                )
                continue

        logger.info(
            "Batch stock level fetch complete",
            requested=len(request.ingredient_ids),
            found=len(stock_levels),
            tenant_id=str(tenant_id)
        )

        return BatchStockLevelsResponse(
            stock_levels=stock_levels,
            found_count=len(stock_levels)
        )

    except Exception as e:
        logger.error(
            "Batch stock level fetch failed",
            error=str(e),
            tenant_id=str(tenant_id)
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Batch stock level fetch failed: {str(e)}"
        )


# ============================================================================
# Tenant Data Deletion Operations (Internal Service Only)
# ============================================================================

from shared.auth.access_control import service_only_access
from shared.services.tenant_deletion import TenantDataDeletionResult
from app.services.tenant_deletion_service import InventoryTenantDeletionService


@router.delete(
    route_builder.build_base_route("tenant/{tenant_id}", include_tenant_prefix=False),
    response_model=dict
)
@service_only_access
async def delete_tenant_data(
    tenant_id: str = Path(..., description="Tenant ID to delete data for"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Delete all inventory data for a tenant (Internal service only)

    This endpoint is called by the orchestrator during tenant deletion.
    It permanently deletes all inventory-related data.

    **WARNING**: This operation is irreversible!

    Returns:
        Deletion summary with counts of deleted records
    """
    try:
        logger.info("inventory.tenant_deletion.api_called", tenant_id=tenant_id)

        deletion_service = InventoryTenantDeletionService(db)
        result = await deletion_service.safe_delete_tenant_data(tenant_id)

        if not result.success:
            raise HTTPException(
                status_code=500,
                detail=f"Tenant data deletion failed: {', '.join(result.errors)}"
            )

        return {
            "message": "Tenant data deletion completed successfully",
            "summary": result.to_dict()
        }

    except HTTPException:
        raise
    except Exception as e:
        logger.error("inventory.tenant_deletion.api_error",
                    tenant_id=tenant_id,
                    error=str(e),
                    exc_info=True)
        raise HTTPException(
            status_code=500,
            detail=f"Failed to delete tenant data: {str(e)}"
        )


@router.get(
    route_builder.build_base_route("tenant/{tenant_id}/deletion-preview", include_tenant_prefix=False),
    response_model=dict
)
@service_only_access
async def preview_tenant_data_deletion(
    tenant_id: str = Path(..., description="Tenant ID to preview deletion for"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Preview what data would be deleted for a tenant (dry-run)

    This endpoint shows counts of all data that would be deleted
    without actually deleting anything.

    Returns:
        Preview with counts of records to be deleted
    """
    try:
        logger.info("inventory.tenant_deletion.preview_called", tenant_id=tenant_id)

        deletion_service = InventoryTenantDeletionService(db)
        preview_data = await deletion_service.get_tenant_data_preview(tenant_id)
        result = TenantDataDeletionResult(tenant_id=tenant_id, service_name=deletion_service.service_name)
        result.deleted_counts = preview_data
        result.success = True

        if not result.success:
            raise HTTPException(
                status_code=500,
                detail=f"Tenant deletion preview failed: {', '.join(result.errors)}"
            )

        return {
            "tenant_id": tenant_id,
            "service": "inventory-service",
            "data_counts": result.deleted_counts,
            "total_items": sum(result.deleted_counts.values())
        }

    except HTTPException:
        raise
    except Exception as e:
        logger.error("inventory.tenant_deletion.preview_error",
                    tenant_id=tenant_id,
                    error=str(e),
                    exc_info=True)
        raise HTTPException(
            status_code=500,
            detail=f"Failed to preview tenant data deletion: {str(e)}"
        )
