"""
ML Insights API Endpoints for Production Service

Provides endpoints to trigger ML insight generation for:
- Production yield predictions
- Quality optimization
- Process efficiency 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}/production/ml/insights",
    tags=["ML Insights"]
)


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

class YieldPredictionRequest(BaseModel):
    """Request schema for yield prediction"""
    recipe_ids: Optional[List[str]] = Field(
        None,
        description="Specific recipe IDs to analyze. If None, analyzes all recipes"
    )
    lookback_days: int = Field(
        90,
        description="Days of historical production to analyze",
        ge=30,
        le=365
    )
    min_history_runs: int = Field(
        30,
        description="Minimum production runs required",
        ge=10,
        le=100
    )


class YieldPredictionResponse(BaseModel):
    """Response schema for yield prediction"""
    success: bool
    message: str
    tenant_id: str
    recipes_analyzed: int
    total_insights_generated: int
    total_insights_posted: int
    recipes_with_issues: int
    insights_by_recipe: dict
    errors: List[str] = []


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

@router.post("/predict-yields", response_model=YieldPredictionResponse)
async def trigger_yield_prediction(
    tenant_id: str,
    request_data: YieldPredictionRequest,
    request: Request,
    db: AsyncSession = Depends(get_db)
):
    """
    Trigger yield prediction for production recipes.

    This endpoint:
    1. Fetches historical production data for specified recipes
    2. Runs the YieldInsightsOrchestrator to predict yields
    3. Generates insights about yield optimization opportunities
    4. Posts insights to AI Insights Service
    5. Publishes recommendation events to RabbitMQ

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

    Returns:
        YieldPredictionResponse with prediction results
    """
    logger.info(
        "ML insights yield prediction requested",
        tenant_id=tenant_id,
        recipe_ids=request_data.recipe_ids,
        lookback_days=request_data.lookback_days
    )

    try:
        # Import ML orchestrator and clients
        from app.ml.yield_insights_orchestrator import YieldInsightsOrchestrator
        from shared.clients.recipes_client import RecipesServiceClient
        from app.core.config import settings

        # 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 and recipes client
        orchestrator = YieldInsightsOrchestrator(
            event_publisher=event_publisher
        )
        recipes_client = RecipesServiceClient(settings)

        # Get recipes to analyze from recipes service via API
        if request_data.recipe_ids:
            # Fetch specific recipes
            recipes = []
            for recipe_id in request_data.recipe_ids:
                recipe = await recipes_client.get_recipe_by_id(
                    recipe_id=recipe_id,
                    tenant_id=tenant_id
                )
                if recipe:
                    recipes.append(recipe)
        else:
            # Fetch all recipes for tenant (limit to 10)
            all_recipes = await recipes_client.get_all_recipes(tenant_id=tenant_id)
            recipes = all_recipes[:10] if all_recipes else []  # Limit to prevent timeout

        if not recipes:
            return YieldPredictionResponse(
                success=False,
                message="No recipes found for analysis",
                tenant_id=tenant_id,
                recipes_analyzed=0,
                total_insights_generated=0,
                total_insights_posted=0,
                recipes_with_issues=0,
                insights_by_recipe={},
                errors=["No recipes found"]
            )

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

        # Process each recipe
        total_insights_generated = 0
        total_insights_posted = 0
        recipes_with_issues = 0
        insights_by_recipe = {}
        errors = []

        for recipe in recipes:
            try:
                recipe_id = str(recipe['id'])
                recipe_name = recipe.get('name', 'Unknown Recipe')
                logger.info(f"Analyzing yield for {recipe_name} ({recipe_id})")

                # Fetch real production batch history from database
                from app.models.production import ProductionBatch, ProductionStatus
                from sqlalchemy import select

                batch_query = select(ProductionBatch).where(
                    ProductionBatch.tenant_id == UUID(tenant_id),
                    ProductionBatch.recipe_id == UUID(recipe_id),  # Use the extracted UUID
                    ProductionBatch.actual_start_time >= start_date,
                    ProductionBatch.actual_start_time <= end_date,
                    ProductionBatch.status == ProductionStatus.COMPLETED,
                    ProductionBatch.actual_quantity.isnot(None)
                ).order_by(ProductionBatch.actual_start_time)

                batch_result = await db.execute(batch_query)
                batches = batch_result.scalars().all()

                if len(batches) < request_data.min_history_runs:
                    logger.warning(
                        f"Insufficient production history for recipe {recipe_id}: "
                        f"{len(batches)} batches < {request_data.min_history_runs} required"
                    )
                    continue

                # Create production history DataFrame from real batches
                production_data = []
                for batch in batches:
                    # Calculate yield percentage
                    if batch.planned_quantity and batch.actual_quantity:
                        yield_pct = (batch.actual_quantity / batch.planned_quantity) * 100
                    else:
                        continue  # Skip batches without complete data

                    production_data.append({
                        'production_run_id': str(batch.id),  # Required: unique identifier for each production run
                        'recipe_id': str(batch.recipe_id),  # Required: recipe identifier
                        'started_at': batch.actual_start_time,
                        'completed_at': batch.actual_end_time,  # Optional but useful for duration analysis
                        'batch_size': float(batch.planned_quantity),  # Use planned_quantity as batch_size
                        'planned_quantity': float(batch.planned_quantity),
                        'actual_quantity': float(batch.actual_quantity),
                        'yield_percentage': yield_pct,
                        'staff_assigned': batch.staff_assigned if batch.staff_assigned else ['unknown'],
                        'batch_number': batch.batch_number,
                        'equipment_id': batch.equipment_used[0] if batch.equipment_used and len(batch.equipment_used) > 0 else None,
                        'notes': batch.quality_notes  # Optional quality notes
                    })

                if not production_data:
                    logger.warning(
                        f"No valid production data for recipe {recipe_id}"
                    )
                    continue

                production_history = pd.DataFrame(production_data)

                # Debug: Log DataFrame columns and sample data
                logger.debug(
                    "Production history DataFrame created",
                    recipe_id=recipe_id,
                    columns=list(production_history.columns),
                    sample_data=production_history.head(1).to_dict('records') if len(production_history) > 0 else None
                )

                # Run yield analysis
                results = await orchestrator.analyze_and_post_insights(
                    tenant_id=tenant_id,
                    recipe_id=recipe_id,
                    production_history=production_history,
                    min_history_runs=request_data.min_history_runs
                )

                # Track results
                total_insights_generated += results['insights_generated']
                total_insights_posted += results['insights_posted']

                baseline_stats = results.get('baseline_stats', {})
                mean_yield = baseline_stats.get('mean_yield', 100)
                if mean_yield < 90:
                    recipes_with_issues += 1

                insights_by_recipe[recipe_id] = {
                    'recipe_name': recipe_name,
                    'insights_posted': results['insights_posted'],
                    'mean_yield': mean_yield,
                    'patterns': len(results.get('patterns', []))
                }

                logger.info(
                    f"Recipe {recipe_id} analysis complete",
                    insights_posted=results['insights_posted'],
                    mean_yield=mean_yield
                )

            except Exception as e:
                error_msg = f"Error analyzing recipe {recipe_id}: {str(e)}"
                logger.error(error_msg, exc_info=True)
                errors.append(error_msg)

        # Close orchestrator
        await orchestrator.close()

        # Build response
        response = YieldPredictionResponse(
            success=total_insights_posted > 0,
            message=f"Successfully analyzed {len([r for r in recipes if isinstance(r, dict)])} recipes, generated {total_insights_posted} insights",
            tenant_id=tenant_id,
            recipes_analyzed=len([r for r in recipes if isinstance(r, dict)]),
            total_insights_generated=total_insights_generated,
            total_insights_posted=total_insights_posted,
            recipes_with_issues=recipes_with_issues,
            insights_by_recipe=insights_by_recipe,
            errors=errors
        )

        logger.info(
            "ML insights yield prediction complete",
            tenant_id=tenant_id,
            total_insights=total_insights_posted,
            recipes_with_issues=recipes_with_issues
        )

        return response

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


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


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

# 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}/production/internal/ml/generate-yield-insights")
async def generate_yield_insights_internal(
    tenant_id: str,
    request: Request,
    db: AsyncSession = Depends(get_db)
):
    """
    Internal endpoint to trigger yield 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 yield insights generation triggered", tenant_id=tenant_id)

    try:
        # Use the existing yield prediction logic with sensible defaults
        request_data = YieldPredictionRequest(
            recipe_ids=None,  # Analyze all recipes
            lookback_days=90,  # 3 months of history
            min_history_runs=20  # Minimum 20 production runs required
        )

        # Call the existing yield prediction endpoint logic
        result = await trigger_yield_prediction(
            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,
            "recipes_analyzed": result.recipes_analyzed,
            "recipes_with_issues": result.recipes_with_issues
        }

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