# ================================================================
# services/inventory/app/services/food_safety_service.py
# ================================================================
"""
Food Safety Service - Business logic for food safety and compliance
"""

import uuid
from datetime import datetime, timedelta
from decimal import Decimal
from typing import List, Optional, Dict, Any
from uuid import UUID
import structlog

from shared.database.transactions import transactional

from app.core.config import settings
from app.models.food_safety import (
    FoodSafetyCompliance,
    TemperatureLog,
    FoodSafetyAlert,
    FoodSafetyStandard,
    ComplianceStatus,
    FoodSafetyAlertType
)
from app.repositories.food_safety_repository import FoodSafetyRepository
from app.schemas.food_safety import (
    FoodSafetyComplianceCreate,
    FoodSafetyComplianceUpdate,
    FoodSafetyComplianceResponse,
    TemperatureLogCreate,
    TemperatureLogResponse,
    FoodSafetyAlertCreate,
    FoodSafetyAlertUpdate,
    FoodSafetyAlertResponse,
    FoodSafetyMetrics,
    TemperatureAnalytics
)
from app.schemas.dashboard import FoodSafetyDashboard, TemperatureMonitoringStatus

logger = structlog.get_logger()


class FoodSafetyService:
    """Service for food safety and compliance operations"""

    def __init__(self):
        pass

    def _get_repository(self, db) -> FoodSafetyRepository:
        """Get repository instance for the current database session"""
        return FoodSafetyRepository(db)
    
    # ===== COMPLIANCE MANAGEMENT =====
    
    @transactional
    async def create_compliance_record(
        self,
        db,
        compliance_data: FoodSafetyComplianceCreate,
        user_id: Optional[UUID] = None
    ) -> FoodSafetyComplianceResponse:
        """Create a new food safety compliance record"""
        try:
            logger.info("Creating compliance record", 
                       ingredient_id=str(compliance_data.ingredient_id),
                       standard=compliance_data.standard)
            
            # Validate compliance data
            await self._validate_compliance_data(db, compliance_data)
            
            # Create compliance record
            compliance = FoodSafetyCompliance(
                tenant_id=compliance_data.tenant_id,
                ingredient_id=compliance_data.ingredient_id,
                standard=FoodSafetyStandard(compliance_data.standard),
                compliance_status=ComplianceStatus(compliance_data.compliance_status),
                certification_number=compliance_data.certification_number,
                certifying_body=compliance_data.certifying_body,
                certification_date=compliance_data.certification_date,
                expiration_date=compliance_data.expiration_date,
                requirements=compliance_data.requirements,
                compliance_notes=compliance_data.compliance_notes,
                documentation_url=compliance_data.documentation_url,
                last_audit_date=compliance_data.last_audit_date,
                next_audit_date=compliance_data.next_audit_date,
                auditor_name=compliance_data.auditor_name,
                audit_score=compliance_data.audit_score,
                risk_level=compliance_data.risk_level,
                risk_factors=compliance_data.risk_factors,
                mitigation_measures=compliance_data.mitigation_measures,
                requires_monitoring=compliance_data.requires_monitoring,
                monitoring_frequency_days=compliance_data.monitoring_frequency_days,
                created_by=user_id,
                updated_by=user_id
            )
            
            # Create compliance record using repository
            repo = self._get_repository(db)
            compliance = await repo.create_compliance(compliance)
            
            # Check for compliance alerts
            await self._check_compliance_alerts(db, compliance)
            
            logger.info("Compliance record created", 
                       compliance_id=str(compliance.id))
            
            return FoodSafetyComplianceResponse(**compliance.to_dict())
            
        except Exception as e:
            logger.error("Failed to create compliance record", error=str(e))
            raise
    
    @transactional
    async def update_compliance_record(
        self,
        db,
        compliance_id: UUID,
        tenant_id: UUID,
        compliance_data: FoodSafetyComplianceUpdate,
        user_id: Optional[UUID] = None
    ) -> Optional[FoodSafetyComplianceResponse]:
        """Update an existing compliance record"""
        try:
            # Get existing compliance record using repository
            repo = self._get_repository(db)
            compliance = await repo.get_compliance_by_id(compliance_id, tenant_id)
            if not compliance:
                return None
            
            # Update fields
            update_fields = compliance_data.dict(exclude_unset=True)
            for field, value in update_fields.items():
                if hasattr(compliance, field):
                    if field in ['compliance_status'] and value:
                        setattr(compliance, field, ComplianceStatus(value))
                    else:
                        setattr(compliance, field, value)
            
            compliance.updated_by = user_id

            # Update compliance record using repository
            compliance = await repo.update_compliance(compliance)
            
            # Check for compliance alerts after update
            await self._check_compliance_alerts(db, compliance)
            
            logger.info("Compliance record updated", 
                       compliance_id=str(compliance.id))
            
            return FoodSafetyComplianceResponse(**compliance.to_dict())
            
        except Exception as e:
            logger.error("Failed to update compliance record", 
                        compliance_id=str(compliance_id), 
                        error=str(e))
            raise
    
    # ===== TEMPERATURE MONITORING =====
    
    @transactional
    async def log_temperature(
        self,
        db,
        temp_data: TemperatureLogCreate,
        user_id: Optional[UUID] = None
    ) -> TemperatureLogResponse:
        """Log a temperature reading"""
        try:
            # Determine if temperature is within range
            is_within_range = self._is_temperature_within_range(
                temp_data.temperature_celsius,
                temp_data.target_temperature_min,
                temp_data.target_temperature_max,
                temp_data.storage_location
            )
            
            # Create temperature log
            temp_log = TemperatureLog(
                tenant_id=temp_data.tenant_id,
                storage_location=temp_data.storage_location,
                warehouse_zone=temp_data.warehouse_zone,
                equipment_id=temp_data.equipment_id,
                temperature_celsius=temp_data.temperature_celsius,
                humidity_percentage=temp_data.humidity_percentage,
                target_temperature_min=temp_data.target_temperature_min,
                target_temperature_max=temp_data.target_temperature_max,
                is_within_range=is_within_range,
                alert_triggered=not is_within_range,
                measurement_method=temp_data.measurement_method,
                device_id=temp_data.device_id,
                calibration_date=temp_data.calibration_date,
                recorded_by=user_id
            )
            
            db.add(temp_log)
            await db.flush()
            await db.refresh(temp_log)
            
            # Create alert if temperature is out of range
            if not is_within_range:
                await self._create_temperature_alert(db, temp_log)
            
            logger.info("Temperature logged", 
                       location=temp_data.storage_location,
                       temperature=temp_data.temperature_celsius,
                       within_range=is_within_range)
            
            return TemperatureLogResponse(**temp_log.to_dict())
            
        except Exception as e:
            logger.error("Failed to log temperature", error=str(e))
            raise
    
    @transactional
    async def bulk_log_temperatures(
        self,
        db,
        temp_readings: List[TemperatureLogCreate],
        user_id: Optional[UUID] = None
    ) -> List[TemperatureLogResponse]:
        """Bulk log temperature readings"""
        try:
            results = []
            alerts_to_create = []
            
            for temp_data in temp_readings:
                # Determine if temperature is within range
                is_within_range = self._is_temperature_within_range(
                    temp_data.temperature_celsius,
                    temp_data.target_temperature_min,
                    temp_data.target_temperature_max,
                    temp_data.storage_location
                )
                
                # Create temperature log
                temp_log = TemperatureLog(
                    tenant_id=temp_data.tenant_id,
                    storage_location=temp_data.storage_location,
                    warehouse_zone=temp_data.warehouse_zone,
                    equipment_id=temp_data.equipment_id,
                    temperature_celsius=temp_data.temperature_celsius,
                    humidity_percentage=temp_data.humidity_percentage,
                    target_temperature_min=temp_data.target_temperature_min,
                    target_temperature_max=temp_data.target_temperature_max,
                    is_within_range=is_within_range,
                    alert_triggered=not is_within_range,
                    measurement_method=temp_data.measurement_method,
                    device_id=temp_data.device_id,
                    calibration_date=temp_data.calibration_date,
                    recorded_by=user_id
                )
                
                db.add(temp_log)
                
                if not is_within_range:
                    alerts_to_create.append(temp_log)
                
                results.append(TemperatureLogResponse(**temp_log.to_dict()))
            
            await db.flush()
            
            # Create alerts for out-of-range temperatures
            for temp_log in alerts_to_create:
                await self._create_temperature_alert(db, temp_log)
            
            logger.info("Bulk temperature logging completed", 
                       count=len(temp_readings),
                       violations=len(alerts_to_create))
            
            return results
            
        except Exception as e:
            logger.error("Failed to bulk log temperatures", error=str(e))
            raise
    
    # ===== ALERT MANAGEMENT =====
    
    @transactional
    async def create_food_safety_alert(
        self,
        db,
        alert_data: FoodSafetyAlertCreate,
        user_id: Optional[UUID] = None
    ) -> FoodSafetyAlertResponse:
        """Create a food safety alert"""
        try:
            alert = FoodSafetyAlert(
                tenant_id=alert_data.tenant_id,
                alert_code=alert_data.alert_code,
                alert_type=FoodSafetyAlertType(alert_data.alert_type),
                severity=alert_data.severity,
                risk_level=alert_data.risk_level,
                source_entity_type=alert_data.source_entity_type,
                source_entity_id=alert_data.source_entity_id,
                ingredient_id=alert_data.ingredient_id,
                stock_id=alert_data.stock_id,
                title=alert_data.title,
                description=alert_data.description,
                detailed_message=alert_data.detailed_message,
                regulatory_requirement=alert_data.regulatory_requirement,
                compliance_standard=FoodSafetyStandard(alert_data.compliance_standard) if alert_data.compliance_standard else None,
                regulatory_action_required=alert_data.regulatory_action_required,
                trigger_condition=alert_data.trigger_condition,
                threshold_value=alert_data.threshold_value,
                actual_value=alert_data.actual_value,
                alert_data=alert_data.alert_data,
                environmental_factors=alert_data.environmental_factors,
                affected_products=alert_data.affected_products,
                public_health_risk=alert_data.public_health_risk,
                business_impact=alert_data.business_impact,
                estimated_loss=alert_data.estimated_loss,
                first_occurred_at=datetime.now(),
                last_occurred_at=datetime.now(),
                created_by=user_id
            )
            
            db.add(alert)
            await db.flush()
            await db.refresh(alert)
            
            # Send notifications
            await self._send_alert_notifications(alert)
            
            logger.info("Food safety alert created", 
                       alert_id=str(alert.id),
                       alert_type=alert_data.alert_type,
                       severity=alert_data.severity)
            
            return FoodSafetyAlertResponse(**alert.to_dict())
            
        except Exception as e:
            logger.error("Failed to create food safety alert", error=str(e))
            raise
    
    # ===== DASHBOARD AND ANALYTICS =====
    
    async def get_food_safety_dashboard(
        self,
        db,
        tenant_id: UUID
    ) -> FoodSafetyDashboard:
        """Get food safety dashboard data"""
        try:
            # Get repository instance
            repo = self._get_repository(db)

            # Get compliance overview using repository
            compliance_stats = await repo.get_compliance_stats(tenant_id)
            total_compliance = compliance_stats["total"]
            compliant_items = compliance_stats["compliant"]
            compliance_percentage = (compliant_items / total_compliance * 100) if total_compliance > 0 else 0

            # Get temperature monitoring status using repository
            temp_stats = await repo.get_temperature_stats(tenant_id)

            # Get expiration tracking using repository
            expiration_stats = await repo.get_expiration_stats(tenant_id)

            # Get alert counts using repository
            alert_stats = await repo.get_alert_stats(tenant_id)
            
            return FoodSafetyDashboard(
                total_compliance_items=total_compliance,
                compliant_items=compliant_items,
                non_compliant_items=compliance_stats["non_compliant"],
                pending_review_items=compliance_stats["pending_review"],
                compliance_percentage=Decimal(str(compliance_percentage)),
                temperature_sensors_online=temp_stats["sensors_online"],
                temperature_sensors_total=temp_stats["sensors_online"],  # Would need actual count
                temperature_violations_24h=temp_stats["violations_24h"],
                current_temperature_status="normal",  # Would need to calculate
                items_expiring_today=expiration_stats["expiring_today"],
                items_expiring_this_week=expiration_stats["expiring_week"],
                expired_items_requiring_action=expiration_stats["expired_requiring_action"],
                upcoming_audits=0,  # Would need to calculate
                overdue_audits=0,  # Would need to calculate
                certifications_valid=compliant_items,
                certifications_expiring_soon=0,  # Would need to calculate
                high_risk_items=alert_stats["high_risk"],
                critical_alerts=alert_stats["critical"],
                regulatory_notifications_pending=alert_stats["regulatory_pending"],
                recent_safety_incidents=[]  # Would need to get recent incidents
            )
            
        except Exception as e:
            logger.error("Failed to get food safety dashboard", error=str(e))
            raise
    
    # ===== PRIVATE HELPER METHODS =====
    
    async def _validate_compliance_data(self, db, compliance_data: FoodSafetyComplianceCreate):
        """Validate compliance data for business rules"""
        # Check if ingredient exists using repository
        repo = self._get_repository(db)
        ingredient_exists = await repo.validate_ingredient_exists(
            compliance_data.ingredient_id,
            compliance_data.tenant_id
        )

        if not ingredient_exists:
            raise ValueError("Ingredient not found")
        
        # Validate standard
        try:
            FoodSafetyStandard(compliance_data.standard)
        except ValueError:
            raise ValueError(f"Invalid food safety standard: {compliance_data.standard}")
        
        # Validate compliance status
        try:
            ComplianceStatus(compliance_data.compliance_status)
        except ValueError:
            raise ValueError(f"Invalid compliance status: {compliance_data.compliance_status}")
    
    def _is_temperature_within_range(
        self,
        temperature: float,
        target_min: Optional[float],
        target_max: Optional[float],
        location: str
    ) -> bool:
        """Check if temperature is within acceptable range"""
        # Use target ranges if provided, otherwise use default ranges
        if target_min is not None and target_max is not None:
            return target_min <= temperature <= target_max
        
        # Default ranges based on location type
        if "freezer" in location.lower():
            return settings.FREEZER_TEMP_MIN <= temperature <= settings.FREEZER_TEMP_MAX
        elif "refrigerat" in location.lower() or "fridge" in location.lower():
            return settings.REFRIGERATION_TEMP_MIN <= temperature <= settings.REFRIGERATION_TEMP_MAX
        else:
            return settings.ROOM_TEMP_MIN <= temperature <= settings.ROOM_TEMP_MAX
    
    async def _create_temperature_alert(self, db, temp_log: TemperatureLog):
        """Create an alert for temperature violation"""
        try:
            alert_code = f"TEMP-{uuid.uuid4().hex[:8].upper()}"
            
            # Determine severity based on deviation
            target_min = temp_log.target_temperature_min or 0
            target_max = temp_log.target_temperature_max or 25
            deviation = max(
                abs(temp_log.temperature_celsius - target_min),
                abs(temp_log.temperature_celsius - target_max)
            )
            
            if deviation > 10:
                severity = "critical"
            elif deviation > 5:
                severity = "high"
            else:
                severity = "medium"
            
            alert = FoodSafetyAlert(
                tenant_id=temp_log.tenant_id,
                alert_code=alert_code,
                alert_type=FoodSafetyAlertType.TEMPERATURE_VIOLATION,
                severity=severity,
                risk_level="high" if severity == "critical" else "medium",
                source_entity_type="temperature_log",
                source_entity_id=temp_log.id,
                title=f"Temperature violation in {temp_log.storage_location}",
                description=f"Temperature reading of {temp_log.temperature_celsius}°C is outside acceptable range",
                regulatory_action_required=severity == "critical",
                trigger_condition="temperature_out_of_range",
                threshold_value=target_max,
                actual_value=temp_log.temperature_celsius,
                alert_data={
                    "location": temp_log.storage_location,
                    "equipment_id": temp_log.equipment_id,
                    "target_range": f"{target_min}°C - {target_max}°C"
                },
                environmental_factors={
                    "temperature": temp_log.temperature_celsius,
                    "humidity": temp_log.humidity_percentage
                },
                first_occurred_at=datetime.now(),
                last_occurred_at=datetime.now()
            )
            
            db.add(alert)
            await db.flush()
            
            # Send notifications
            await self._send_alert_notifications(alert)
            
        except Exception as e:
            logger.error("Failed to create temperature alert", error=str(e))
    
    async def _check_compliance_alerts(self, db, compliance: FoodSafetyCompliance):
        """Check for compliance-related alerts"""
        try:
            alerts_to_create = []
            
            # Check for expiring certifications
            if compliance.expiration_date:
                days_to_expiry = (compliance.expiration_date - datetime.now()).days
                if days_to_expiry <= settings.CERTIFICATION_EXPIRY_WARNING_DAYS:
                    alert_code = f"CERT-{uuid.uuid4().hex[:8].upper()}"
                    severity = "critical" if days_to_expiry <= 7 else "high"
                    
                    alert = FoodSafetyAlert(
                        tenant_id=compliance.tenant_id,
                        alert_code=alert_code,
                        alert_type=FoodSafetyAlertType.CERTIFICATION_EXPIRY,
                        severity=severity,
                        risk_level="high",
                        source_entity_type="compliance",
                        source_entity_id=compliance.id,
                        ingredient_id=compliance.ingredient_id,
                        title=f"Certification expiring soon - {compliance.standard.value}",
                        description=f"Certification expires in {days_to_expiry} days",
                        regulatory_action_required=True,
                        compliance_standard=compliance.standard,
                        first_occurred_at=datetime.now(),
                        last_occurred_at=datetime.now()
                    )
                    alerts_to_create.append(alert)
            
            # Check for overdue audits
            if compliance.next_audit_date and compliance.next_audit_date < datetime.now():
                alert_code = f"AUDIT-{uuid.uuid4().hex[:8].upper()}"
                
                alert = FoodSafetyAlert(
                    tenant_id=compliance.tenant_id,
                    alert_code=alert_code,
                    alert_type=FoodSafetyAlertType.CERTIFICATION_EXPIRY,
                    severity="high",
                    risk_level="medium",
                    source_entity_type="compliance",
                    source_entity_id=compliance.id,
                    ingredient_id=compliance.ingredient_id,
                    title=f"Audit overdue - {compliance.standard.value}",
                    description="Scheduled audit is overdue",
                    regulatory_action_required=True,
                    compliance_standard=compliance.standard,
                    first_occurred_at=datetime.now(),
                    last_occurred_at=datetime.now()
                )
                alerts_to_create.append(alert)
            
            # Add alerts to database
            for alert in alerts_to_create:
                db.add(alert)
            
            if alerts_to_create:
                await db.flush()
                
                # Send notifications
                for alert in alerts_to_create:
                    await self._send_alert_notifications(alert)
            
        except Exception as e:
            logger.error("Failed to check compliance alerts", error=str(e))
    
    async def _send_alert_notifications(self, alert: FoodSafetyAlert):
        """Send notifications for food safety alerts"""
        try:
            if not settings.ENABLE_EMAIL_ALERTS:
                return
            
            # Determine notification methods based on severity
            notification_methods = ["dashboard"]
            
            if alert.severity in ["high", "critical"]:
                notification_methods.extend(["email"])
                
                if settings.ENABLE_SMS_ALERTS and alert.severity == "critical":
                    notification_methods.append("sms")
                
                if settings.ENABLE_WHATSAPP_ALERTS and alert.public_health_risk:
                    notification_methods.append("whatsapp")
            
            # Send notification via notification service
            if self.notification_client:
                await self.notification_client.send_alert(
                    str(alert.tenant_id),
                    {
                        "alert_id": str(alert.id),
                        "alert_type": alert.alert_type.value,
                        "severity": alert.severity,
                        "title": alert.title,
                        "description": alert.description,
                        "methods": notification_methods,
                        "regulatory_action_required": alert.regulatory_action_required,
                        "public_health_risk": alert.public_health_risk
                    }
                )
            
            # Update alert with notification status
            alert.notification_sent = True
            alert.notification_methods = notification_methods
            
        except Exception as e:
            logger.warning("Failed to send alert notifications", 
                          alert_id=str(alert.id), 
                          error=str(e))
