"""Core notification service for RateRight Australian Construction Marketplace"""
import logging
from datetime import datetime, time, timedelta, timezone
from typing import List, Optional, Dict, Any
from flask import current_app

from ..extensions import db
from ..models.notification import (
    Notification, NotificationPreference, NotificationTemplate,
    NotificationType, DeliveryChannel, NotificationStatus, NotificationFrequency
)
from ..models.user import User
from .email_service import email_service
from .sms_service import sms_service

logger = logging.getLogger(__name__)


class NotificationService:
    """Central notification service with multi-channel delivery and Australian compliance"""
    
    def __init__(self):
        self.email_service = email_service
        self.sms_service = sms_service  # Twilio SMS service
        self.push_service = None  # Will be initialized when push service is created
        
    def create_notification(
        self,
        user_id: int,
        notification_type: NotificationType = None,
        type: NotificationType = None,  # Accept both names for compatibility
        title: str = "",
        content: str = "",
        message: str = None,  # Accept message as alias for content
        priority: int = 5,
        category: Optional[str] = None,
        action_url: Optional[str] = None,
        template_context: Optional[Dict[str, Any]] = None,
        force_send: bool = False
    ) -> Optional[Notification]:
        """
        Create a notification (alias for send_notification for test compatibility)
        """
        # Use 'type' if provided, otherwise use 'notification_type'
        final_type = type or notification_type
        if not final_type:
            raise ValueError("notification_type or type must be provided")
        
        # Handle message parameter mapping to content
        final_content = content or message or ""
        if not final_content:
            raise ValueError("content or message must be provided")
            
        return self.send_notification(
            user_id=user_id,
            notification_type=final_type,
            title=title,
            content=final_content,
            priority=priority,
            category=category,
            action_url=action_url,
            template_context=template_context,
            force_send=force_send
        )
    
    def _get_category_from_type(self, notification_type: NotificationType) -> str:
        """Map notification type to category for filtering"""
        type_to_category = {
            NotificationType.JOB_MATCH: 'job',
            NotificationType.JOB_APPLICATION_UPDATE: 'job_alerts',
            NotificationType.CONTRACT_AWARDED: 'contract',
            NotificationType.CONTRACT_COMPLETED: 'contract',
            NotificationType.CONTRACT_RATING_REQUIRED: 'contract',
            NotificationType.CONTRACT_RATING_RECEIVED: 'contract',
            NotificationType.PAYMENT_RECEIVED: 'payment',
            NotificationType.PAYMENT_DUE: 'payment',
            NotificationType.INVOICE_GENERATED: 'payment',
            NotificationType.HOURS_PENDING_APPROVAL: 'contract',
            NotificationType.HOURS_APPROVED: 'contract',
            NotificationType.TIMESHEET_SUBMITTED: 'contract',
        }
        return type_to_category.get(notification_type, 'general')
    
    def send_notification(
        self,
        user_id: int,
        notification_type: NotificationType,
        title: str,
        content: str,
        priority: int = 5,
        category: Optional[str] = None,
        action_url: Optional[str] = None,
        template_context: Optional[Dict[str, Any]] = None,
        force_send: bool = False
    ) -> Optional[Notification]:
        """
        Send notification to user via preferred channels
        
        Args:
            user_id: Target user ID
            notification_type: Type of notification
            title: Notification title
            content: Notification content
            priority: Priority (1=urgent, 5=normal, 10=low)
            category: Notification category for grouping
            action_url: URL for notification action
            template_context: Context for template rendering
            force_send: Skip preference checks (for urgent notifications)
        
        Returns:
            Created notification instance or None if failed
        """
        try:
            # Get user
            user = User.query.get(user_id)
            if not user:
                logger.error(f"User not found: {user_id}")
                return None
            
            # Create notification record
            # Auto-detect category from notification type if not provided
            if category is None:
                category = self._get_category_from_type(notification_type)
            
            notification = Notification(
                user_id=user_id,
                notification_type=notification_type,
                title=title,
                content=content,
                priority=priority,
                category=category,
                action_url=action_url,
                requires_consent=self._requires_consent(notification_type),
                business_hours_only=self._is_business_hours_only(notification_type)
            )
            
            db.session.add(notification)
            db.session.commit()
            
            # Get user preferences
            preferences = self._get_user_preferences(user_id, notification_type)
            
            # Check if should send now (respect quiet hours unless urgent)
            if not force_send and not self._should_send_now(user, preferences, notification):
                logger.info(f"Notification {notification.id} scheduled for later (quiet hours/business hours)")
                return notification
            
            # Try to send via all enabled channels
            channels_to_try = self._get_enabled_channels(preferences, force_send)
            
            # Exclude email channel for certain notification types (like job matches)
            if self._should_exclude_email_for_type(notification_type):
                channels_to_try = [ch for ch in channels_to_try if ch != DeliveryChannel.EMAIL]
                logger.info(f"Email excluded for notification type: {notification_type.value}")
            
            notification.channels_attempted = [channel.value for channel in channels_to_try]
            
            success_count = 0
            for channel in channels_to_try:
                try:
                    success = self._send_via_channel(notification, user, channel, template_context)
                    if success:
                        success_count += 1
                        notification.mark_as_delivered(channel)
                    else:
                        notification.mark_as_failed(channel, f"Failed to send via {channel.value}")
                        
                except Exception as e:
                    logger.error(f"Error sending via {channel.value}: {e}")
                    notification.mark_as_failed(channel, str(e))
            
            # Update notification status
            if success_count > 0:
                notification.status = NotificationStatus.DELIVERED
                notification.sent_at = datetime.now(timezone.utc)
            else:
                notification.status = NotificationStatus.FAILED
                
            db.session.commit()
            
            logger.info(f"Notification {notification.id} sent via {success_count}/{len(channels_to_try)} channels")
            return notification
            
        except Exception as e:
            logger.error(f"Failed to send notification: {e}")
            db.session.rollback()
            return None
    
    def send_bulk_notifications(
        self,
        user_ids: List[int],
        notification_type: NotificationType,
        title: str,
        content: str,
        **kwargs
    ) -> List[Notification]:
        """Send notification to multiple users"""
        notifications = []
        
        for user_id in user_ids:
            notification = self.send_notification(
                user_id=user_id,
                notification_type=notification_type,
                title=title,
                content=content,
                **kwargs
            )
            if notification:
                notifications.append(notification)
        
        logger.info(f"Sent bulk notification to {len(notifications)}/{len(user_ids)} users")
        return notifications
    
    def get_user_notifications(
        self,
        user_id: int,
        limit: int = 20,
        offset: int = 0,
        unread_only: bool = False
    ) -> List[Notification]:
        """Get user's notifications with pagination"""
        query = Notification.query.filter_by(user_id=user_id)
        
        if unread_only:
            query = query.filter(Notification.read_at.is_(None))
        
        return query.order_by(Notification.created_at.desc()).offset(offset).limit(limit).all()
    
    def get_unread_count(self, user_id: int) -> int:
        """Get count of unread notifications for user"""
        return Notification.query.filter_by(user_id=user_id).filter(
            Notification.read_at.is_(None)
        ).count()
    
    def mark_as_read(self, notification_id: int, user_id: int) -> bool:
        """Mark notification as read"""
        notification = Notification.query.filter_by(
            id=notification_id, 
            user_id=user_id
        ).first()
        
        if notification and not notification.is_read:
            notification.mark_as_read()
            return True
        return False
    
    def mark_all_as_read(self, user_id: int) -> int:
        """Mark all notifications as read for user"""
        count = Notification.query.filter_by(user_id=user_id).filter(
            Notification.read_at.is_(None)
        ).update({
            'read_at': datetime.now(timezone.utc),
            'status': NotificationStatus.READ
        })
        db.session.commit()
        return count
    
    def update_user_preferences(
        self,
        user_id: int,
        notification_type: NotificationType,
        preferences: Dict[str, Any]
    ) -> bool:
        """Update user notification preferences"""
        try:
            pref = NotificationPreference.query.filter_by(
                user_id=user_id,
                notification_type=notification_type
            ).first()
            
            if not pref:
                pref = NotificationPreference(
                    user_id=user_id,
                    notification_type=notification_type
                )
                db.session.add(pref)
            
            # Update preferences
            for key, value in preferences.items():
                if hasattr(pref, key):
                    setattr(pref, key, value)
            
            # Update consent if provided
            if 'consent_given' in preferences and preferences['consent_given']:
                pref.consent_given = True
                pref.consent_date = datetime.now(timezone.utc)
            
            db.session.commit()
            logger.info(f"Updated preferences for user {user_id}, type {notification_type.value}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to update preferences: {e}")
            db.session.rollback()
            return False
    
    def create_default_preferences(self, user_id: int):
        """Create default notification preferences for new user"""
        try:
            # Create preferences for all notification types
            for notification_type in NotificationType:
                existing = NotificationPreference.query.filter_by(
                    user_id=user_id,
                    notification_type=notification_type
                ).first()
                
                if not existing:
                    pref = NotificationPreference(
                        user_id=user_id,
                        notification_type=notification_type,
                        consent_given=True,  # Default consent for new users
                        consent_date=datetime.now(timezone.utc)
                    )
                    
                    # Set specific defaults based on notification type
                    if notification_type in [NotificationType.SECURITY_ALERT, NotificationType.SYSTEM_ANNOUNCEMENT]:
                        # Important notifications - enable all channels
                        pref.email_enabled = True
                        pref.sms_enabled = True
                    elif notification_type in [NotificationType.CHAT_NOTIFICATION, NotificationType.MESSAGE_RECEIVED]:
                        # Communication - enable push and in-app, email digest
                        pref.email_frequency = NotificationFrequency.DAILY_DIGEST
                    elif notification_type in [NotificationType.ACHIEVEMENT_UNLOCKED]:
                        # Gamification - in-app and push only
                        pref.email_enabled = False
                    
                    db.session.add(pref)
            
            db.session.commit()
            logger.info(f"Created default preferences for user {user_id}")
            
        except Exception as e:
            logger.error(f"Failed to create default preferences: {e}")
            db.session.rollback()
    
    def _get_user_preferences(self, user_id: int, notification_type: NotificationType) -> NotificationPreference:
        """Get user preferences for notification type"""
        pref = NotificationPreference.query.filter_by(
            user_id=user_id,
            notification_type=notification_type
        ).first()
        
        if not pref:
            # Create default if none exists
            pref = NotificationPreference(
                user_id=user_id,
                notification_type=notification_type,
                consent_given=True,
                consent_date=datetime.now(timezone.utc)
            )
            db.session.add(pref)
            db.session.commit()
        
        return pref
    
    def _should_send_now(self, user: User, preferences: NotificationPreference, notification: Notification) -> bool:
        """Check if notification should be sent now based on preferences and business rules"""
        # Always send urgent notifications
        if notification.is_urgent:
            return True
        
        # Check consent for non-urgent notifications
        if notification.requires_consent and not preferences.consent_given:
            return False
        
        # Check quiet hours
        if preferences.is_in_quiet_hours():
            return False
        
        # Check business hours for business-only notifications
        if notification.business_hours_only:
            current_time = datetime.now()
            # Australian business hours: 9 AM - 5 PM weekdays
            if (current_time.weekday() >= 5 or  # Weekend
                current_time.time() < time(9, 0) or  # Before 9 AM
                current_time.time() > time(17, 0)):  # After 5 PM
                return False
        
        # Check weekend preferences
        if preferences.respect_weekends and datetime.now().weekday() >= 5:
            return False
        
        return True
    
    def _get_enabled_channels(self, preferences: NotificationPreference, force_send: bool = False) -> List[DeliveryChannel]:
        """Get list of enabled delivery channels for user"""
        channels = []
        
        # In-app notifications are always enabled
        channels.append(DeliveryChannel.IN_APP)
        
        # Check other channels based on preferences
        if force_send or preferences.should_send_now(DeliveryChannel.EMAIL):
            channels.append(DeliveryChannel.EMAIL)
        
        if force_send or preferences.should_send_now(DeliveryChannel.SMS):
            channels.append(DeliveryChannel.SMS)
        
        if force_send or preferences.should_send_now(DeliveryChannel.PUSH_WEB):
            channels.append(DeliveryChannel.PUSH_WEB)
        
        if force_send or preferences.should_send_now(DeliveryChannel.PUSH_MOBILE):
            channels.append(DeliveryChannel.PUSH_MOBILE)
        
        return channels
    
    def _should_exclude_email_for_type(self, notification_type: NotificationType) -> bool:
        """Check if this notification type should not send emails"""
        # Job match notifications (new jobs posted) should not send emails
        # to avoid overwhelming users with too many emails
        excluded_types = [
            NotificationType.JOB_MATCH,
        ]
        return notification_type in excluded_types
    
    def _send_via_channel(
        self,
        notification: Notification,
        user: User,
        channel: DeliveryChannel,
        template_context: Optional[Dict[str, Any]] = None
    ) -> bool:
        """Send notification via specific channel"""
        try:
            if channel == DeliveryChannel.IN_APP:
                # In-app notifications are stored in database, no external delivery needed
                return True
                
            elif channel == DeliveryChannel.EMAIL:
                return self._send_email(notification, user, template_context)
                
            elif channel == DeliveryChannel.SMS:
                return self._send_sms(notification, user, template_context)
                
            elif channel in [DeliveryChannel.PUSH_WEB, DeliveryChannel.PUSH_MOBILE]:
                return self._send_push(notification, user, channel, template_context)
                
            else:
                logger.warning(f"Unknown delivery channel: {channel}")
                return False
                
        except Exception as e:
            logger.error(f"Error sending via {channel.value}: {e}")
            return False
    
    def _send_email(self, notification: Notification, user: User, template_context: Optional[Dict[str, Any]] = None) -> bool:
        """Send email notification"""
        try:
            # Convert relative URLs to absolute URLs for email
            action_url = notification.action_url
            if action_url and action_url.startswith('/'):
                # Get base URL from config (auto-detects dev vs production)
                base_url = current_app.config.get('BASE_URL')
                action_url = f"{base_url}{action_url}"
            
            # Generate unsubscribe token for this user
            unsubscribe_token = user.generate_unsubscribe_token()
            base_url = current_app.config.get('BASE_URL')
            unsubscribe_url = f"{base_url}/unsubscribe/{user.id}/{unsubscribe_token}"
            
            # Get email template
            template = self._get_template(notification.notification_type, DeliveryChannel.EMAIL)
            
            if template:
                # Prepare context
                context = {
                    'user_name': f"{user.first_name} {user.last_name}",
                    'title': notification.title,
                    'content': notification.content,
                    'action_url': action_url,
                    'unsubscribe_url': unsubscribe_url,
                    **(template_context or {})
                }
                
                rendered = template.render(context)
                result = self.email_service.send_email(
                    to_email=user.email,
                    subject=rendered['subject'] or notification.title,
                    body=rendered['body'],
                    html_body=rendered['html']
                )
                logger.info(f"Email sent using template for notification {notification.id}: {result}")
                return result
            else:
                # Use standardized email template with RateRight branding
                user_name = f"{user.first_name} {user.last_name}"
                
                html_body = self.email_service.generate_standard_email_html(
                    title=notification.title,
                    content=f"<p>{notification.content}</p>",
                    action_url=action_url,
                    action_text="View Details",
                    user_name=user_name,
                    unsubscribe_url=unsubscribe_url
                )
                
                result = self.email_service.send_email(
                    to_email=user.email,
                    subject=notification.title,
                    body=notification.content,
                    html_body=html_body
                )
                logger.info(f"Email sent with standard template for notification {notification.id}: {result}")
                return result
                
        except Exception as e:
            logger.error(f"Failed to send email for notification {notification.id}: {e}")
            import traceback
            logger.error(traceback.format_exc())
            return False
    
    def _send_sms(self, notification: Notification, user: User, template_context: Optional[Dict[str, Any]] = None) -> bool:
        """Send SMS notification via Twilio"""
        # Check if user has a phone number
        if not hasattr(user, 'phone_number') or not user.phone_number:
            logger.info(f"No phone number for user {user.id}, skipping SMS notification")
            return False

        try:
            # Prepare SMS message (truncate to 160 chars for single SMS)
            message = f"{notification.title}: {notification.content}"
            if len(message) > 160:
                message = message[:157] + "..."

            return self.sms_service.send_sms(user.phone_number, message)

        except Exception as e:
            logger.error(f"Failed to send SMS to user {user.id}: {e}")
            return False
    
    def _send_push(self, notification: Notification, user: User, channel: DeliveryChannel, template_context: Optional[Dict[str, Any]] = None) -> bool:
        """Send push notification (placeholder - will implement when push service is created)"""
        # For now, just log that push would be sent
        logger.info(f"Push notification ({channel.value}) would be sent to user {user.id}: {notification.title}")
        return True  # Return True for testing/stub mode
    
    def _get_template(self, notification_type: NotificationType, channel: DeliveryChannel) -> Optional[NotificationTemplate]:
        """Get notification template for type and channel"""
        return NotificationTemplate.query.filter_by(
            notification_type=notification_type,
            channel=channel,
            is_active=True
        ).first()
    
    def _requires_consent(self, notification_type: NotificationType) -> bool:
        """Check if notification type requires explicit consent"""
        # Marketing and non-essential notifications require consent
        marketing_types = [
            NotificationType.ACHIEVEMENT_UNLOCKED,
            NotificationType.SYSTEM_ANNOUNCEMENT
        ]
        return notification_type in marketing_types
    
    def _is_business_hours_only(self, notification_type: NotificationType) -> bool:
        """Check if notification should only be sent during business hours"""
        business_only = [
            NotificationType.TAX_OBLIGATION,
            NotificationType.SYSTEM_ANNOUNCEMENT
        ]
        return notification_type in business_only


# Singleton instance
notification_service = NotificationService()
