"""
ML Insights API Endpoints for Inventory Service

Provides endpoints to trigger ML insight generation for:
- Safety stock optimization
- Inventory level recommendations
- Demand pattern analysis
"""

from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel, Field
from typing import Optional, List
from uuid import UUID
from datetime import datetime, timedelta
import structlog
import pandas as pd

from app.core.database import get_db
from sqlalchemy.ext.asyncio import AsyncSession

logger = structlog.get_logger()

router = APIRouter(
    prefix="/api/v1/tenants/{tenant_id}/inventory/ml/insights",
    tags=["ML Insights"]
)


# ================================================================
# REQUEST/RESPONSE SCHEMAS
# ================================================================

class SafetyStockOptimizationRequest(BaseModel):
    """Request schema for safety stock optimization"""
    product_ids: Optional[List[str]] = Field(
        None,
        description="Specific product IDs to optimize. If None, optimizes all products"
    )
    lookback_days: int = Field(
        90,
        description="Days of historical demand to analyze",
        ge=30,
        le=365
    )
    min_history_days: int = Field(
        30,
        description="Minimum days of history required",
        ge=7,
        le=180
    )


class SafetyStockOptimizationResponse(BaseModel):
    """Response schema for safety stock optimization"""
    success: bool
    message: str
    tenant_id: str
    products_optimized: int
    total_insights_generated: int
    total_insights_posted: int
    total_cost_savings: float
    insights_by_product: dict
    errors: List[str] = []


# ================================================================
# API ENDPOINTS
# ================================================================

