# services/recipes/app/repositories/recipe_repository.py
"""
Async recipe repository for database operations
"""

from typing import List, Optional, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, and_, or_
from sqlalchemy.orm import selectinload
from uuid import UUID
from datetime import datetime
import structlog

from shared.database.repository import BaseRepository
from ..models.recipes import Recipe, RecipeIngredient, RecipeStatus
from ..schemas.recipes import RecipeCreate, RecipeUpdate

logger = structlog.get_logger()


class RecipeRepository(BaseRepository[Recipe, RecipeCreate, RecipeUpdate]):
    """Async repository for recipe operations"""

    def __init__(self, session: AsyncSession):
        super().__init__(Recipe, session)

    async def get_recipe_with_ingredients(self, recipe_id: UUID) -> Optional[Dict[str, Any]]:
        """Get recipe with ingredients loaded"""
        result = await self.session.execute(
            select(Recipe)
            .options(selectinload(Recipe.ingredients))
            .where(Recipe.id == recipe_id)
        )
        recipe = result.scalar_one_or_none()

        if not recipe:
            return None

        return {
            "id": str(recipe.id),
            "tenant_id": str(recipe.tenant_id),
            "name": recipe.name,
            "recipe_code": recipe.recipe_code,
            "version": recipe.version,
            "finished_product_id": str(recipe.finished_product_id),
            "description": recipe.description,
            "category": recipe.category,
            "cuisine_type": recipe.cuisine_type,
            "difficulty_level": recipe.difficulty_level,
            "yield_quantity": float(recipe.yield_quantity),
            "yield_unit": recipe.yield_unit.value if hasattr(recipe.yield_unit, 'value') else recipe.yield_unit,
            "prep_time_minutes": recipe.prep_time_minutes,
            "cook_time_minutes": recipe.cook_time_minutes,
            "total_time_minutes": recipe.total_time_minutes,
            "rest_time_minutes": recipe.rest_time_minutes,
            "estimated_cost_per_unit": float(recipe.estimated_cost_per_unit) if recipe.estimated_cost_per_unit else None,
            "last_calculated_cost": float(recipe.last_calculated_cost) if recipe.last_calculated_cost else None,
            "cost_calculation_date": recipe.cost_calculation_date.isoformat() if recipe.cost_calculation_date else None,
            "target_margin_percentage": recipe.target_margin_percentage,
            "suggested_selling_price": float(recipe.suggested_selling_price) if recipe.suggested_selling_price else None,
            "instructions": recipe.instructions,
            "preparation_notes": recipe.preparation_notes,
            "storage_instructions": recipe.storage_instructions,
            "quality_check_configuration": recipe.quality_check_configuration,
            "serves_count": recipe.serves_count,
            "nutritional_info": recipe.nutritional_info,
            "allergen_info": recipe.allergen_info,
            "dietary_tags": recipe.dietary_tags,
            "batch_size_multiplier": float(recipe.batch_size_multiplier),
            "minimum_batch_size": float(recipe.minimum_batch_size) if recipe.minimum_batch_size else None,
            "maximum_batch_size": float(recipe.maximum_batch_size) if recipe.maximum_batch_size else None,
            "optimal_production_temperature": float(recipe.optimal_production_temperature) if recipe.optimal_production_temperature else None,
            "optimal_humidity": float(recipe.optimal_humidity) if recipe.optimal_humidity else None,
            "status": recipe.status.value if hasattr(recipe.status, 'value') else recipe.status,
            "is_seasonal": recipe.is_seasonal,
            "season_start_month": recipe.season_start_month,
            "season_end_month": recipe.season_end_month,
            "is_signature_item": recipe.is_signature_item,
            "created_at": recipe.created_at.isoformat() if recipe.created_at else None,
            "updated_at": recipe.updated_at.isoformat() if recipe.updated_at else None,
            "created_by": str(recipe.created_by) if recipe.created_by else None,
            "updated_by": str(recipe.updated_by) if hasattr(recipe, 'updated_by') and recipe.updated_by else None,
            "ingredients": [
                {
                    "id": str(ingredient.id),
                    "tenant_id": str(ingredient.tenant_id),
                    "recipe_id": str(ingredient.recipe_id),
                    "ingredient_id": str(ingredient.ingredient_id),
                    "quantity": float(ingredient.quantity),
                    "unit": ingredient.unit.value if hasattr(ingredient.unit, 'value') else ingredient.unit,
                    "quantity_in_base_unit": float(ingredient.quantity_in_base_unit) if ingredient.quantity_in_base_unit else None,
                    "alternative_quantity": float(ingredient.alternative_quantity) if ingredient.alternative_quantity else None,
                    "alternative_unit": ingredient.alternative_unit.value if hasattr(ingredient.alternative_unit, 'value') and ingredient.alternative_unit else None,
                    "preparation_method": ingredient.preparation_method,
                    "ingredient_notes": ingredient.ingredient_notes,
                    "is_optional": ingredient.is_optional,
                    "ingredient_order": ingredient.ingredient_order,
                    "ingredient_group": ingredient.ingredient_group,
                    "substitution_options": ingredient.substitution_options,
                    "substitution_ratio": float(ingredient.substitution_ratio) if ingredient.substitution_ratio else None,
                    "unit_cost": float(ingredient.unit_cost) if hasattr(ingredient, 'unit_cost') and ingredient.unit_cost else None,
                    "total_cost": float(ingredient.total_cost) if hasattr(ingredient, 'total_cost') and ingredient.total_cost else None,
                    "cost_updated_at": ingredient.cost_updated_at.isoformat() if hasattr(ingredient, 'cost_updated_at') and ingredient.cost_updated_at else None
                }
                for ingredient in recipe.ingredients
            ] if hasattr(recipe, 'ingredients') else []
        }

    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 multiple filters"""
        query = select(Recipe).where(Recipe.tenant_id == tenant_id)

        # Text search
        if search_term:
            query = query.where(
                or_(
                    Recipe.name.ilike(f"%{search_term}%"),
                    Recipe.description.ilike(f"%{search_term}%")
                )
            )

        # Status filter
        if status:
            query = query.where(Recipe.status == status)

        # Category filter
        if category:
            query = query.where(Recipe.category == category)

        # Seasonal filter
        if is_seasonal is not None:
            query = query.where(Recipe.is_seasonal == is_seasonal)

        # Signature filter
        if is_signature is not None:
            query = query.where(Recipe.is_signature_item == is_signature)

        # Difficulty filter
        if difficulty_level is not None:
            query = query.where(Recipe.difficulty_level == difficulty_level)

        # Apply ordering and pagination
        query = query.order_by(Recipe.name).limit(limit).offset(offset)

        result = await self.session.execute(query)
        recipes = result.scalars().all()

        return [
            {
                "id": str(recipe.id),
                "tenant_id": str(recipe.tenant_id),
                "name": recipe.name,
                "recipe_code": recipe.recipe_code,
                "version": recipe.version,
                "finished_product_id": str(recipe.finished_product_id),
                "description": recipe.description,
                "category": recipe.category,
                "cuisine_type": recipe.cuisine_type,
                "difficulty_level": recipe.difficulty_level,
                "yield_quantity": float(recipe.yield_quantity),
                "yield_unit": recipe.yield_unit.value if hasattr(recipe.yield_unit, 'value') else recipe.yield_unit,
                "prep_time_minutes": recipe.prep_time_minutes,
                "cook_time_minutes": recipe.cook_time_minutes,
                "total_time_minutes": recipe.total_time_minutes,
                "rest_time_minutes": recipe.rest_time_minutes,
                "estimated_cost_per_unit": float(recipe.estimated_cost_per_unit) if recipe.estimated_cost_per_unit else None,
                "last_calculated_cost": float(recipe.last_calculated_cost) if recipe.last_calculated_cost else None,
                "cost_calculation_date": recipe.cost_calculation_date.isoformat() if recipe.cost_calculation_date else None,
                "target_margin_percentage": recipe.target_margin_percentage,
                "suggested_selling_price": float(recipe.suggested_selling_price) if recipe.suggested_selling_price else None,
                "instructions": recipe.instructions,
                "preparation_notes": recipe.preparation_notes,
                "storage_instructions": recipe.storage_instructions,
                "quality_check_configuration": recipe.quality_check_configuration,
                "serves_count": recipe.serves_count,
                "nutritional_info": recipe.nutritional_info,
                "allergen_info": recipe.allergen_info,
                "dietary_tags": recipe.dietary_tags,
                "batch_size_multiplier": float(recipe.batch_size_multiplier),
                "minimum_batch_size": float(recipe.minimum_batch_size) if recipe.minimum_batch_size else None,
                "maximum_batch_size": float(recipe.maximum_batch_size) if recipe.maximum_batch_size else None,
                "optimal_production_temperature": float(recipe.optimal_production_temperature) if recipe.optimal_production_temperature else None,
                "optimal_humidity": float(recipe.optimal_humidity) if recipe.optimal_humidity else None,
                "status": recipe.status.value if hasattr(recipe.status, 'value') else recipe.status,
                "is_seasonal": recipe.is_seasonal,
                "season_start_month": recipe.season_start_month,
                "season_end_month": recipe.season_end_month,
                "is_signature_item": recipe.is_signature_item,
                "created_at": recipe.created_at.isoformat() if recipe.created_at else None,
                "updated_at": recipe.updated_at.isoformat() if recipe.updated_at else None,
                "created_by": str(recipe.created_by) if recipe.created_by else None,
                "updated_by": str(recipe.updated_by) if hasattr(recipe, 'updated_by') and recipe.updated_by else None,
                "ingredients": []  # For list view, don't load ingredients to improve performance
            }
            for recipe in recipes
        ]

    async def get_recipe_statistics(self, tenant_id: UUID) -> Dict[str, Any]:
        """Get recipe statistics for dashboard"""
        # Total recipes
        total_result = await self.session.execute(
            select(func.count(Recipe.id)).where(Recipe.tenant_id == tenant_id)
        )
        total_recipes = total_result.scalar() or 0

        # Active recipes
        active_result = await self.session.execute(
            select(func.count(Recipe.id)).where(
                and_(
                    Recipe.tenant_id == tenant_id,
                    Recipe.status == RecipeStatus.ACTIVE
                )
            )
        )
        active_recipes = active_result.scalar() or 0

        # Signature recipes
        signature_result = await self.session.execute(
            select(func.count(Recipe.id)).where(
                and_(
                    Recipe.tenant_id == tenant_id,
                    Recipe.is_signature_item == True
                )
            )
        )
        signature_recipes = signature_result.scalar() or 0

        # Seasonal recipes
        seasonal_result = await self.session.execute(
            select(func.count(Recipe.id)).where(
                and_(
                    Recipe.tenant_id == tenant_id,
                    Recipe.is_seasonal == True
                )
            )
        )
        seasonal_recipes = seasonal_result.scalar() or 0

        # Category breakdown
        category_result = await self.session.execute(
            select(Recipe.category, func.count(Recipe.id))
            .where(Recipe.tenant_id == tenant_id)
            .group_by(Recipe.category)
        )
        category_data = category_result.all()

        # Convert to list of dicts for the schema
        category_breakdown = [
            {"category": category or "Uncategorized", "count": count}
            for category, count in category_data
        ]

        return {
            "total_recipes": total_recipes,
            "active_recipes": active_recipes,
            "signature_recipes": signature_recipes,
            "seasonal_recipes": seasonal_recipes,
            "category_breakdown": category_breakdown
        }