from django.db import models
from django.conf import settings
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

from .email_types import EmailType
from .email_tags import validate_tags, extract_tags_from_puck_data


class CommunicationSession(models.Model):
    """
    Masked communication session using Twilio Conversations.
    
    Enables staff and customers to communicate via SMS/chat without
    exposing personal phone numbers. Twilio routes messages through
    a proxy number.
    """
    
    # Link to event
    event = models.OneToOneField(
        'schedule.Event',
        on_delete=models.CASCADE,
        related_name='communication_session'
    )
    
    # Twilio identifiers
    twilio_conversation_sid = models.CharField(
        max_length=255,
        unique=True,
        db_index=True,
        help_text="Twilio Conversation SID"
    )
    
    # Session status
    is_active = models.BooleanField(
        default=True,
        db_index=True,
        help_text="Whether the conversation is active"
    )
    
    # Phone validation regex (E.164 format)
    phone_regex = RegexValidator(
        regex=r'^\+?1?\d{9,15}$',
        message="Phone number must be in E.164 format (+1234567890)"
    )
    
    # Secure phone storage
    # NOTE: In production, consider encrypting these fields
    staff_phone = models.CharField(
        max_length=20,
        validators=[phone_regex],
        help_text="Staff member's phone number (E.164 format)"
    )
    
    customer_phone = models.CharField(
        max_length=20,
        validators=[phone_regex],
        help_text="Customer's phone number (E.164 format)"
    )
    
    #  Localization
    language_code = models.CharField(
        max_length=10,
        default='en',
        help_text="Language preference for SMS templates (en/es/fr/de)"
    )
    
    # Participant SIDs (for cleanup)
    staff_participant_sid = models.CharField(
        max_length=255,
        blank=True,
        help_text="Twilio Participant SID for staff"
    )
    
    customer_participant_sid = models.CharField(
        max_length=255,
        blank=True,
        help_text="Twilio Participant SID for customer"
    )
    
    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    closed_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the conversation was closed"
    )
    
    class Meta:
        app_label = 'messaging'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['is_active', 'created_at']),
        ]
    
    def __str__(self):
        status = "Active" if self.is_active else "Closed"
        return f"Session for {self.event.title} ({status})"
    
    @property
    def message_count(self):
        """
        Get total message count (would require Twilio API call).
        Stub for future implementation.
        """
        # TODO: Implement via Twilio API
        return None


class BroadcastMessage(models.Model):
    """
    Broadcast message sent by owners/managers to groups of users.

    Allows targeting specific role groups (owners, managers, staff, customers)
    or individual users within those groups.
    """

    class TargetType(models.TextChoices):
        ALL_OWNERS = 'all_owners', 'All Owners'
        ALL_MANAGERS = 'all_managers', 'All Managers'
        ALL_STAFF = 'all_staff', 'All Staff'
        ALL_CUSTOMERS = 'all_customers', 'All Customers'
        SELECTED_USERS = 'selected_users', 'Selected Users'

    class DeliveryMethod(models.TextChoices):
        IN_APP = 'in_app', 'In-App Notification'
        EMAIL = 'email', 'Email'
        SMS = 'sms', 'SMS'
        ALL = 'all', 'All Channels'

    class Status(models.TextChoices):
        DRAFT = 'draft', 'Draft'
        SCHEDULED = 'scheduled', 'Scheduled'
        SENDING = 'sending', 'Sending'
        SENT = 'sent', 'Sent'
        FAILED = 'failed', 'Failed'

    # Sender
    sender = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name='sent_broadcast_messages',
        help_text="User who sent the message"
    )

    # Message content
    subject = models.CharField(
        max_length=255,
        help_text="Message subject (for email) or title (for in-app)"
    )
    body = models.TextField(
        help_text="Message body content"
    )

    # Targeting - can select multiple target types
    target_owners = models.BooleanField(
        default=False,
        help_text="Send to all owners"
    )
    target_managers = models.BooleanField(
        default=False,
        help_text="Send to all managers"
    )
    target_staff = models.BooleanField(
        default=False,
        help_text="Send to all staff"
    )
    target_customers = models.BooleanField(
        default=False,
        help_text="Send to all customers"
    )

    # Individual recipients (for selected_users mode)
    individual_recipients = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        blank=True,
        related_name='received_broadcast_messages',
        help_text="Specific users to receive the message"
    )

    # Delivery settings
    delivery_method = models.CharField(
        max_length=20,
        choices=DeliveryMethod.choices,
        default=DeliveryMethod.IN_APP,
        help_text="How to deliver the message"
    )

    # Status tracking
    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.DRAFT,
        db_index=True
    )

    # Scheduling
    scheduled_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When to send the message (null = send immediately)"
    )

    # Statistics
    total_recipients = models.PositiveIntegerField(
        default=0,
        help_text="Total number of recipients"
    )
    delivered_count = models.PositiveIntegerField(
        default=0,
        help_text="Number of successful deliveries"
    )
    failed_count = models.PositiveIntegerField(
        default=0,
        help_text="Number of failed deliveries"
    )
    read_count = models.PositiveIntegerField(
        default=0,
        help_text="Number of recipients who read the message"
    )

    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    sent_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the message was actually sent"
    )

    class Meta:
        app_label = 'messaging'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['status', 'scheduled_at']),
            models.Index(fields=['sender', 'created_at']),
        ]

    def __str__(self):
        return f"{self.subject} ({self.get_status_display()})"

    def get_target_description(self):
        """Get human-readable description of targets."""
        targets = []
        if self.target_owners:
            targets.append('Owners')
        if self.target_managers:
            targets.append('Managers')
        if self.target_staff:
            targets.append('Staff')
        if self.target_customers:
            targets.append('Customers')
        if self.individual_recipients.exists():
            count = self.individual_recipients.count()
            targets.append(f'{count} Individual(s)')
        return ', '.join(targets) if targets else 'No recipients'