@router.post("/optimize-safety-stock", response_model=SafetyStockOptimizationResponse)
async def trigger_safety_stock_optimization(
    tenant_id: str,
    request_data: SafetyStockOptimizationRequest,
    request: Request,
    db: AsyncSession = Depends(get_db)
):
    """
    Trigger safety stock optimization for inventory products.

    This endpoint:
    1. Fetches historical demand data for specified products
    2. Runs the SafetyStockInsightsOrchestrator to optimize levels
    3. Generates insights about safety stock recommendations
    4. Posts insights to AI Insights Service
    5. Publishes recommendation events to RabbitMQ

    Args:
        tenant_id: Tenant UUID
        request_data: Optimization parameters
        request: FastAPI request (for app state access)
        db: Database session

    Returns:
        SafetyStockOptimizationResponse with optimization results
    """
    logger.info(
        "ML insights safety stock optimization requested",
        tenant_id=tenant_id,
        product_ids=request_data.product_ids,
        lookback_days=request_data.lookback_days
    )

    try:
        # Import ML orchestrator
        from app.ml.safety_stock_insights_orchestrator import SafetyStockInsightsOrchestrator
        from app.models.inventory import Ingredient
        from sqlalchemy import select

        # Get event publisher from app state (if available)
        event_publisher = getattr(request.app.state, 'event_publisher', None) if hasattr(request, 'app') else None

        # Initialize orchestrator
        orchestrator = SafetyStockInsightsOrchestrator(
            event_publisher=event_publisher
        )

        # Get products to optimize
        if request_data.product_ids:
            query = select(Ingredient).where(
                Ingredient.tenant_id == UUID(tenant_id),
                Ingredient.id.in_([UUID(pid) for pid in request_data.product_ids])
            )
        else:
            query = select(Ingredient).where(
                Ingredient.tenant_id == UUID(tenant_id)
            ).limit(10)  # Limit to prevent timeout

        result = await db.execute(query)
        products = result.scalars().all()

        if not products:
            return SafetyStockOptimizationResponse(
                success=False,
                message="No products found for optimization",
                tenant_id=tenant_id,
                products_optimized=0,
                total_insights_generated=0,
                total_insights_posted=0,
                total_cost_savings=0.0,
                insights_by_product={},
                errors=["No products found"]
            )

        # Calculate date range for demand history
        end_date = datetime.utcnow()
        start_date = end_date - timedelta(days=request_data.lookback_days)

        # Process each product
        total_insights_generated = 0
        total_insights_posted = 0
        total_cost_savings = 0.0
        insights_by_product = {}
        errors = []

        for product in products:
            try:
                product_id = str(product.id)
                logger.info(f"Optimizing safety stock for {product.name} ({product_id})")

                # Fetch real sales/demand history from sales service
                from shared.clients.sales_client import SalesServiceClient
                from app.core.config import settings

                sales_client = SalesServiceClient(settings)

                try:
                    # Fetch sales data for this product
                    sales_data = await sales_client.get_sales_data(
                        tenant_id=tenant_id,
                        product_id=product_id,
                        start_date=start_date.strftime('%Y-%m-%d'),
                        end_date=end_date.strftime('%Y-%m-%d')
                    )

                    if not sales_data:
                        logger.warning(
                            f"No sales history for product {product_id}, skipping"
                        )
                        continue
                    demand_data = []

                    for sale in sales_data:
                        demand_data.append({
                            'date': pd.to_datetime(sale.get('date') or sale.get('sale_date')),
                            'quantity': float(sale.get('quantity', 0))
                        })

                    if not demand_data:
                        logger.warning(
                            f"No valid demand data for product {product_id}, skipping"
                        )
                        continue

                    demand_history = pd.DataFrame(demand_data)

                    # Aggregate by date if there are multiple sales per day
                    demand_history = demand_history.groupby('date').agg({
                        'quantity': 'sum'
                    }).reset_index()

                    if len(demand_history) < request_data.min_history_days:
                        logger.warning(
                            f"Insufficient demand history for product {product_id}: "
                            f"{len(demand_history)} days < {request_data.min_history_days} required"
                        )
                        continue

                except Exception as e:
                    logger.error(
                        f"Error fetching sales data for product {product_id}: {e}",
                        exc_info=True
                    )
                    continue

                # Get lead time from supplier if available
                lead_time_days = 7  # Default fallback
                if product.supplier_id:
                    try:
                        from shared.clients.suppliers_client import SuppliersClient
                        suppliers_client = SuppliersClient()
                        supplier_data = await suppliers_client.get_supplier_by_id(
                            tenant_id=str(tenant_id),
                            supplier_id=str(product.supplier_id)
                        )
                        if supplier_data and 'standard_lead_time' in supplier_data:
                            lead_time_days = supplier_data['standard_lead_time']
                            logger.debug(
                                f"Using supplier lead time for product {product_id}",
                                lead_time=lead_time_days,
                                supplier_id=str(product.supplier_id)
                            )
                    except Exception as e:
                        logger.warning(
                            f"Failed to fetch supplier lead time for product {product_id}, using default",
                            error=str(e),
                            supplier_id=str(product.supplier_id)
                        )

                # Product characteristics
                product_characteristics = {
                    'lead_time_days': lead_time_days,
                    'shelf_life_days': 30 if product.is_perishable else 365,
                    'perishable': product.is_perishable
                }

                # Run optimization
                results = await orchestrator.optimize_and_post_insights(
                    tenant_id=tenant_id,
                    inventory_product_id=product_id,
                    demand_history=demand_history,
                    product_characteristics=product_characteristics,
                    min_history_days=request_data.min_history_days
                )

                # Track results
                total_insights_generated += results['insights_generated']
                total_insights_posted += results['insights_posted']
                if results.get('cost_savings'):
                    total_cost_savings += results['cost_savings']

                insights_by_product[product_id] = {
                    'product_name': product.name,
                    'insights_posted': results['insights_posted'],
                    'optimal_safety_stock': results.get('optimal_safety_stock'),
                    'cost_savings': results.get('cost_savings', 0.0)
                }

                logger.info(
                    f"Product {product_id} optimization complete",
                    insights_posted=results['insights_posted'],
                    cost_savings=results.get('cost_savings', 0)
                )

            except Exception as e:
                error_msg = f"Error optimizing product {product_id}: {str(e)}"
                logger.error(error_msg, exc_info=True)
                errors.append(error_msg)

        # Close orchestrator
        await orchestrator.close()

        # Build response
        response = SafetyStockOptimizationResponse(
            success=total_insights_posted > 0,
            message=f"Successfully optimized {len(products)} products, generated {total_insights_posted} insights",
            tenant_id=tenant_id,
            products_optimized=len(products),
            total_insights_generated=total_insights_generated,
            total_insights_posted=total_insights_posted,
            total_cost_savings=round(total_cost_savings, 2),
            insights_by_product=insights_by_product,
            errors=errors
        )

        logger.info(
            "ML insights safety stock optimization complete",
            tenant_id=tenant_id,
            total_insights=total_insights_posted,
            total_savings=total_cost_savings
        )

        return response

    except Exception as e:
        logger.error(
            "ML insights safety stock optimization failed",
            tenant_id=tenant_id,
            error=str(e),
            exc_info=True
        )
        raise HTTPException(
            status_code=500,
            detail=f"Safety stock optimization failed: {str(e)}"
        )


