"""
Quality Check Repository
Repository for quality check operations
"""

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

from .base import ProductionBaseRepository
from app.models.production import QualityCheck
from shared.database.exceptions import DatabaseError, ValidationError


logger = structlog.get_logger()


class QualityCheckRepository(ProductionBaseRepository):
    """Repository for quality check operations"""
    
    def __init__(self, session: AsyncSession, cache_ttl: Optional[int] = 300):
        # Quality checks are dynamic, short cache time (5 minutes)
        super().__init__(QualityCheck, session, cache_ttl)
    
    async def create_quality_check(self, check_data: Dict[str, Any]) -> QualityCheck:
        """Create a new quality check with validation"""
        try:
            # Validate check data
            validation_result = self._validate_production_data(
                check_data, 
                ["tenant_id", "batch_id", "check_type", "check_time", 
                 "quality_score", "pass_fail"]
            )
            
            if not validation_result["is_valid"]:
                raise ValidationError(f"Invalid quality check data: {validation_result['errors']}")
            
            # Validate quality score range (1-10)
            if check_data.get("quality_score"):
                score = float(check_data["quality_score"])
                if score < 1 or score > 10:
                    raise ValidationError("Quality score must be between 1 and 10")
            
            # Set default values
            if "defect_count" not in check_data:
                check_data["defect_count"] = 0
            if "corrective_action_needed" not in check_data:
                check_data["corrective_action_needed"] = False
            
            # Create quality check
            quality_check = await self.create(check_data)
            
            logger.info("Quality check created successfully",
                       check_id=str(quality_check.id),
                       batch_id=str(quality_check.batch_id),
                       check_type=quality_check.check_type,
                       quality_score=quality_check.quality_score,
                       tenant_id=str(quality_check.tenant_id))
            
            return quality_check
            
        except ValidationError:
            raise
        except Exception as e:
            logger.error("Error creating quality check", error=str(e))
            raise DatabaseError(f"Failed to create quality check: {str(e)}")
    
    async def get_checks_by_batch(
        self, 
        tenant_id: str, 
        batch_id: str
    ) -> List[QualityCheck]:
        """Get all quality checks for a specific batch"""
        try:
            checks = await self.get_multi(
                filters={
                    "tenant_id": tenant_id,
                    "batch_id": batch_id
                },
                order_by="check_time"
            )
            
            logger.info("Retrieved quality checks by batch",
                       count=len(checks),
                       batch_id=batch_id,
                       tenant_id=tenant_id)
            
            return checks
            
        except Exception as e:
            logger.error("Error fetching quality checks by batch", error=str(e))
            raise DatabaseError(f"Failed to fetch quality checks by batch: {str(e)}")
    
    async def get_checks_by_date_range(
        self, 
        tenant_id: str, 
        start_date: date, 
        end_date: date,
        check_type: Optional[str] = None
    ) -> List[QualityCheck]:
        """Get quality checks 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,
                "check_time__gte": start_datetime,
                "check_time__lte": end_datetime
            }
            
            if check_type:
                filters["check_type"] = check_type
            
            checks = await self.get_multi(
                filters=filters,
                order_by="check_time",
                order_desc=True
            )
            
            logger.info("Retrieved quality checks by date range",
                       count=len(checks),
                       start_date=start_date.isoformat(),
                       end_date=end_date.isoformat(),
                       tenant_id=tenant_id)
            
            return checks
            
        except Exception as e:
            logger.error("Error fetching quality checks by date range", error=str(e))
            raise DatabaseError(f"Failed to fetch quality checks by date range: {str(e)}")
    
    async def get_failed_checks(
        self, 
        tenant_id: str, 
        days_back: int = 7
    ) -> List[QualityCheck]:
        """Get failed quality checks from the last N days"""
        try:
            cutoff_date = datetime.utcnow() - timedelta(days=days_back)
            
            checks = await self.get_multi(
                filters={
                    "tenant_id": tenant_id,
                    "pass_fail": False,
                    "check_time__gte": cutoff_date
                },
                order_by="check_time",
                order_desc=True
            )
            
            logger.info("Retrieved failed quality checks",
                       count=len(checks),
                       days_back=days_back,
                       tenant_id=tenant_id)
            
            return checks
            
        except Exception as e:
            logger.error("Error fetching failed quality checks", error=str(e))
            raise DatabaseError(f"Failed to fetch failed quality checks: {str(e)}")
    
    async def get_quality_metrics(
        self, 
        tenant_id: str, 
        start_date: date, 
        end_date: date
    ) -> Dict[str, Any]:
        """Get quality metrics for a tenant and date range"""
        try:
            checks = await self.get_checks_by_date_range(tenant_id, start_date, end_date)
            
            total_checks = len(checks)
            passed_checks = len([c for c in checks if c.pass_fail])
            failed_checks = total_checks - passed_checks
            
            # Calculate average quality score
            quality_scores = [c.quality_score for c in checks if c.quality_score is not None]
            avg_quality_score = sum(quality_scores) / len(quality_scores) if quality_scores else 0
            
            # Calculate defect rate
            total_defects = sum(c.defect_count for c in checks)
            avg_defects_per_check = total_defects / total_checks if total_checks > 0 else 0
            
            # Group by check type
            by_check_type = {}
            for check in checks:
                check_type = check.check_type
                if check_type not in by_check_type:
                    by_check_type[check_type] = {
                        "total_checks": 0,
                        "passed_checks": 0,
                        "failed_checks": 0,
                        "avg_quality_score": 0,
                        "total_defects": 0
                    }
                
                by_check_type[check_type]["total_checks"] += 1
                if check.pass_fail:
                    by_check_type[check_type]["passed_checks"] += 1
                else:
                    by_check_type[check_type]["failed_checks"] += 1
                by_check_type[check_type]["total_defects"] += check.defect_count
            
            # Calculate pass rates by check type
            for type_data in by_check_type.values():
                if type_data["total_checks"] > 0:
                    type_data["pass_rate"] = round(
                        (type_data["passed_checks"] / type_data["total_checks"]) * 100, 2
                    )
                else:
                    type_data["pass_rate"] = 0
                
                type_scores = [c.quality_score for c in checks 
                             if c.check_type == check_type and c.quality_score is not None]
                type_data["avg_quality_score"] = round(
                    sum(type_scores) / len(type_scores) if type_scores else 0, 2
                )
            
            # Identify trends
            checks_needing_action = len([c for c in checks if c.corrective_action_needed])
            
            return {
                "period_start": start_date.isoformat(),
                "period_end": end_date.isoformat(),
                "total_checks": total_checks,
                "passed_checks": passed_checks,
                "failed_checks": failed_checks,
                "pass_rate_percentage": round((passed_checks / total_checks * 100) if total_checks > 0 else 0, 2),
                "average_quality_score": round(avg_quality_score, 2),
                "total_defects": total_defects,
                "average_defects_per_check": round(avg_defects_per_check, 2),
                "checks_needing_corrective_action": checks_needing_action,
                "by_check_type": by_check_type,
                "tenant_id": tenant_id
            }
            
        except Exception as e:
            logger.error("Error calculating quality metrics", error=str(e))
            raise DatabaseError(f"Failed to calculate quality metrics: {str(e)}")
    
    async def get_quality_trends(
        self, 
        tenant_id: str, 
        check_type: str,
        days_back: int = 30
    ) -> Dict[str, Any]:
        """Get quality trends for a specific check type"""
        try:
            end_date = datetime.utcnow().date()
            start_date = end_date - timedelta(days=days_back)
            
            checks = await self.get_checks_by_date_range(
                tenant_id, start_date, end_date, check_type
            )
            
            # Group by date
            daily_metrics = {}
            for check in checks:
                check_date = check.check_time.date()
                if check_date not in daily_metrics:
                    daily_metrics[check_date] = {
                        "total_checks": 0,
                        "passed_checks": 0,
                        "quality_scores": [],
                        "defect_count": 0
                    }
                
                daily_metrics[check_date]["total_checks"] += 1
                if check.pass_fail:
                    daily_metrics[check_date]["passed_checks"] += 1
                if check.quality_score is not None:
                    daily_metrics[check_date]["quality_scores"].append(check.quality_score)
                daily_metrics[check_date]["defect_count"] += check.defect_count
            
            # Calculate daily pass rates and averages
            trend_data = []
            for date_key, metrics in sorted(daily_metrics.items()):
                pass_rate = (metrics["passed_checks"] / metrics["total_checks"] * 100) if metrics["total_checks"] > 0 else 0
                avg_score = sum(metrics["quality_scores"]) / len(metrics["quality_scores"]) if metrics["quality_scores"] else 0
                
                trend_data.append({
                    "date": date_key.isoformat(),
                    "total_checks": metrics["total_checks"],
                    "pass_rate": round(pass_rate, 2),
                    "average_quality_score": round(avg_score, 2),
                    "total_defects": metrics["defect_count"]
                })
            
            # Calculate overall trend direction
            if len(trend_data) >= 2:
                recent_avg = sum(d["pass_rate"] for d in trend_data[-7:]) / min(7, len(trend_data))
                earlier_avg = sum(d["pass_rate"] for d in trend_data[:-7]) / max(1, len(trend_data) - 7)
                trend_direction = "improving" if recent_avg > earlier_avg else "declining" if recent_avg < earlier_avg else "stable"
            else:
                trend_direction = "insufficient_data"
            
            return {
                "check_type": check_type,
                "period_start": start_date.isoformat(),
                "period_end": end_date.isoformat(),
                "trend_direction": trend_direction,
                "daily_data": trend_data,
                "total_checks": len(checks),
                "tenant_id": tenant_id
            }
            
        except Exception as e:
            logger.error("Error calculating quality trends", error=str(e))
            raise DatabaseError(f"Failed to calculate quality trends: {str(e)}")

    async def get_quality_checks_with_filters(
        self,
        tenant_id: str,
        batch_id: Optional[UUID] = None,
        product_id: Optional[UUID] = None,
        start_date: Optional[date] = None,
        end_date: Optional[date] = None,
        pass_fail: Optional[bool] = None,
        page: int = 1,
        page_size: int = 50
    ) -> tuple[List[QualityCheck], int]:
        """Get quality checks with filters and pagination"""
        try:
            filters = {"tenant_id": tenant_id}

            if batch_id:
                filters["batch_id"] = batch_id
            if product_id:
                # Note: This would require a join with production batches to filter by product_id
                # For now, we'll skip this filter or implement it differently
                pass
            if start_date:
                start_datetime = datetime.combine(start_date, datetime.min.time())
                filters["check_time__gte"] = start_datetime
            if end_date:
                end_datetime = datetime.combine(end_date, datetime.max.time())
                filters["check_time__lte"] = end_datetime
            if pass_fail is not None:
                filters["pass_fail"] = pass_fail

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

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

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

            return checks, total_count

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

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

    async def get_quality_issues(self, tenant_id: Optional[UUID] = None) -> List[Dict[str, Any]]:
        """
        Get quality control failures.
        Returns quality checks that failed within recent hours.

        Args:
            tenant_id: Optional tenant ID to filter by
        """
        try:
            from app.models.production import ProductionBatch

            query_str = """
                SELECT
                    qc.id, qc.tenant_id, qc.batch_id, qc.check_type,
                    qc.quality_score, qc.within_tolerance,
                    qc.pass_fail, qc.defect_count,
                    qc.check_notes as qc_severity,
                    1 as total_failures,
                    pb.product_name, pb.batch_number,
                    qc.created_at,
                    qc.process_stage
                FROM quality_checks qc
                JOIN production_batches pb ON pb.id = qc.batch_id
                WHERE qc.pass_fail = false
                AND qc.created_at > NOW() - INTERVAL '4 hours'
                AND qc.corrective_action_needed = true
            """

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

            query_str += """
                ORDER BY
                    CASE
                        WHEN qc.pass_fail = false AND qc.defect_count > 5 THEN 1
                        WHEN qc.pass_fail = false THEN 2
                        ELSE 3
                    END,
                    qc.created_at DESC
            """

            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 quality issues", error=str(e))
            raise DatabaseError(f"Failed to get quality issues: {str(e)}")

    async def mark_quality_check_acknowledged(self, quality_check_id: UUID) -> None:
        """
        Mark a quality check as acknowledged to avoid duplicate alerts.
        """
        try:
            query = text("""
                UPDATE quality_checks
                SET acknowledged = true
                WHERE id = :id
            """)

            await self.session.execute(query, {"id": quality_check_id})
            await self.session.commit()

        except Exception as e:
            logger.error("Failed to mark quality check acknowledged", error=str(e), qc_id=str(quality_check_id))
            raise DatabaseError(f"Failed to mark quality check acknowledged: {str(e)}")