"""
Preference Repository
Repository for notification preference operations
"""

from typing import Optional, List, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, text, and_
from datetime import datetime
import structlog

from .base import NotificationBaseRepository
from app.models.notifications import NotificationPreference
from shared.database.exceptions import DatabaseError, ValidationError, DuplicateRecordError

logger = structlog.get_logger()


class PreferenceRepository(NotificationBaseRepository):
    """Repository for notification preference operations"""
    
    def __init__(self, session: AsyncSession, cache_ttl: Optional[int] = 900):
        # Preferences are relatively stable, medium cache time (15 minutes)
        super().__init__(NotificationPreference, session, cache_ttl)
    
    async def create_preferences(self, preference_data: Dict[str, Any]) -> NotificationPreference:
        """Create user notification preferences with validation"""
        try:
            # Validate preference data
            validation_result = self._validate_notification_data(
                preference_data, 
                ["user_id", "tenant_id"]
            )
            
            if not validation_result["is_valid"]:
                raise ValidationError(f"Invalid preference data: {validation_result['errors']}")
            
            # Check if preferences already exist for this user and tenant
            existing_prefs = await self.get_user_preferences(
                preference_data["user_id"], 
                preference_data["tenant_id"]
            )
            
            if existing_prefs:
                raise DuplicateRecordError(f"Preferences already exist for user in this tenant")
            
            # Set default values
            defaults = {
                "email_enabled": True,
                "email_alerts": True,
                "email_marketing": False,
                "email_reports": True,
                "whatsapp_enabled": False,
                "whatsapp_alerts": False,
                "whatsapp_reports": False,
                "push_enabled": True,
                "push_alerts": True,
                "push_reports": False,
                "quiet_hours_start": "22:00",
                "quiet_hours_end": "08:00",
                "timezone": "Europe/Madrid",
                "digest_frequency": "daily",
                "max_emails_per_day": 10,
                "language": "es"
            }
            
            # Apply defaults for any missing fields
            for key, default_value in defaults.items():
                if key not in preference_data:
                    preference_data[key] = default_value
            
            # Create preferences
            preferences = await self.create(preference_data)
            
            logger.info("User notification preferences created",
                       preferences_id=preferences.id,
                       user_id=preferences.user_id,
                       tenant_id=preferences.tenant_id)
            
            return preferences
            
        except (ValidationError, DuplicateRecordError):
            raise
        except Exception as e:
            logger.error("Failed to create preferences",
                        user_id=preference_data.get("user_id"),
                        tenant_id=preference_data.get("tenant_id"),
                        error=str(e))
            raise DatabaseError(f"Failed to create preferences: {str(e)}")
    
    async def get_user_preferences(
        self, 
        user_id: str, 
        tenant_id: str
    ) -> Optional[NotificationPreference]:
        """Get notification preferences for a specific user and tenant"""
        try:
            preferences = await self.get_multi(
                filters={
                    "user_id": user_id,
                    "tenant_id": tenant_id
                },
                limit=1
            )
            return preferences[0] if preferences else None
            
        except Exception as e:
            logger.error("Failed to get user preferences",
                        user_id=user_id,
                        tenant_id=tenant_id,
                        error=str(e))
            raise DatabaseError(f"Failed to get preferences: {str(e)}")
    
    async def update_user_preferences(
        self,
        user_id: str,
        tenant_id: str,
        update_data: Dict[str, Any]
    ) -> Optional[NotificationPreference]:
        """Update user notification preferences"""
        try:
            preferences = await self.get_user_preferences(user_id, tenant_id)
            if not preferences:
                # Create preferences if they don't exist
                create_data = {
                    "user_id": user_id,
                    "tenant_id": tenant_id,
                    **update_data
                }
                return await self.create_preferences(create_data)
            
            # Validate specific preference fields
            self._validate_preference_updates(update_data)
            
            updated_preferences = await self.update(str(preferences.id), update_data)
            
            logger.info("User preferences updated",
                       preferences_id=preferences.id,
                       user_id=user_id,
                       tenant_id=tenant_id,
                       updated_fields=list(update_data.keys()))
            
            return updated_preferences
            
        except ValidationError:
            raise
        except Exception as e:
            logger.error("Failed to update user preferences",
                        user_id=user_id,
                        tenant_id=tenant_id,
                        error=str(e))
            raise DatabaseError(f"Failed to update preferences: {str(e)}")
    
    async def get_users_with_email_enabled(
        self, 
        tenant_id: str,
        notification_category: str = "alerts"
    ) -> List[NotificationPreference]:
        """Get users who have email notifications enabled for a category"""
        try:
            filters = {
                "tenant_id": tenant_id,
                "email_enabled": True
            }
            
            # Add category-specific filter
            if notification_category == "alerts":
                filters["email_alerts"] = True
            elif notification_category == "marketing":
                filters["email_marketing"] = True
            elif notification_category == "reports":
                filters["email_reports"] = True
            
            return await self.get_multi(filters=filters)
            
        except Exception as e:
            logger.error("Failed to get users with email enabled",
                        tenant_id=tenant_id,
                        category=notification_category,
                        error=str(e))
            return []
    
    async def get_users_with_whatsapp_enabled(
        self, 
        tenant_id: str,
        notification_category: str = "alerts"
    ) -> List[NotificationPreference]:
        """Get users who have WhatsApp notifications enabled for a category"""
        try:
            filters = {
                "tenant_id": tenant_id,
                "whatsapp_enabled": True
            }
            
            # Add category-specific filter
            if notification_category == "alerts":
                filters["whatsapp_alerts"] = True
            elif notification_category == "reports":
                filters["whatsapp_reports"] = True
            
            return await self.get_multi(filters=filters)
            
        except Exception as e:
            logger.error("Failed to get users with WhatsApp enabled",
                        tenant_id=tenant_id,
                        category=notification_category,
                        error=str(e))
            return []
    
    async def get_users_with_push_enabled(
        self, 
        tenant_id: str,
        notification_category: str = "alerts"
    ) -> List[NotificationPreference]:
        """Get users who have push notifications enabled for a category"""
        try:
            filters = {
                "tenant_id": tenant_id,
                "push_enabled": True
            }
            
            # Add category-specific filter
            if notification_category == "alerts":
                filters["push_alerts"] = True
            elif notification_category == "reports":
                filters["push_reports"] = True
            
            return await self.get_multi(filters=filters)
            
        except Exception as e:
            logger.error("Failed to get users with push enabled",
                        tenant_id=tenant_id,
                        category=notification_category,
                        error=str(e))
            return []
    
    async def check_quiet_hours(
        self, 
        user_id: str, 
        tenant_id: str,
        check_time: datetime = None
    ) -> bool:
        """Check if current time is within user's quiet hours"""
        try:
            preferences = await self.get_user_preferences(user_id, tenant_id)
            if not preferences:
                return False  # No quiet hours if no preferences
            
            if not check_time:
                check_time = datetime.utcnow()
            
            # Convert time to user's timezone (simplified - using hour comparison)
            current_hour = check_time.hour
            quiet_start = int(preferences.quiet_hours_start.split(":")[0])
            quiet_end = int(preferences.quiet_hours_end.split(":")[0])
            
            # Handle quiet hours that span midnight
            if quiet_start > quiet_end:
                return current_hour >= quiet_start or current_hour < quiet_end
            else:
                return quiet_start <= current_hour < quiet_end
                
        except Exception as e:
            logger.error("Failed to check quiet hours",
                        user_id=user_id,
                        tenant_id=tenant_id,
                        error=str(e))
            return False
    
    async def get_users_for_digest(
        self, 
        tenant_id: str,
        frequency: str = "daily"
    ) -> List[NotificationPreference]:
        """Get users who want digest notifications for a frequency"""
        try:
            return await self.get_multi(
                filters={
                    "tenant_id": tenant_id,
                    "digest_frequency": frequency,
                    "email_enabled": True
                }
            )
            
        except Exception as e:
            logger.error("Failed to get users for digest",
                        tenant_id=tenant_id,
                        frequency=frequency,
                        error=str(e))
            return []
    
    async def can_send_email(
        self, 
        user_id: str, 
        tenant_id: str,
        category: str = "alerts"
    ) -> Dict[str, Any]:
        """Check if an email can be sent to a user based on their preferences"""
        try:
            preferences = await self.get_user_preferences(user_id, tenant_id)
            if not preferences:
                return {
                    "can_send": True,  # Default to allowing if no preferences set
                    "reason": "No preferences found, using defaults"
                }
            
            # Check if email is enabled
            if not preferences.email_enabled:
                return {
                    "can_send": False,
                    "reason": "Email notifications disabled"
                }
            
            # Check category-specific settings
            category_enabled = True
            if category == "alerts" and not preferences.email_alerts:
                category_enabled = False
            elif category == "marketing" and not preferences.email_marketing:
                category_enabled = False
            elif category == "reports" and not preferences.email_reports:
                category_enabled = False
            
            if not category_enabled:
                return {
                    "can_send": False,
                    "reason": f"Email {category} notifications disabled"
                }
            
            # Check quiet hours
            if self.check_quiet_hours(user_id, tenant_id):
                return {
                    "can_send": False,
                    "reason": "Within user's quiet hours"
                }
            
            # Check daily limit (simplified - would need to query recent notifications)
            # For now, just return the limit info
            return {
                "can_send": True,
                "max_daily_emails": preferences.max_emails_per_day,
                "language": preferences.language,
                "timezone": preferences.timezone
            }
            
        except Exception as e:
            logger.error("Failed to check if email can be sent",
                        user_id=user_id,
                        tenant_id=tenant_id,
                        error=str(e))
            return {
                "can_send": True,  # Default to allowing on error
                "reason": "Error checking preferences"
            }
    
    async def bulk_update_preferences(
        self,
        tenant_id: str,
        update_data: Dict[str, Any],
        user_ids: List[str] = None
    ) -> int:
        """Bulk update preferences for multiple users"""
        try:
            conditions = ["tenant_id = :tenant_id"]
            params = {"tenant_id": tenant_id}
            
            if user_ids:
                placeholders = ", ".join([f":user_id_{i}" for i in range(len(user_ids))])
                conditions.append(f"user_id IN ({placeholders})")
                for i, user_id in enumerate(user_ids):
                    params[f"user_id_{i}"] = user_id
            
            # Build update clause
            update_fields = []
            for key, value in update_data.items():
                update_fields.append(f"{key} = :update_{key}")
                params[f"update_{key}"] = value
            
            params["updated_at"] = datetime.utcnow()
            update_fields.append("updated_at = :updated_at")
            
            query_text = f"""
                UPDATE notification_preferences
                SET {', '.join(update_fields)}
                WHERE {' AND '.join(conditions)}
            """
            
            result = await self.session.execute(text(query_text), params)
            updated_count = result.rowcount
            
            logger.info("Bulk preferences update completed",
                       tenant_id=tenant_id,
                       updated_count=updated_count,
                       updated_fields=list(update_data.keys()))
            
            return updated_count
            
        except Exception as e:
            logger.error("Failed to bulk update preferences",
                        tenant_id=tenant_id,
                        error=str(e))
            raise DatabaseError(f"Bulk update failed: {str(e)}")
    
    async def delete_user_preferences(
        self, 
        user_id: str, 
        tenant_id: str
    ) -> bool:
        """Delete user preferences (when user leaves tenant)"""
        try:
            preferences = await self.get_user_preferences(user_id, tenant_id)
            if not preferences:
                return False
            
            await self.delete(str(preferences.id))
            
            logger.info("User preferences deleted",
                       user_id=user_id,
                       tenant_id=tenant_id)
            
            return True
            
        except Exception as e:
            logger.error("Failed to delete user preferences",
                        user_id=user_id,
                        tenant_id=tenant_id,
                        error=str(e))
            raise DatabaseError(f"Failed to delete preferences: {str(e)}")
    
    def _validate_preference_updates(self, update_data: Dict[str, Any]) -> None:
        """Validate preference update data"""
        # Validate boolean fields
        boolean_fields = [
            "email_enabled", "email_alerts", "email_marketing", "email_reports",
            "whatsapp_enabled", "whatsapp_alerts", "whatsapp_reports",
            "push_enabled", "push_alerts", "push_reports"
        ]
        
        for field in boolean_fields:
            if field in update_data and not isinstance(update_data[field], bool):
                raise ValidationError(f"{field} must be a boolean value")
        
        # Validate time format for quiet hours
        time_fields = ["quiet_hours_start", "quiet_hours_end"]
        for field in time_fields:
            if field in update_data:
                time_value = update_data[field]
                if not isinstance(time_value, str) or len(time_value) != 5 or ":" not in time_value:
                    raise ValidationError(f"{field} must be in HH:MM format")
                
                try:
                    hour, minute = time_value.split(":")
                    hour, minute = int(hour), int(minute)
                    if hour < 0 or hour > 23 or minute < 0 or minute > 59:
                        raise ValueError()
                except ValueError:
                    raise ValidationError(f"{field} must be a valid time in HH:MM format")
        
        # Validate digest frequency
        if "digest_frequency" in update_data:
            valid_frequencies = ["none", "daily", "weekly"]
            if update_data["digest_frequency"] not in valid_frequencies:
                raise ValidationError(f"digest_frequency must be one of: {valid_frequencies}")
        
        # Validate max emails per day
        if "max_emails_per_day" in update_data:
            max_emails = update_data["max_emails_per_day"]
            if not isinstance(max_emails, int) or max_emails < 0 or max_emails > 100:
                raise ValidationError("max_emails_per_day must be an integer between 0 and 100")
        
        # Validate language
        if "language" in update_data:
            valid_languages = ["es", "en", "fr", "de"]
            if update_data["language"] not in valid_languages:
                raise ValidationError(f"language must be one of: {valid_languages}")