"""Notification system models for RateRight Australian Construction Marketplace"""
from datetime import datetime, timedelta, timezone
from sqlalchemy import Enum as SQLEnum
from enum import Enum

from ..extensions import db
from .base import BaseModel


class NotificationType(Enum):
    """Notification types for Australian construction marketplace"""
    JOB_MATCH = "job_match"
    JOB_INVITATION = "job_invitation"
    JOB_APPLICATION_UPDATE = "job_application_update"
    CONTRACT_AWARDED = "contract_awarded"
    PAYMENT_RECEIVED = "payment_received"
    PAYMENT_DUE = "payment_due"
    INVOICE_GENERATED = "invoice_generated"
    RATING_RECEIVED = "rating_received"
    REVIEW_REQUEST = "review_request"
    ACHIEVEMENT_UNLOCKED = "achievement_unlocked"
    MESSAGE_RECEIVED = "message_received"
    CHAT_NOTIFICATION = "chat_notification"
    DEADLINE_REMINDER = "deadline_reminder"
    INSURANCE_EXPIRY = "insurance_expiry"
    CERTIFICATION_RENEWAL = "certification_renewal"
    SYSTEM_ANNOUNCEMENT = "system_announcement"
    SECURITY_ALERT = "security_alert"
    WHS_COMPLIANCE = "whs_compliance"
    TAX_OBLIGATION = "tax_obligation"
    # Time tracking notification types
    HOURS_PENDING_APPROVAL = "hours_pending_approval"
    HOURS_APPROVED = "hours_approved"
    OVERTIME_ALERT = "overtime_alert"
    TIMESHEET_SUBMITTED = "timesheet_submitted"
    # Rating notification types
    CONTRACT_RATING_REQUIRED = "contract_rating_required"
    CONTRACT_RATING_RECEIVED = "contract_rating_received"
    CONTRACT_COMPLETED = "contract_completed"


class DeliveryChannel(Enum):
    """Notification delivery channels"""
    EMAIL = "email"
    SMS = "sms"
    PUSH_WEB = "push_web"
    PUSH_MOBILE = "push_mobile"
    IN_APP = "in_app"


class NotificationStatus(Enum):
    """Notification delivery status"""
    PENDING = "pending"
    SENT = "sent"
    DELIVERED = "delivered"
    FAILED = "failed"
    READ = "read"


class NotificationFrequency(Enum):
    """Notification frequency preferences"""
    IMMEDIATE = "immediate"
    DAILY_DIGEST = "daily_digest"
    WEEKLY_SUMMARY = "weekly_summary"
    DISABLED = "disabled"


class NotificationPreference(BaseModel):
    """User notification preferences with Australian compliance features"""
    
    __tablename__ = "notification_preferences"
    
    # User relationship
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    
    # Notification type
    notification_type = db.Column(SQLEnum(NotificationType), nullable=False)
    
    # Channel preferences
    email_enabled = db.Column(db.Boolean, default=True, nullable=False)
    email_frequency = db.Column(SQLEnum(NotificationFrequency), default=NotificationFrequency.IMMEDIATE)
    
    sms_enabled = db.Column(db.Boolean, default=False, nullable=False)
    sms_frequency = db.Column(SQLEnum(NotificationFrequency), default=NotificationFrequency.DISABLED)
    
    push_web_enabled = db.Column(db.Boolean, default=True, nullable=False)
    push_mobile_enabled = db.Column(db.Boolean, default=True, nullable=False)
    
    in_app_enabled = db.Column(db.Boolean, default=True, nullable=False)
    
    # Australian compliance features
    quiet_hours_start = db.Column(db.Time, default=datetime.strptime("21:00", "%H:%M").time())
    quiet_hours_end = db.Column(db.Time, default=datetime.strptime("07:00", "%H:%M").time())
    respect_weekends = db.Column(db.Boolean, default=True, nullable=False)
    
    # Privacy and consent
    consent_given = db.Column(db.Boolean, default=False, nullable=False)
    consent_date = db.Column(db.DateTime)
    
    # User relationship
    user = db.relationship('User', backref=db.backref('notification_preferences', lazy=True))
    
    # Unique constraint per user per notification type
    __table_args__ = (
        db.UniqueConstraint('user_id', 'notification_type', name='unique_user_notification_pref'),
    )
    
    def is_in_quiet_hours(self, check_time=None):
        """Check if current time is within user's quiet hours (Australian timezone aware)"""
        if check_time is None:
            check_time = datetime.now().time()
        
        # Handle overnight quiet hours (e.g., 21:00 - 07:00)
        if self.quiet_hours_start > self.quiet_hours_end:
            return check_time >= self.quiet_hours_start or check_time <= self.quiet_hours_end
        else:
            return self.quiet_hours_start <= check_time <= self.quiet_hours_end
    
    def should_send_now(self, channel: DeliveryChannel):
        """Determine if notification should be sent now based on preferences"""
        if channel == DeliveryChannel.EMAIL:
            return self.email_enabled and self.email_frequency == NotificationFrequency.IMMEDIATE
        elif channel == DeliveryChannel.SMS:
            return self.sms_enabled and self.sms_frequency == NotificationFrequency.IMMEDIATE
        elif channel == DeliveryChannel.PUSH_WEB:
            return self.push_web_enabled
        elif channel == DeliveryChannel.PUSH_MOBILE:
            return self.push_mobile_enabled
        elif channel == DeliveryChannel.IN_APP:
            return self.in_app_enabled
        return False