class MessageRecipient(models.Model):
    """
    Tracks individual message delivery status for each recipient.
    """

    class DeliveryStatus(models.TextChoices):
        PENDING = 'pending', 'Pending'
        DELIVERED = 'delivered', 'Delivered'
        FAILED = 'failed', 'Failed'
        BOUNCED = 'bounced', 'Bounced'

    message = models.ForeignKey(
        BroadcastMessage,
        on_delete=models.CASCADE,
        related_name='recipients'
    )

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='message_deliveries'
    )

    # Delivery tracking
    delivery_status = models.CharField(
        max_length=20,
        choices=DeliveryStatus.choices,
        default=DeliveryStatus.PENDING
    )
    delivered_at = models.DateTimeField(null=True, blank=True)

    # Read tracking
    is_read = models.BooleanField(default=False)
    read_at = models.DateTimeField(null=True, blank=True)

    # Error tracking
    error_message = models.TextField(blank=True)

    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        app_label = 'messaging'
        unique_together = ['message', 'user']
        indexes = [
            models.Index(fields=['message', 'delivery_status']),
            models.Index(fields=['user', 'is_read']),
        ]

    def __str__(self):
        return f"{self.message.subject} -> {self.user.email}"


def default_puck_data():
    """Default Puck data structure for email templates."""
    return {
        'content': [],
        'root': {},
    }