@router.get("/health")
async def ml_insights_health():
    """Health check for ML insights endpoints"""
    return {
        "status": "healthy",
        "service": "inventory-ml-insights",
        "endpoints": [
            "POST /ml/insights/optimize-safety-stock"
        ]
    }


# ================================================================
# INTERNAL ENDPOINTS (for demo-session service)
# ================================================================

from fastapi import Request

# Create a separate router for internal endpoints to avoid the tenant prefix
internal_router = APIRouter(
    tags=["ML Insights - Internal"]
)


@internal_router.post("/api/v1/tenants/{tenant_id}/inventory/internal/ml/generate-safety-stock-insights")
async def generate_safety_stock_insights_internal(
    tenant_id: str,
    request: Request,
    db: AsyncSession = Depends(get_db)
):
    """
    Internal endpoint to trigger safety stock insights generation for demo sessions.

    This endpoint is called by the demo-session service after cloning data.
    It uses the same ML logic as the public endpoint but with optimized defaults.

    Security: Protected by x-internal-service header check.

    Args:
        tenant_id: The tenant UUID
        request: FastAPI request object
        db: Database session

    Returns:
        {
            "insights_posted": int,
            "tenant_id": str,
            "status": str
        }
    """
    # Verify internal service header
    if not request or request.headers.get("x-internal-service") not in ["demo-session", "internal"]:
        logger.warning("Unauthorized internal API call", tenant_id=tenant_id)
        raise HTTPException(
            status_code=403,
            detail="This endpoint is for internal service use only"
        )

    logger.info("Internal safety stock insights generation triggered", tenant_id=tenant_id)

    try:
        # Use the existing safety stock optimization logic with sensible defaults
        request_data = SafetyStockOptimizationRequest(
            product_ids=None,  # Analyze all products
            lookback_days=90,  # 3 months of history
            min_history_days=30  # Minimum 30 days required
        )

        # Call the existing safety stock optimization endpoint logic
        result = await trigger_safety_stock_optimization(
            tenant_id=tenant_id,
            request_data=request_data,
            request=request,
            db=db
        )

        # Return simplified response for internal use
        return {
            "insights_posted": result.total_insights_posted,
            "tenant_id": tenant_id,
            "status": "success" if result.success else "failed",
            "message": result.message,
            "products_optimized": result.products_optimized,
            "total_cost_savings": result.total_cost_savings
        }

    except Exception as e:
        logger.error(
            "Internal safety stock insights generation failed",
            tenant_id=tenant_id,
            error=str(e),
            exc_info=True
        )
        raise HTTPException(
            status_code=500,
            detail=f"Internal safety stock insights generation failed: {str(e)}"
        )
