"""
Production Batch Repository
Repository for production batch operations
"""

from typing import Optional, List, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, text, desc, func, or_
from datetime import datetime, timedelta, date
from uuid import UUID
import structlog

from .base import ProductionBaseRepository
from app.models.production import ProductionBatch, ProductionStatus, ProductionPriority
from shared.database.exceptions import DatabaseError, ValidationError
from shared.database.transactions import transactional
from shared.utils.batch_generator import BatchCountProvider, BatchNumberGenerator, create_fallback_batch_number

logger = structlog.get_logger()


class ProductionBatchRepository(ProductionBaseRepository, BatchCountProvider):
    """Repository for production batch operations"""
    
    def __init__(self, session: AsyncSession, cache_ttl: Optional[int] = 300):
        # Production batches are dynamic, short cache time (5 minutes)
        super().__init__(ProductionBatch, session, cache_ttl)
    
    async def create_batch(self, batch_data: Dict[str, Any]) -> ProductionBatch:
        """Create a new production batch with validation"""
        try:
            # Validate batch data
            validation_result = self._validate_production_data(
                batch_data, 
                ["tenant_id", "product_id", "product_name", "planned_start_time", 
                 "planned_end_time", "planned_quantity", "planned_duration_minutes"]
            )
            
            if not validation_result["is_valid"]:
                raise ValidationError(f"Invalid batch data: {validation_result['errors']}")
            
            # Generate batch number if not provided
            if "batch_number" not in batch_data or not batch_data["batch_number"]:
                try:
                    batch_generator = BatchNumberGenerator(self)
                    batch_data["batch_number"] = await batch_generator.generate_batch_number(
                        tenant_id=batch_data["tenant_id"],
                        prefix="PROD"
                    )
                    logger.info("Generated production batch number", batch_number=batch_data["batch_number"])
                except Exception as e:
                    # Fallback to a simple batch number if generation fails
                    batch_data["batch_number"] = create_fallback_batch_number("PROD")
                    logger.warning("Used fallback batch number", batch_number=batch_data["batch_number"], error=str(e))
            
            # Set default values
            if "status" not in batch_data:
                batch_data["status"] = ProductionStatus.PENDING
            if "priority" not in batch_data:
                batch_data["priority"] = ProductionPriority.MEDIUM
            if "is_rush_order" not in batch_data:
                batch_data["is_rush_order"] = False
            if "is_special_recipe" not in batch_data:
                batch_data["is_special_recipe"] = False
            
            # Check for duplicate batch number
            if await self.check_duplicate(batch_data["tenant_id"], {"batch_number": batch_data["batch_number"]}):
                raise ValidationError(f"Batch number {batch_data['batch_number']} already exists")
            
            # Create batch
            batch = await self.create(batch_data)
            
            logger.info("Production batch created successfully",
                       batch_id=str(batch.id),
                       batch_number=batch.batch_number,
                       tenant_id=str(batch.tenant_id))
            
            return batch
            
        except ValidationError:
            raise
        except Exception as e:
            logger.error("Error creating production batch", error=str(e))
            raise DatabaseError(f"Failed to create production batch: {str(e)}")
    
    async def get_active_batches(self, tenant_id: str) -> List[ProductionBatch]:
        """Get active production batches for a tenant"""
        try:
            active_statuses = [
                ProductionStatus.PENDING,
                ProductionStatus.IN_PROGRESS,
                ProductionStatus.QUALITY_CHECK,
                ProductionStatus.ON_HOLD
            ]
            
            batches = await self.get_multi(
                filters={
                    "tenant_id": tenant_id,
                    "status__in": active_statuses
                },
                order_by="planned_start_time"
            )
            
            logger.info("Retrieved active production batches",
                       count=len(batches),
                       tenant_id=tenant_id)
            
            return batches
            
        except Exception as e:
            logger.error("Error fetching active batches", error=str(e))
            raise DatabaseError(f"Failed to fetch active batches: {str(e)}")
    
    async def get_batches_by_date_range(
        self, 
        tenant_id: str, 
        start_date: date, 
        end_date: date,
        status: Optional[ProductionStatus] = None
    ) -> List[ProductionBatch]:
        """Get production batches within a date range"""
        try:
            start_datetime = datetime.combine(start_date, datetime.min.time())
            end_datetime = datetime.combine(end_date, datetime.max.time())
            
            filters = {
                "tenant_id": tenant_id,
                "planned_start_time__gte": start_datetime,
                "planned_start_time__lte": end_datetime
            }
            
            if status:
                filters["status"] = status
            
            batches = await self.get_multi(
                filters=filters,
                order_by="planned_start_time"
            )
            
            logger.info("Retrieved batches by date range",
                       count=len(batches),
                       start_date=start_date.isoformat(),
                       end_date=end_date.isoformat(),
                       tenant_id=tenant_id)
            
            return batches
            
        except Exception as e:
            logger.error("Error fetching batches by date range", error=str(e))
            raise DatabaseError(f"Failed to fetch batches by date range: {str(e)}")
    
    async def get_batches_by_product(
        self, 
        tenant_id: str, 
        product_id: str,
        limit: int = 50
    ) -> List[ProductionBatch]:
        """Get production batches for a specific product"""
        try:
            batches = await self.get_multi(
                filters={
                    "tenant_id": tenant_id,
                    "product_id": product_id
                },
                order_by="created_at",
                order_desc=True,
                limit=limit
            )
            
            logger.info("Retrieved batches by product",
                       count=len(batches),
                       product_id=product_id,
                       tenant_id=tenant_id)
            
            return batches
            
        except Exception as e:
            logger.error("Error fetching batches by product", error=str(e))
            raise DatabaseError(f"Failed to fetch batches by product: {str(e)}")
    
    async def update_batch_status(
        self, 
        batch_id: UUID, 
        status: ProductionStatus,
        actual_quantity: Optional[float] = None,
        notes: Optional[str] = None
    ) -> ProductionBatch:
        """Update production batch status"""
        try:
            batch = await self.get(batch_id)
            if not batch:
                raise ValidationError(f"Batch {batch_id} not found")
            
            update_data = {
                "status": status,
                "updated_at": datetime.utcnow()
            }
            
            # Set completion time if completed
            if status == ProductionStatus.COMPLETED:
                update_data["completed_at"] = datetime.utcnow()
                update_data["actual_end_time"] = datetime.utcnow()
                
                if actual_quantity is not None:
                    update_data["actual_quantity"] = actual_quantity
                    # Calculate yield percentage
                    if batch.planned_quantity > 0:
                        update_data["yield_percentage"] = (actual_quantity / batch.planned_quantity) * 100
            
            # Set start time if starting production
            if status == ProductionStatus.IN_PROGRESS and not batch.actual_start_time:
                update_data["actual_start_time"] = datetime.utcnow()
            
            # Add notes
            if notes:
                if status == ProductionStatus.CANCELLED:
                    update_data["cancellation_reason"] = notes
                elif status == ProductionStatus.ON_HOLD:
                    update_data["delay_reason"] = notes
                else:
                    update_data["production_notes"] = notes
            
            batch = await self.update(batch_id, update_data)
            
            logger.info("Updated batch status",
                       batch_id=str(batch_id),
                       new_status=status.value,
                       actual_quantity=actual_quantity)
            
            return batch
            
        except ValidationError:
            raise
        except Exception as e:
            logger.error("Error updating batch status", error=str(e))
            raise DatabaseError(f"Failed to update batch status: {str(e)}")
    
    async def get_production_metrics(
        self, 
        tenant_id: str, 
        start_date: date, 
        end_date: date
    ) -> Dict[str, Any]:
        """Get production metrics for a tenant and date range"""
        try:
            batches = await self.get_batches_by_date_range(tenant_id, start_date, end_date)
            
            total_batches = len(batches)
            completed_batches = len([b for b in batches if b.status == ProductionStatus.COMPLETED])
            in_progress_batches = len([b for b in batches if b.status == ProductionStatus.IN_PROGRESS])
            cancelled_batches = len([b for b in batches if b.status == ProductionStatus.CANCELLED])
            
            # Calculate totals
            total_planned_quantity = sum(b.planned_quantity for b in batches)
            total_actual_quantity = sum(b.actual_quantity or 0 for b in batches if b.actual_quantity)
            
            # Calculate average yield
            completed_with_yield = [b for b in batches if b.yield_percentage is not None]
            avg_yield = (
                sum(b.yield_percentage for b in completed_with_yield) / len(completed_with_yield)
                if completed_with_yield else 0
            )
            
            # Calculate on-time completion rate
            on_time_completed = len([
                b for b in batches 
                if b.status == ProductionStatus.COMPLETED 
                and b.actual_end_time 
                and b.planned_end_time
                and b.actual_end_time <= b.planned_end_time
            ])
            
            on_time_rate = (on_time_completed / completed_batches * 100) if completed_batches > 0 else 0
            
            return {
                "period_start": start_date.isoformat(),
                "period_end": end_date.isoformat(),
                "total_batches": total_batches,
                "completed_batches": completed_batches,
                "in_progress_batches": in_progress_batches,
                "cancelled_batches": cancelled_batches,
                "completion_rate": (completed_batches / total_batches * 100) if total_batches > 0 else 0,
                "total_planned_quantity": total_planned_quantity,
                "total_actual_quantity": total_actual_quantity,
                "average_yield_percentage": round(avg_yield, 2),
                "on_time_completion_rate": round(on_time_rate, 2),
                "tenant_id": tenant_id
            }
            
        except Exception as e:
            logger.error("Error calculating production metrics", error=str(e))
            raise DatabaseError(f"Failed to calculate production metrics: {str(e)}")
    
    async def get_urgent_batches(self, tenant_id: str, hours_ahead: int = 4) -> List[ProductionBatch]:
        """Get batches that need to start within the specified hours"""
        try:
            cutoff_time = datetime.utcnow() + timedelta(hours=hours_ahead)
            
            batches = await self.get_multi(
                filters={
                    "tenant_id": tenant_id,
                    "status": ProductionStatus.PENDING,
                    "planned_start_time__lte": cutoff_time
                },
                order_by="planned_start_time"
            )
            
            logger.info("Retrieved urgent batches",
                       count=len(batches),
                       hours_ahead=hours_ahead,
                       tenant_id=tenant_id)
            
            return batches
            
        except Exception as e:
            logger.error("Error fetching urgent batches", error=str(e))
            raise DatabaseError(f"Failed to fetch urgent batches: {str(e)}")
    
    async def get_daily_batch_count(
        self,
        tenant_id: str,
        date_start: datetime,
        date_end: datetime,
        prefix: Optional[str] = None
    ) -> int:
        """Get the count of production batches created today for the given tenant"""
        try:
            conditions = {
                "tenant_id": tenant_id,
                "created_at__gte": date_start,
                "created_at__lte": date_end
            }

            if prefix:
                # Filter by batch numbers that start with the given prefix
                filters_list = [
                    and_(
                        ProductionBatch.tenant_id == tenant_id,
                        ProductionBatch.created_at >= date_start,
                        ProductionBatch.created_at <= date_end,
                        ProductionBatch.batch_number.like(f"{prefix}-%")
                    )
                ]
                result = await self.session.execute(
                    select(func.count(ProductionBatch.id)).where(and_(*filters_list))
                )
            else:
                batches = await self.get_multi(filters=conditions)
                result_count = len(batches)
                return result_count

            count = result.scalar() or 0

            logger.debug(
                "Retrieved daily production batch count",
                tenant_id=tenant_id,
                prefix=prefix,
                count=count,
                date_start=date_start,
                date_end=date_end
            )

            return count

        except Exception as e:
            logger.error(
                "Failed to get daily production batch count",
                error=str(e),
                tenant_id=tenant_id,
                prefix=prefix
            )
            raise

    async def get_batches_with_filters(
        self,
        tenant_id: str,
        status: Optional[ProductionStatus] = None,
        product_id: Optional[UUID] = None,
        order_id: Optional[UUID] = None,
        start_date: Optional[date] = None,
        end_date: Optional[date] = None,
        page: int = 1,
        page_size: int = 50
    ) -> tuple[List[ProductionBatch], int]:
        """Get production batches with filters and pagination"""
        try:
            filters = {"tenant_id": tenant_id}

            if status:
                filters["status"] = status
            if product_id:
                filters["product_id"] = product_id
            if order_id:
                filters["order_id"] = order_id
            if start_date:
                start_datetime = datetime.combine(start_date, datetime.min.time())
                filters["planned_start_time__gte"] = start_datetime
            if end_date:
                end_datetime = datetime.combine(end_date, datetime.max.time())
                filters["planned_start_time__lte"] = end_datetime

            # Get total count
            total_count = await self.count(filters)

            # Get paginated results
            offset = (page - 1) * page_size
            batches = await self.get_multi(
                filters=filters,
                order_by="planned_start_time",
                order_desc=True,
                limit=page_size,
                offset=offset
            )

            logger.info("Retrieved batches with filters",
                       count=len(batches),
                       total_count=total_count,
                       page=page,
                       page_size=page_size,
                       tenant_id=tenant_id)

            return batches, total_count

        except Exception as e:
            logger.error("Error fetching batches with filters", error=str(e))
            raise DatabaseError(f"Failed to fetch batches with filters: {str(e)}")

    async def update_batch(self, batch_id: UUID, update_data: Dict[str, Any]) -> ProductionBatch:
        """Update a production batch"""
        try:
            batch = await self.get(batch_id)
            if not batch:
                raise ValidationError(f"Batch {batch_id} not found")

            # Add updated timestamp
            update_data["updated_at"] = datetime.utcnow()

            # Update the batch
            batch = await self.update(batch_id, update_data)

            logger.info("Updated production batch",
                       batch_id=str(batch_id),
                       update_fields=list(update_data.keys()))

            return batch

        except ValidationError:
            raise
        except Exception as e:
            logger.error("Error updating production batch", error=str(e))
            raise DatabaseError(f"Failed to update production batch: {str(e)}")

    async def delete_batch(self, batch_id: UUID) -> bool:
        """Delete a production batch"""
        try:
            batch = await self.get(batch_id)
            if not batch:
                raise ValidationError(f"Batch {batch_id} not found")

            # Check if batch can be deleted (not in progress or completed)
            if batch.status in [ProductionStatus.IN_PROGRESS, ProductionStatus.COMPLETED]:
                raise ValidationError(f"Cannot delete batch in {batch.status.value} status")

            success = await self.delete(batch_id)

            logger.info("Deleted production batch",
                       batch_id=str(batch_id),
                       success=success)

            return success

        except ValidationError:
            raise
        except Exception as e:
            logger.error("Error deleting production batch", error=str(e))
            raise DatabaseError(f"Failed to delete production batch: {str(e)}")

    async def start_batch(self, batch_id: UUID) -> ProductionBatch:
        """Start a production batch"""
        try:
            batch = await self.get(batch_id)
            if not batch:
                raise ValidationError(f"Batch {batch_id} not found")

            # Check if batch can be started
            if batch.status != ProductionStatus.PENDING:
                raise ValidationError(f"Cannot start batch in {batch.status.value} status")

            # Update status and start time
            update_data = {
                "status": ProductionStatus.IN_PROGRESS,
                "actual_start_time": datetime.utcnow(),
                "updated_at": datetime.utcnow()
            }

            batch = await self.update(batch_id, update_data)

            logger.info("Started production batch",
                       batch_id=str(batch_id),
                       actual_start_time=batch.actual_start_time)

            return batch

        except ValidationError:
            raise
        except Exception as e:
            logger.error("Error starting production batch", error=str(e))
            raise DatabaseError(f"Failed to start production batch: {str(e)}")

    async def complete_batch(
        self,
        batch_id: UUID,
        completion_data: Optional[Dict[str, Any]] = None
    ) -> ProductionBatch:
        """Complete a production batch"""
        try:
            batch = await self.get(batch_id)
            if not batch:
                raise ValidationError(f"Batch {batch_id} not found")

            # Check if batch can be completed
            if batch.status not in [ProductionStatus.IN_PROGRESS, ProductionStatus.QUALITY_CHECK]:
                raise ValidationError(f"Cannot complete batch in {batch.status.value} status")

            # Prepare completion data
            update_data = {
                "status": ProductionStatus.COMPLETED,
                "actual_end_time": datetime.utcnow(),
                "completed_at": datetime.utcnow(),
                "updated_at": datetime.utcnow()
            }

            # Add optional completion data
            if completion_data:
                if "actual_quantity" in completion_data:
                    update_data["actual_quantity"] = completion_data["actual_quantity"]
                    # Calculate yield percentage
                    if batch.planned_quantity > 0:
                        update_data["yield_percentage"] = (
                            completion_data["actual_quantity"] / batch.planned_quantity
                        ) * 100

                if "notes" in completion_data:
                    update_data["production_notes"] = completion_data["notes"]

                if "quality_score" in completion_data:
                    update_data["quality_score"] = completion_data["quality_score"]

            # Calculate actual duration if start time exists
            if batch.actual_start_time:
                duration = update_data["actual_end_time"] - batch.actual_start_time
                update_data["actual_duration_minutes"] = int(duration.total_seconds() / 60)

            batch = await self.update(batch_id, update_data)

            logger.info("Completed production batch",
                       batch_id=str(batch_id),
                       actual_quantity=update_data.get("actual_quantity"),
                       yield_percentage=update_data.get("yield_percentage"))

            return batch

        except ValidationError:
            raise
        except Exception as e:
            logger.error("Error completing production batch", error=str(e))
            raise DatabaseError(f"Failed to complete production batch: {str(e)}")

    async def get_batch_statistics(
        self,
        tenant_id: str,
        start_date: Optional[date] = None,
        end_date: Optional[date] = None
    ) -> Dict[str, Any]:
        """Get batch statistics for a tenant"""
        try:
            # Use date range or default to last 30 days
            if not start_date:
                start_date = (datetime.utcnow() - timedelta(days=30)).date()
            if not end_date:
                end_date = datetime.utcnow().date()

            batches = await self.get_batches_by_date_range(tenant_id, start_date, end_date)

            total_batches = len(batches)
            completed_batches = len([b for b in batches if b.status == ProductionStatus.COMPLETED])
            failed_batches = len([b for b in batches if b.status == ProductionStatus.FAILED])
            cancelled_batches = len([b for b in batches if b.status == ProductionStatus.CANCELLED])

            # Calculate rates
            completion_rate = (completed_batches / total_batches * 100) if total_batches > 0 else 0

            # Calculate average yield
            completed_with_yield = [b for b in batches if b.yield_percentage is not None]
            average_yield = (
                sum(b.yield_percentage for b in completed_with_yield) / len(completed_with_yield)
                if completed_with_yield else 0
            )

            # Calculate on-time rate
            on_time_completed = len([
                b for b in batches
                if b.status == ProductionStatus.COMPLETED
                and b.actual_end_time
                and b.planned_end_time
                and b.actual_end_time <= b.planned_end_time
            ])
            on_time_rate = (on_time_completed / completed_batches * 100) if completed_batches > 0 else 0

            return {
                "total_batches": total_batches,
                "completed_batches": completed_batches,
                "failed_batches": failed_batches,
                "cancelled_batches": cancelled_batches,
                "completion_rate": round(completion_rate, 2),
                "average_yield": round(average_yield, 2),
                "on_time_rate": round(on_time_rate, 2),
                "period_start": start_date.isoformat(),
                "period_end": end_date.isoformat()
            }

        except Exception as e:
            logger.error("Error calculating batch statistics", error=str(e))
            raise DatabaseError(f"Failed to calculate batch statistics: {str(e)}")

    async def get_batches_filtered(
        self,
        filters: Dict[str, Any],
        page: int,
        page_size: int
    ) -> List[ProductionBatch]:
        """Get batches with filters and pagination"""
        try:
            query = select(ProductionBatch)

            # Apply filters
            if "tenant_id" in filters:
                query = query.where(ProductionBatch.tenant_id == filters["tenant_id"])
            if "status" in filters:
                query = query.where(ProductionBatch.status == filters["status"])
            if "product_id" in filters:
                query = query.where(ProductionBatch.product_id == filters["product_id"])
            if "order_id" in filters:
                query = query.where(ProductionBatch.order_id == filters["order_id"])
            if "start_date" in filters:
                # Filter batches that START on or after this date
                query = query.where(func.date(ProductionBatch.planned_start_time) >= filters["start_date"])
            if "end_date" in filters:
                # Filter batches that START on or before this date
                query = query.where(func.date(ProductionBatch.planned_start_time) <= filters["end_date"])

            # Apply pagination
            offset = (page - 1) * page_size
            query = query.offset(offset).limit(page_size)

            # Order by created_at descending
            query = query.order_by(desc(ProductionBatch.created_at))

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

            return list(batches)

        except Exception as e:
            logger.error("Error getting filtered batches", error=str(e))
            raise DatabaseError(f"Failed to get filtered batches: {str(e)}")

    async def count_batches_filtered(self, filters: Dict[str, Any]) -> int:
        """Count batches with filters"""
        try:
            query = select(func.count(ProductionBatch.id))

            # Apply same filters as get_batches_filtered
            if "tenant_id" in filters:
                query = query.where(ProductionBatch.tenant_id == filters["tenant_id"])
            if "status" in filters:
                query = query.where(ProductionBatch.status == filters["status"])
            if "product_id" in filters:
                query = query.where(ProductionBatch.product_id == filters["product_id"])
            if "order_id" in filters:
                query = query.where(ProductionBatch.order_id == filters["order_id"])
            if "start_date" in filters:
                # Filter batches that START on or after this date
                query = query.where(func.date(ProductionBatch.planned_start_time) >= filters["start_date"])
            if "end_date" in filters:
                # Filter batches that START on or before this date
                query = query.where(func.date(ProductionBatch.planned_start_time) <= filters["end_date"])

            result = await self.session.execute(query)
            count = result.scalar()

            return count or 0

        except Exception as e:
            logger.error("Error counting filtered batches", error=str(e))
            raise DatabaseError(f"Failed to count filtered batches: {str(e)}")

    async def get_waste_analytics(
        self,
        tenant_id: UUID,
        start_date: datetime,
        end_date: datetime
    ) -> Dict[str, Any]:
        """
        Get production waste analytics for sustainability reporting

        Args:
            tenant_id: Tenant UUID
            start_date: Start date for analytics period
            end_date: End date for analytics period

        Returns:
            Dictionary with waste analytics data
        """
        try:
            query = text("""
                SELECT
                    COALESCE(SUM(waste_quantity), 0) as total_production_waste,
                    COALESCE(SUM(defect_quantity), 0) as total_defects,
                    COALESCE(SUM(planned_quantity), 0) as total_planned,
                    COALESCE(SUM(actual_quantity), 0) as total_actual,
                    COUNT(*) as total_batches,
                    COUNT(CASE WHEN is_ai_assisted = true THEN 1 END) as ai_assisted_batches,
                    COALESCE(SUM(CASE WHEN waste_defect_type = 'burnt' THEN waste_quantity ELSE 0 END), 0) as burnt_waste,
                    COALESCE(SUM(CASE WHEN waste_defect_type = 'misshapen' THEN waste_quantity ELSE 0 END), 0) as misshapen_waste,
                    COALESCE(SUM(CASE WHEN waste_defect_type = 'underproofed' THEN waste_quantity ELSE 0 END), 0) as underproofed_waste,
                    COALESCE(SUM(CASE WHEN waste_defect_type = 'temperature_issues' THEN waste_quantity ELSE 0 END), 0) as temperature_waste,
                    COALESCE(SUM(CASE WHEN waste_defect_type = 'expired' THEN waste_quantity ELSE 0 END), 0) as expired_waste
                FROM production_batches
                WHERE tenant_id = :tenant_id
                AND created_at BETWEEN :start_date AND :end_date
                AND status IN ('COMPLETED', 'QUALITY_CHECK')
            """)

            result = await self.session.execute(
                query,
                {
                    'tenant_id': tenant_id,
                    'start_date': start_date,
                    'end_date': end_date
                }
            )
            row = result.fetchone()

            waste_data = {
                'total_production_waste': float(row.total_production_waste or 0),
                'total_defects': float(row.total_defects or 0),
                'total_planned': float(row.total_planned or 0),
                'total_actual': float(row.total_actual or 0),
                'total_batches': int(row.total_batches or 0),
                'ai_assisted_batches': int(row.ai_assisted_batches or 0),
                'waste_by_defect_type': {
                    'burnt': float(row.burnt_waste or 0),
                    'misshapen': float(row.misshapen_waste or 0),
                    'underproofed': float(row.underproofed_waste or 0),
                    'temperature_issues': float(row.temperature_waste or 0),
                    'expired': float(row.expired_waste or 0)
                }
            }

            logger.info(
                "Waste analytics calculated",
                tenant_id=str(tenant_id),
                total_waste=waste_data['total_production_waste'],
                batches=waste_data['total_batches']
            )

            return waste_data

        except Exception as e:
            logger.error("Error calculating waste analytics", error=str(e), tenant_id=str(tenant_id))
            raise DatabaseError(f"Failed to calculate waste analytics: {str(e)}")

    async def get_baseline_metrics(self, tenant_id: UUID) -> Dict[str, Any]:
        """
        Get baseline production metrics from first 90 days

        Used by sustainability service to establish waste baseline
        for SDG 12.3 compliance tracking.

        Args:
            tenant_id: Tenant UUID

        Returns:
            Dictionary with baseline metrics data
        """
        try:
            query = text("""
                WITH first_batch AS (
                    SELECT MIN(created_at) as start_date
                    FROM production_batches
                    WHERE tenant_id = :tenant_id
                ),
                baseline_data AS (
                    SELECT
                        COALESCE(SUM(waste_quantity + defect_quantity), 0) as total_waste,
                        COALESCE(SUM(planned_quantity), 0) as total_production
                    FROM production_batches, first_batch
                    WHERE tenant_id = :tenant_id
                    AND created_at BETWEEN first_batch.start_date
                        AND first_batch.start_date + INTERVAL '90 days'
                    AND status IN ('COMPLETED', 'QUALITY_CHECK')
                )
                SELECT
                    total_waste,
                    total_production,
                    CASE
                        WHEN total_production > 0
                        THEN (total_waste / total_production * 100)
                        ELSE NULL
                    END as waste_percentage,
                    (SELECT start_date FROM first_batch) as baseline_start,
                    (SELECT start_date + INTERVAL '90 days' FROM first_batch) as baseline_end
                FROM baseline_data
            """)

            result = await self.session.execute(query, {'tenant_id': tenant_id})
            row = result.fetchone()

            if row and row.waste_percentage is not None and row.total_production > 100:
                # We have enough data for a real baseline
                baseline_data = {
                    'waste_percentage': float(row.waste_percentage),
                    'total_waste': float(row.total_waste),
                    'total_production': float(row.total_production),
                    'baseline_start': row.baseline_start,
                    'baseline_end': row.baseline_end,
                    'has_baseline': True
                }
            else:
                # Not enough data yet, return defaults
                baseline_data = {
                    'waste_percentage': None,
                    'total_waste': 0,
                    'total_production': 0,
                    'baseline_start': None,
                    'baseline_end': None,
                    'has_baseline': False
                }

            logger.info(
                "Baseline metrics calculated",
                tenant_id=str(tenant_id),
                has_baseline=baseline_data['has_baseline'],
                waste_percentage=baseline_data.get('waste_percentage')
            )

            return baseline_data

        except Exception as e:
            logger.error("Error calculating baseline metrics", error=str(e), tenant_id=str(tenant_id))
            raise DatabaseError(f"Failed to calculate baseline metrics: {str(e)}")

    # ================================================================
    # ALERT-RELATED METHODS (migrated from production_alert_repository)
    # ================================================================

    async def get_capacity_issues(self, tenant_id: Optional[UUID] = None) -> List[Dict[str, Any]]:
        """
        Get production capacity overload issues.
        Returns batches that exceed daily capacity thresholds.

        Args:
            tenant_id: Optional tenant ID to filter by
        """
        try:
            query_str = """
                SELECT
                    pb.tenant_id,
                    DATE(pb.planned_start_time) as planned_date,
                    COUNT(*) as batch_count,
                    SUM(pb.planned_quantity) as total_planned,
                    'capacity_check' as capacity_status,
                    100.0 as capacity_percentage
                FROM production_batches pb
                WHERE pb.planned_start_time >= CURRENT_DATE
                AND pb.planned_start_time <= CURRENT_DATE + INTERVAL '3 days'
                AND pb.status IN ('PENDING', 'IN_PROGRESS')
            """

            params = {}
            if tenant_id:
                query_str += " AND pb.tenant_id = :tenant_id"
                params["tenant_id"] = tenant_id

            query_str += """
                GROUP BY pb.tenant_id, DATE(pb.planned_start_time)
                HAVING COUNT(*) > 10
                ORDER BY total_planned DESC
                LIMIT 20
            """

            result = await self.session.execute(text(query_str), params)
            return [dict(row._mapping) for row in result.fetchall()]

        except Exception as e:
            logger.error("Failed to get capacity issues", error=str(e))
            raise DatabaseError(f"Failed to get capacity issues: {str(e)}")

    async def get_production_delays(self, tenant_id: Optional[UUID] = None) -> List[Dict[str, Any]]:
        """
        Get production batches that are delayed.
        Returns batches in progress past their planned end time.

        Args:
            tenant_id: Optional tenant ID to filter by
        """
        try:
            query_str = """
                SELECT
                    pb.id, pb.tenant_id, pb.product_name, pb.batch_number,
                    pb.planned_end_time as planned_completion_time, pb.actual_start_time,
                    pb.actual_end_time as estimated_completion_time, pb.status,
                    EXTRACT(minutes FROM (NOW() - pb.planned_end_time)) as delay_minutes,
                    COALESCE(pb.priority::text, 'medium') as priority_level,
                    1 as affected_orders
                FROM production_batches pb
                WHERE pb.status = 'IN_PROGRESS'
                AND pb.planned_end_time < NOW()
                AND pb.planned_end_time > NOW() - INTERVAL '24 hours'
            """

            params = {}
            if tenant_id:
                query_str += " AND pb.tenant_id = :tenant_id"
                params["tenant_id"] = tenant_id

            query_str += """
                ORDER BY
                    CASE COALESCE(pb.priority::text, 'MEDIUM')
                        WHEN 'URGENT' THEN 1 WHEN 'HIGH' THEN 2 ELSE 3
                    END,
                    delay_minutes DESC
                LIMIT 50
            """

            result = await self.session.execute(text(query_str), params)
            return [dict(row._mapping) for row in result.fetchall()]

        except Exception as e:
            logger.error("Failed to get production delays", error=str(e))
            raise DatabaseError(f"Failed to get production delays: {str(e)}")

    async def get_batches_with_delayed_start(self, tenant_id: Optional[UUID] = None) -> List[Dict[str, Any]]:
        """
        Get batches that should have started but haven't.
        Returns PENDING batches past their planned start time (with 30 min grace period).
        Only returns batches planned for TODAY to avoid alerting on old batches.

        Args:
            tenant_id: Optional tenant ID to filter by
        """
        try:
            query_str = """
                SELECT
                    pb.id, pb.tenant_id, pb.product_name, pb.batch_number,
                    pb.planned_start_time as scheduled_start_time, pb.status
                FROM production_batches pb
                WHERE pb.status = 'PENDING'
                AND pb.planned_start_time < NOW() - INTERVAL '30 minutes'
                AND pb.actual_start_time IS NULL
                AND pb.planned_start_time >= CURRENT_DATE
                AND pb.planned_start_time < CURRENT_DATE + INTERVAL '1 day'
            """

            params = {}
            if tenant_id:
                query_str += " AND pb.tenant_id = :tenant_id"
                params["tenant_id"] = tenant_id

            query_str += " ORDER BY pb.planned_start_time ASC LIMIT 50"

            result = await self.session.execute(text(query_str), params)
            rows = result.fetchall()

            return [
                {
                    'id': str(row.id),
                    'tenant_id': str(row.tenant_id),
                    'product_name': row.product_name,
                    'batch_number': row.batch_number,
                    'scheduled_start_time': row.scheduled_start_time.isoformat() if row.scheduled_start_time else None
                }
                for row in rows
            ]

        except Exception as e:
            logger.error("Failed to get batches with delayed start", error=str(e))
            raise DatabaseError(f"Failed to get batches with delayed start: {str(e)}")

    async def get_affected_production_batches(self, ingredient_id: str) -> List[str]:
        """
        Get production batches affected by ingredient shortage.
        Returns batch IDs that use the specified ingredient.
        """
        try:
            query = text("""
                SELECT DISTINCT pb.id
                FROM production_batches pb
                JOIN recipe_ingredients ri ON ri.recipe_id = pb.recipe_id
                WHERE ri.ingredient_id = :ingredient_id
                AND pb.status = 'IN_PROGRESS'
                AND pb.planned_completion_time > NOW()
            """)

            result = await self.session.execute(query, {"ingredient_id": ingredient_id})
            return [str(row.id) for row in result.fetchall()]

        except Exception as e:
            logger.error("Failed to get affected production batches", error=str(e), ingredient_id=ingredient_id)
            raise DatabaseError(f"Failed to get affected production batches: {str(e)}")
