# services/recipes/app/services/recipe_service.py
"""
Service layer for recipe management operations
"""

import logging
from typing import List, Optional, Dict, Any
from uuid import UUID
from datetime import datetime
from sqlalchemy.ext.asyncio import AsyncSession

from ..repositories.recipe_repository import RecipeRepository
from ..schemas.recipes import RecipeCreate, RecipeUpdate

logger = logging.getLogger(__name__)


class RecipeService:
    """Async service for recipe management operations"""

    def __init__(self, session: AsyncSession):
        self.session = session
        self.recipe_repo = RecipeRepository(session)

    async def get_recipe_with_ingredients(self, recipe_id: UUID) -> Optional[Dict[str, Any]]:
        """Get recipe by ID with ingredients"""
        try:
            return await self.recipe_repo.get_recipe_with_ingredients(recipe_id)
        except Exception as e:
            logger.error(f"Error getting recipe {recipe_id}: {e}")
            return None

    async def search_recipes(
        self,
        tenant_id: UUID,
        search_term: Optional[str] = None,
        status: Optional[str] = None,
        category: Optional[str] = None,
        is_seasonal: Optional[bool] = None,
        is_signature: Optional[bool] = None,
        difficulty_level: Optional[int] = None,
        limit: int = 100,
        offset: int = 0
    ) -> List[Dict[str, Any]]:
        """Search recipes with filters"""
        try:
            return await self.recipe_repo.search_recipes(
                tenant_id=tenant_id,
                search_term=search_term,
                status=status,
                category=category,
                is_seasonal=is_seasonal,
                is_signature=is_signature,
                difficulty_level=difficulty_level,
                limit=limit,
                offset=offset
            )
        except Exception as e:
            logger.error(f"Error searching recipes: {e}")
            return []

    async def get_recipe_statistics(self, tenant_id: UUID) -> Dict[str, Any]:
        """Get recipe statistics for dashboard"""
        try:
            return await self.recipe_repo.get_recipe_statistics(tenant_id)
        except Exception as e:
            logger.error(f"Error getting recipe statistics: {e}")
            return {"total_recipes": 0, "active_recipes": 0, "signature_recipes": 0, "seasonal_recipes": 0}

    async def get_deletion_summary(self, recipe_id: UUID) -> Dict[str, Any]:
        """Get summary of what will be affected by deleting this recipe"""
        try:
            from sqlalchemy import select, func
            from ..models.recipes import RecipeIngredient

            # Get recipe info
            recipe = await self.recipe_repo.get_by_id(recipe_id)
            if not recipe:
                return {"success": False, "error": "Recipe not found"}

            # Count recipe ingredients
            ingredients_result = await self.session.execute(
                select(func.count(RecipeIngredient.id))
                .where(RecipeIngredient.recipe_id == recipe_id)
            )
            ingredients_count = ingredients_result.scalar() or 0

            # Count production batches using this recipe (if production tables exist)
            production_batches_count = 0
            try:
                # Try to import production models if they exist
                production_batches_result = await self.session.execute(
                    select(func.count()).select_from(
                        select(1).where(
                            # This would need actual production_batches table reference
                            # For now, set to 0
                        ).subquery()
                    )
                )
                production_batches_count = 0  # Set to 0 for now
            except:
                production_batches_count = 0

            # Count dependent recipes (recipes using this as ingredient) - future feature
            dependent_recipes_count = 0

            # Count affected orders - would need orders service integration
            affected_orders_count = 0

            # Determine if deletion is safe
            warnings = []
            can_delete = True

            if production_batches_count > 0:
                warnings.append(f"Esta receta tiene {production_batches_count} lotes de producción asociados")
                can_delete = False

            if affected_orders_count > 0:
                warnings.append(f"Esta receta está en {affected_orders_count} pedidos")
                can_delete = False

            if dependent_recipes_count > 0:
                warnings.append(f"{dependent_recipes_count} recetas dependen de esta")

            if recipe.status == RecipeStatus.ACTIVE:
                warnings.append("Esta receta está activa. Considera archivarla primero.")

            return {
                "success": True,
                "data": {
                    "recipe_id": str(recipe.id),
                    "recipe_name": recipe.name,
                    "recipe_code": recipe.recipe_code or "",
                    "production_batches_count": production_batches_count,
                    "recipe_ingredients_count": ingredients_count,
                    "dependent_recipes_count": dependent_recipes_count,
                    "affected_orders_count": affected_orders_count,
                    "last_used_date": None,
                    "can_delete": can_delete,
                    "warnings": warnings
                }
            }
        except Exception as e:
            logger.error(f"Error getting deletion summary: {e}")
            return {"success": False, "error": str(e)}

    async def create_recipe(
        self,
        recipe_data: Dict[str, Any],
        ingredients_data: List[Dict[str, Any]],
        created_by: UUID
    ) -> Dict[str, Any]:
        """Create a new recipe with ingredients"""
        from ..models.recipes import Recipe, RecipeIngredient, RecipeStatus

        try:
            # Add metadata
            recipe_data["created_by"] = created_by
            recipe_data["created_at"] = datetime.utcnow()
            recipe_data["updated_at"] = datetime.utcnow()
            recipe_data["status"] = recipe_data.get("status", RecipeStatus.DRAFT)

            # Create Recipe model directly (without ingredients)
            recipe = Recipe(**recipe_data)
            self.session.add(recipe)
            await self.session.flush()  # Get the recipe ID

            # Now create ingredients with the recipe_id and tenant_id
            for ing_data in ingredients_data:
                ingredient = RecipeIngredient(
                    recipe_id=recipe.id,
                    tenant_id=recipe.tenant_id,  # Add tenant_id from recipe
                    **ing_data
                )
                self.session.add(ingredient)

            await self.session.flush()

            # Commit the transaction to persist changes
            await self.session.commit()

            # Get the created recipe with ingredients
            result = await self.recipe_repo.get_recipe_with_ingredients(recipe.id)

            return {
                "success": True,
                "data": result
            }

        except Exception as e:
            logger.error(f"Error creating recipe: {e}")
            await self.session.rollback()
            return {
                "success": False,
                "error": str(e)
            }

    async def update_recipe(
        self,
        recipe_id: UUID,
        recipe_data: Dict[str, Any],
        ingredients_data: Optional[List[Dict[str, Any]]] = None,
        updated_by: UUID = None
    ) -> Dict[str, Any]:
        """Update an existing recipe"""
        try:
            # Check if recipe exists
            existing_recipe = await self.recipe_repo.get_by_id(recipe_id)
            if not existing_recipe:
                return {
                    "success": False,
                    "error": "Recipe not found"
                }

            # Status transition business rules
            if "status" in recipe_data:
                from ..models.recipes import RecipeStatus
                new_status = recipe_data["status"]
                current_status = existing_recipe.status

                # Cannot reactivate discontinued recipes
                if current_status == RecipeStatus.DISCONTINUED:
                    if new_status != RecipeStatus.DISCONTINUED:
                        return {
                            "success": False,
                            "error": "Cannot reactivate a discontinued recipe. Create a new version instead."
                        }

                # Can only archive active or testing recipes
                if new_status == RecipeStatus.ARCHIVED:
                    if current_status not in [RecipeStatus.ACTIVE, RecipeStatus.TESTING]:
                        return {
                            "success": False,
                            "error": "Can only archive active or testing recipes."
                        }

                # Cannot activate drafts without ingredients
                if new_status == RecipeStatus.ACTIVE and current_status == RecipeStatus.DRAFT:
                    # Check if recipe has ingredients
                    from sqlalchemy import select, func
                    from ..models.recipes import RecipeIngredient

                    result = await self.session.execute(
                        select(func.count(RecipeIngredient.id)).where(RecipeIngredient.recipe_id == recipe_id)
                    )
                    ingredient_count = result.scalar()

                    if ingredient_count == 0:
                        return {
                            "success": False,
                            "error": "Cannot activate a recipe without ingredients."
                        }

            # Add metadata
            if updated_by:
                recipe_data["updated_by"] = updated_by
            recipe_data["updated_at"] = datetime.utcnow()

            # Use the shared repository's update method
            recipe_update = RecipeUpdate(**recipe_data)
            updated_recipe = await self.recipe_repo.update(recipe_id, recipe_update)

            # Get the updated recipe with ingredients
            result = await self.recipe_repo.get_recipe_with_ingredients(recipe_id)

            return {
                "success": True,
                "data": result
            }

        except Exception as e:
            logger.error(f"Error updating recipe {recipe_id}: {e}")
            await self.session.rollback()
            return {
                "success": False,
                "error": str(e)
            }

    async def delete_recipe(self, recipe_id: UUID) -> bool:
        """Delete a recipe"""
        try:
            return await self.recipe_repo.delete(recipe_id)
        except Exception as e:
            logger.error(f"Error deleting recipe {recipe_id}: {e}")
            return False

    async def check_recipe_feasibility(self, recipe_id: UUID, batch_multiplier: float = 1.0) -> Dict[str, Any]:
        """Check if recipe can be produced with current inventory"""
        try:
            recipe = await self.recipe_repo.get_recipe_with_ingredients(recipe_id)
            if not recipe:
                return {
                    "success": False,
                    "error": "Recipe not found"
                }

            # Simplified feasibility check - can be enhanced later with inventory service integration
            return {
                "success": True,
                "data": {
                    "recipe_id": str(recipe_id),
                    "recipe_name": recipe["name"],
                    "batch_multiplier": batch_multiplier,
                    "feasible": True,
                    "missing_ingredients": [],
                    "insufficient_ingredients": []
                }
            }

        except Exception as e:
            logger.error(f"Error checking recipe feasibility {recipe_id}: {e}")
            return {
                "success": False,
                "error": str(e)
            }

    async def duplicate_recipe(
        self,
        recipe_id: UUID,
        new_name: str,
        created_by: UUID
    ) -> Dict[str, Any]:
        """Create a duplicate of an existing recipe"""
        try:
            # Get original recipe
            original_recipe = await self.recipe_repo.get_recipe_with_ingredients(recipe_id)
            if not original_recipe:
                return {
                    "success": False,
                    "error": "Recipe not found"
                }

            # Create new recipe data
            new_recipe_data = original_recipe.copy()
            new_recipe_data["name"] = new_name

            # Remove fields that should be auto-generated
            new_recipe_data.pop("id", None)
            new_recipe_data.pop("created_at", None)
            new_recipe_data.pop("updated_at", None)

            # Handle ingredients
            ingredients = new_recipe_data.pop("ingredients", [])

            # Create the duplicate
            result = await self.create_recipe(new_recipe_data, ingredients, created_by)

            return result

        except Exception as e:
            logger.error(f"Error duplicating recipe {recipe_id}: {e}")
            await self.session.rollback()
            return {
                "success": False,
                "error": str(e)
            }

    async def activate_recipe(self, recipe_id: UUID, activated_by: UUID) -> Dict[str, Any]:
        """Activate a recipe for production"""
        try:
            # Check if recipe exists
            recipe = await self.recipe_repo.get_recipe_with_ingredients(recipe_id)
            if not recipe:
                return {
                    "success": False,
                    "error": "Recipe not found"
                }

            if not recipe.get("ingredients"):
                return {
                    "success": False,
                    "error": "Recipe must have at least one ingredient"
                }

            # Update recipe status
            update_data = {
                "status": "active",
                "updated_by": activated_by,
                "updated_at": datetime.utcnow()
            }

            recipe_update = RecipeUpdate(**update_data)
            await self.recipe_repo.update(recipe_id, recipe_update)

            # Get the updated recipe
            result = await self.recipe_repo.get_recipe_with_ingredients(recipe_id)

            return {
                "success": True,
                "data": result
            }

        except Exception as e:
            logger.error(f"Error activating recipe {recipe_id}: {e}")
            return {
                "success": False,
                "error": str(e)
            }

    # Quality Configuration Methods

    async def update_recipe_quality_configuration(
        self,
        tenant_id: UUID,
        recipe_id: UUID,
        quality_config_update: Dict[str, Any],
        user_id: UUID
    ) -> Dict[str, Any]:
        """Update quality configuration for a recipe"""
        try:
            # Get current recipe
            recipe = await self.recipe_repo.get_recipe(tenant_id, recipe_id)
            if not recipe:
                raise ValueError("Recipe not found")

            # Get existing quality configuration or create default
            current_config = recipe.get("quality_check_configuration", {
                "stages": {},
                "overall_quality_threshold": 7.0,
                "critical_stage_blocking": True,
                "auto_create_quality_checks": True,
                "quality_manager_approval_required": False
            })

            # Merge with updates
            if "stages" in quality_config_update:
                current_config["stages"].update(quality_config_update["stages"])

            for key in ["overall_quality_threshold", "critical_stage_blocking",
                       "auto_create_quality_checks", "quality_manager_approval_required"]:
                if key in quality_config_update:
                    current_config[key] = quality_config_update[key]

            # Update recipe with new configuration
            recipe_update = RecipeUpdate(quality_check_configuration=current_config)
            await self.recipe_repo.update_recipe(tenant_id, recipe_id, recipe_update, user_id)

            # Return updated recipe
            updated_recipe = await self.recipe_repo.get_recipe(tenant_id, recipe_id)
            return updated_recipe

        except Exception as e:
            logger.error(f"Error updating recipe quality configuration: {e}")
            raise

    async def add_quality_templates_to_stage(
        self,
        tenant_id: UUID,
        recipe_id: UUID,
        stage: str,
        template_ids: List[UUID],
        user_id: UUID
    ):
        """Add quality templates to a specific recipe stage"""
        try:
            # Get current recipe
            recipe = await self.recipe_repo.get_recipe(tenant_id, recipe_id)
            if not recipe:
                raise ValueError("Recipe not found")

            # Get existing quality configuration
            quality_config = recipe.get("quality_check_configuration", {"stages": {}})

            # Initialize stage if it doesn't exist
            if stage not in quality_config["stages"]:
                quality_config["stages"][stage] = {
                    "template_ids": [],
                    "required_checks": [],
                    "optional_checks": [],
                    "blocking_on_failure": True,
                    "min_quality_score": None
                }

            # Add template IDs (avoid duplicates)
            stage_config = quality_config["stages"][stage]
            existing_ids = set(stage_config.get("template_ids", []))
            new_ids = [str(tid) for tid in template_ids if str(tid) not in existing_ids]
            stage_config["template_ids"].extend(new_ids)

            # Update recipe
            recipe_update = RecipeUpdate(quality_check_configuration=quality_config)
            await self.recipe_repo.update_recipe(tenant_id, recipe_id, recipe_update, user_id)

        except Exception as e:
            logger.error(f"Error adding quality templates to stage: {e}")
            raise

    async def remove_quality_template_from_stage(
        self,
        tenant_id: UUID,
        recipe_id: UUID,
        stage: str,
        template_id: UUID,
        user_id: UUID
    ):
        """Remove a quality template from a specific recipe stage"""
        try:
            # Get current recipe
            recipe = await self.recipe_repo.get_recipe(tenant_id, recipe_id)
            if not recipe:
                raise ValueError("Recipe not found")

            # Get existing quality configuration
            quality_config = recipe.get("quality_check_configuration", {"stages": {}})

            # Remove template ID from stage
            if stage in quality_config["stages"]:
                stage_config = quality_config["stages"][stage]
                template_ids = stage_config.get("template_ids", [])
                template_ids = [tid for tid in template_ids if str(tid) != str(template_id)]
                stage_config["template_ids"] = template_ids

                # Update recipe
                recipe_update = RecipeUpdate(quality_check_configuration=quality_config)
                await self.recipe_repo.update_recipe(tenant_id, recipe_id, recipe_update, user_id)

        except Exception as e:
            logger.error(f"Error removing quality template from stage: {e}")
            raise