class NotificationTemplate(BaseModel):
    """Reusable notification templates for different channels"""
    
    __tablename__ = "notification_templates"
    
    # Template identification
    notification_type = db.Column(SQLEnum(NotificationType), nullable=False)
    channel = db.Column(SQLEnum(DeliveryChannel), nullable=False)
    
    # Template content
    subject_template = db.Column(db.Text)  # For email/push notifications
    body_template = db.Column(db.Text, nullable=False)
    html_template = db.Column(db.Text)  # For email HTML content
    
    # Template metadata
    template_name = db.Column(db.String(100), nullable=False)
    description = db.Column(db.String(255))
    
    # Australian compliance
    includes_unsubscribe = db.Column(db.Boolean, default=False)
    privacy_compliant = db.Column(db.Boolean, default=True, nullable=False)
    
    # Active status
    is_active = db.Column(db.Boolean, default=True, nullable=False)
    
    # Unique constraint per type per channel
    __table_args__ = (
        db.UniqueConstraint('notification_type', 'channel', name='unique_template_type_channel'),
    )
    
    def render(self, context: dict):
        """Render template with provided context"""
        try:
            subject = self.subject_template.format(**context) if self.subject_template else ""
            body = self.body_template.format(**context)
            html = self.html_template.format(**context) if self.html_template else None
            
            return {
                'subject': subject,
                'body': body,
                'html': html
            }
        except KeyError as e:
            raise ValueError(f"Missing template variable: {e}")


class Notification(BaseModel):
    """Central notification log with delivery tracking"""
    
    __tablename__ = "notifications"
    
    # Recipient
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    
    # Notification details
    notification_type = db.Column(SQLEnum(NotificationType), nullable=False)
    title = db.Column(db.String(255), nullable=False)
    content = db.Column(db.Text, nullable=False)
    
    # Delivery tracking
    channels_attempted = db.Column(db.JSON)  # List of channels attempted
    channels_delivered = db.Column(db.JSON)  # List of channels successfully delivered
    channels_failed = db.Column(db.JSON)  # List of channels that failed
    
    # Status and timing
    status = db.Column(SQLEnum(NotificationStatus), default=NotificationStatus.PENDING, nullable=False)
    scheduled_for = db.Column(db.DateTime, default=datetime.utcnow)
    sent_at = db.Column(db.DateTime)
    delivered_at = db.Column(db.DateTime)
    read_at = db.Column(db.DateTime)
    
    # Metadata
    priority = db.Column(db.Integer, default=5)  # 1 = urgent, 5 = normal, 10 = low
    category = db.Column(db.String(50))  # For grouping notifications
    action_url = db.Column(db.String(255))  # URL for notification action
    
    # Australian compliance
    requires_consent = db.Column(db.Boolean, default=False)
    business_hours_only = db.Column(db.Boolean, default=False)
    
    # Error tracking
    error_count = db.Column(db.Integer, default=0)
    last_error = db.Column(db.Text)
    
    # User relationship
    user = db.relationship('User', backref=db.backref('notifications', lazy=True, order_by='Notification.created_at.desc()'))
    
    def mark_as_read(self):
        """Mark notification as read"""
        self.read_at = datetime.now(timezone.utc)
        self.status = NotificationStatus.READ
        db.session.commit()
    
    def mark_as_delivered(self, channel: DeliveryChannel):
        """Mark notification as delivered for specific channel"""
        if self.channels_delivered is None:
            self.channels_delivered = []
        
        if channel.value not in self.channels_delivered:
            # Create a new list to ensure SQLAlchemy detects the change
            self.channels_delivered = self.channels_delivered + [channel.value]
            
        self.delivered_at = datetime.now(timezone.utc)
        self.status = NotificationStatus.DELIVERED
        # Mark column as modified to ensure SQLAlchemy tracks the change
        from sqlalchemy.orm.attributes import flag_modified
        flag_modified(self, 'channels_delivered')
        # Note: Don't commit here - let the caller handle commits to avoid conflicts
    
    def mark_as_failed(self, channel: DeliveryChannel, error: str):
        """Mark notification as failed for specific channel"""
        if self.channels_failed is None:
            self.channels_failed = []
            
        if channel.value not in self.channels_failed:
            # Create a new list to ensure SQLAlchemy detects the change
            self.channels_failed = self.channels_failed + [channel.value]
            
        self.error_count += 1
        self.last_error = error
        self.status = NotificationStatus.FAILED
        # Mark column as modified to ensure SQLAlchemy tracks the change
        from sqlalchemy.orm.attributes import flag_modified
        flag_modified(self, 'channels_failed')
        # Note: Don't commit here - let the caller handle commits to avoid conflicts
    
    @property
    def is_read(self):
        """Check if notification has been read"""
        return self.read_at is not None
    
    @property
    def is_urgent(self):
        """Check if notification is urgent (priority 1-2)"""
        return self.priority <= 2
    
    def to_dict(self):
        """Convert to dictionary for JSON serialization"""
        return {
            'id': self.id,
            'type': self.notification_type.value,
            'title': self.title,
            'content': self.content,
            'priority': self.priority,
            'category': self.category,
            'action_url': self.action_url,
            'status': self.status.value,
            'is_read': self.is_read,
            'is_urgent': self.is_urgent,
            'created_at': self.created_at.isoformat() if self.created_at else None,
            'sent_at': self.sent_at.isoformat() if self.sent_at else None,
            'read_at': self.read_at.isoformat() if self.read_at else None,
            'channels_delivered': self.channels_delivered or [],
            'channels_failed': self.channels_failed or []
        }