class PuckEmailTemplate(models.Model):
    """
    Puck-based email template for tenant-customizable system emails.

    Each tenant has one template per email type. Templates use Puck JSON
    for visual editing and support variable tags like {{ customer_name }}.

    Key features:
    - Tenant-scoped: each business has their own templates
    - Type-specific: one template per EmailType per tenant
    - Tag validation: only allowed tags are permitted
    - Email-safe: Puck components render to email-safe HTML
    """

    # Tenant association - templates are tenant-scoped
    # Note: In multi-tenant setup, this is handled by django-tenants
    # For explicit tenant tracking (if needed), add FK here

    email_type = models.CharField(
        max_length=50,
        choices=EmailType.choices(),
        db_index=True,
        help_text="Type of email this template is for"
    )

    # Subject line with tag support
    subject_template = models.CharField(
        max_length=500,
        help_text="Email subject line. Supports tags like {{ customer_name }}"
    )

    # Puck visual editor data
    puck_data = models.JSONField(
        default=default_puck_data,
        help_text="Puck editor JSON data for email body"
    )

    # Status
    is_active = models.BooleanField(
        default=True,
        db_index=True,
        help_text="Whether this template is active"
    )

    is_customized = models.BooleanField(
        default=False,
        help_text="Whether tenant has customized from default"
    )

    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_email_templates_puck',
        help_text="User who created/last edited this template"
    )

    class Meta:
        app_label = 'messaging'
        ordering = ['email_type']
        # In multi-tenant setup with django-tenants, uniqueness is per-schema
        # If using explicit tenant FK, add: unique_together = [('tenant', 'email_type')]
        constraints = [
            models.UniqueConstraint(
                fields=['email_type'],
                name='unique_email_type_per_tenant'
            )
        ]
        indexes = [
            models.Index(fields=['email_type', 'is_active']),
        ]

    def __str__(self):
        type_display = EmailType.get_display_name(EmailType(self.email_type))
        status = "Active" if self.is_active else "Inactive"
        return f"{type_display} ({status})"

    def clean(self):
        """Validate template before saving."""
        super().clean()

        # Validate tags
        try:
            email_type_enum = EmailType(self.email_type)
        except ValueError:
            raise ValidationError({'email_type': f'Invalid email type: {self.email_type}'})

        errors = validate_tags(
            self.subject_template,
            self.puck_data,
            email_type_enum
        )

        if errors:
            raise ValidationError({'subject_template': errors})

    def save(self, *args, **kwargs):
        """Run validation before saving."""
        self.full_clean()
        super().save(*args, **kwargs)

    def get_email_type_display_name(self) -> str:
        """Get human-readable email type name."""
        try:
            return EmailType.get_display_name(EmailType(self.email_type))
        except ValueError:
            return self.email_type

    def get_email_type_description(self) -> str:
        """Get description of when this email is sent."""
        try:
            return EmailType.get_description(EmailType(self.email_type))
        except ValueError:
            return ''

    def render(self, context: dict) -> dict:
        """
        Render the template with given context.

        Args:
            context: Dictionary of template variables

        Returns:
            Dict with 'subject', 'html', and 'text' keys
        """
        from .email_renderer import render_email
        return render_email(self, context)

    @classmethod
    def get_or_create_for_type(cls, email_type: EmailType) -> 'PuckEmailTemplate':
        """
        Get template for email type, creating from default if needed.

        This ensures every email type has a template available.

        Args:
            email_type: The EmailType enum value

        Returns:
            PuckEmailTemplate instance
        """
        template, created = cls.objects.get_or_create(
            email_type=email_type.value,
            defaults=cls._get_default_for_type(email_type)
        )

        return template

    @classmethod
    def _get_default_for_type(cls, email_type: EmailType) -> dict:
        """Get default template data for an email type."""
        from .default_templates import DEFAULT_TEMPLATES
        from .utils import add_component_ids

        default = DEFAULT_TEMPLATES.get(email_type.value, {})
        puck_data = default.get('puck_data', default_puck_data())

        # Ensure all components have unique IDs (required by Puck)
        puck_data_with_ids = add_component_ids(puck_data)

        return {
            'subject_template': default.get('subject_template', f'{email_type.value} email'),
            'puck_data': puck_data_with_ids,
            'is_customized': False,
        }

    def reset_to_default(self):
        """Reset template to system default."""
        try:
            email_type_enum = EmailType(self.email_type)
        except ValueError:
            return

        defaults = self._get_default_for_type(email_type_enum)
        self.subject_template = defaults['subject_template']
        self.puck_data = defaults['puck_data']
        self.is_customized = False
        self.save()


class CustomEmailTemplate(models.Model):
    """
    Custom email template created by tenants for use with plugins.

    Unlike PuckEmailTemplate (system emails), these are:
    - User-created with custom names/slugs
    - Have access to ALL template tags
    - Can be referenced by plugins via slug
    - Not tied to a specific system email type
    """

    # Unique identifier for plugin reference
    slug = models.SlugField(
        max_length=100,
        db_index=True,
        help_text="Unique identifier for referencing in plugins (e.g., 'monthly-newsletter')"
    )

    # Display name
    name = models.CharField(
        max_length=255,
        help_text="Human-readable template name"
    )

    # Description for admin reference
    description = models.TextField(
        blank=True,
        help_text="Description of when/how this template is used"
    )

    # Subject line with tag support
    subject_template = models.CharField(
        max_length=500,
        help_text="Email subject line. Supports all template tags like {{ customer_name }}"
    )

    # Puck visual editor data
    puck_data = models.JSONField(
        default=default_puck_data,
        help_text="Puck editor JSON data for email body"
    )

    # Status
    is_active = models.BooleanField(
        default=True,
        db_index=True,
        help_text="Whether this template is active"
    )

    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_custom_email_templates',
        help_text="User who created this template"
    )

    class Meta:
        app_label = 'messaging'
        ordering = ['name']
        constraints = [
            models.UniqueConstraint(
                fields=['slug'],
                name='unique_custom_template_slug_per_tenant'
            )
        ]
        indexes = [
            models.Index(fields=['slug', 'is_active']),
            models.Index(fields=['created_at']),
        ]

    def __str__(self):
        status = "Active" if self.is_active else "Inactive"
        return f"{self.name} ({status})"

    def render(self, context: dict) -> dict:
        """
        Render the template with given context.

        Args:
            context: Dictionary of template variables

        Returns:
            Dict with 'subject', 'html', and 'text' keys
        """
        from .email_renderer import render_custom_email
        return render_custom_email(self, context)
