from django.db import models
from django.core.exceptions import ValidationError
from django.contrib.auth import get_user_model
from coresite.mixin import AbstractTimeStampModel
from apps.companies.constants import BotName, CompanyType


User = get_user_model()


class CompanyBotSettings(AbstractTimeStampModel):
    """Settings for company-specific bot configurations"""
    company = models.ForeignKey(
        'Company',
        on_delete=models.CASCADE,
        related_name='bot_settings'
    )
    bot_name = models.CharField(max_length=50, choices=BotName)
    phone_number = models.CharField(
        max_length=20,
        unique=True,
        null=True, blank=True,
        help_text="Phone number of the bot"
    )
    is_active = models.BooleanField(
        default=True,
        help_text="Whether this bot setting is active"
    )

    def __str__(self):
        return f"{self.company.name} - {self.bot_name}"

class Holiday(AbstractTimeStampModel):
    """Holiday model for company-specific holidays"""
    company = models.ForeignKey('Company', on_delete=models.CASCADE, related_name='holidays')
    name = models.CharField(max_length=255, help_text="Holiday name (e.g., Christmas, EID)")
    message = models.TextField(null=True, blank=True, help_text="Holiday message")
    start_date = models.DateField(help_text="Start date of holiday period")
    end_date = models.DateField(help_text="End date of holiday period")
    is_active = models.BooleanField(default=True, help_text="Whether this holiday is active")
    bots = models.ManyToManyField(
        'CompanyBotSettings',
        blank=True,
        related_name='holidays',
        help_text="Bots this holiday applies to"
    )

    class Meta:
        unique_together = ['company', 'name', 'start_date']  # Prevent duplicate holidays
        ordering = ['start_date']
        verbose_name_plural = "Holidays"

    def __str__(self):
        if self.start_date == self.end_date:
            return f"{self.company.name} - {self.name} ({self.start_date})"
        return f"{self.company.name} - {self.name} ({self.start_date} to {self.end_date})"

    def clean(self):
        """Validate holiday dates"""
        super().clean()

        if self.start_date and self.end_date:
            if self.start_date > self.end_date:
                raise ValidationError("End date must be after or equal to start date")

    def save(self, *args, **kwargs):
        self.clean()
        super().save(*args, **kwargs)

    def get_duration_days(self):
        """Get number of days in holiday period"""
        if self.start_date and self.end_date:
            return (self.end_date - self.start_date).days + 1
        return 0

class OfficeHours(AbstractTimeStampModel):
    """Office hours configuration for each day of the week"""
    DAY_CHOICES = [
        ('monday', 'Monday'),
        ('tuesday', 'Tuesday'),
        ('wednesday', 'Wednesday'),
        ('thursday', 'Thursday'),
        ('friday', 'Friday'),
        ('saturday', 'Saturday'),
        ('sunday', 'Sunday'),
    ]
    
    company = models.ForeignKey('Company', on_delete=models.CASCADE, related_name='office_hours')
    day = models.CharField(max_length=10, choices=DAY_CHOICES)
    is_open = models.BooleanField(default=False, help_text="Whether the office is open on this day")
    start_time = models.TimeField(null=True, blank=True, help_text="Office opening time")
    end_time = models.TimeField(null=True, blank=True, help_text="Office closing time")
    
    class Meta:
        unique_together = ['company', 'day']
        ordering = ['day']
        verbose_name_plural = "Office Hours"
    
    def __str__(self):
        if self.is_open:
            return f"{self.company.name} - {self.get_day_display()}: {self.start_time} - {self.end_time}"
        return f"{self.company.name} - {self.get_day_display()}: Closed"
    
    def clean(self):
        """Validate office hours configuration"""
        super().clean()
        
        if self.is_open:
            if not self.start_time or not self.end_time:
                raise ValidationError("Start and end times are required when office is open")
            
            if self.start_time >= self.end_time:
                raise ValidationError("End time must be after start time")
    
    def save(self, *args, **kwargs):
        self.clean()
        super().save(*args, **kwargs)

class SalesTiming(AbstractTimeStampModel):
    """Sales timing configuration for bots"""
    BOT_CHOICES = [
        ('hazel', 'Hazel'),
    ]
    company = models.ForeignKey('Company', on_delete=models.CASCADE, related_name='sales_timings')
    start_time = models.TimeField(help_text="Sales start time")
    end_time = models.TimeField(help_text="Sales end time")
    is_active = models.BooleanField(default=True, help_text="Whether this sales timing is active")
    bot = models.CharField(max_length=100, choices=BOT_CHOICES, help_text="Bot this sales timing applies to")
    
    class Meta:
        verbose_name_plural = "Sales Timings"
        ordering = ['company', 'start_time']
    
    def __str__(self):
        return f"{self.company.name} - {self.bot}: {self.start_time} - {self.end_time}"
    
    def clean(self):
        """Validate sales timing configuration"""
        super().clean()
        
        if not self.start_time or not self.end_time:
            raise ValidationError("Start and end times are required")
        
        if self.start_time >= self.end_time:
            raise ValidationError("End time must be after start time")
    
    def save(self, *args, **kwargs):
        self.clean()
        super().save(*args, **kwargs)

class CatchPhrase(AbstractTimeStampModel):
    """Catchphrases for companies"""
    company = models.ForeignKey(
        'Company',
        on_delete=models.CASCADE,
        related_name='catch_phrases'
    )
    phrase = models.CharField(
        max_length=255,
        help_text="Short catch phrase"
    )

    match_type = models.CharField(
        max_length=20,
        choices=[
            ("string", "String Match"),
            ("word", "Word Boundary"),
            ("semantic", "Semantic (LLM)"),
        ],
        default="string",
        help_text="How this phrase should be detected"
    )

    synonyms = models.JSONField(
        default=list,
        blank=True,
        help_text="Alternative phrasings or variations"
    )

    allowed_speakers = models.JSONField(
        default=list,
        blank=True,
        help_text="e.g. ['user', 'agent']"
    )

    min_confidence = models.FloatField(
        default=0.7,
        help_text="Ignore detections below this confidence"
    )

    is_active = models.BooleanField(default=True)

    class Meta:
        verbose_name_plural = "Catch Phrases"
        unique_together = ("company", "phrase")
        ordering = ['company', 'phrase']
        indexes = [
            models.Index(fields=["company", "is_active"]),
        ]
    
    def __str__(self):
        return f"{self.company.name} - {self.phrase}"
    
    def clean(self):
        """Validate catchphrase"""
        super().clean()
        
        if not self.phrase or not self.phrase.strip():
            raise ValidationError("Catch phrase cannot be empty")
    
    def save(self, *args, **kwargs):
        self.clean()
        super().save(*args, **kwargs)

class CallCatchPhrase(models.Model):
    call = models.ForeignKey(
        'calls.Call',
        on_delete=models.CASCADE,
        related_name="catch_phrases"
    )
    phrase = models.ForeignKey(
        "CatchPhrase",
        on_delete=models.CASCADE,
        related_name="occurrences"
    )
    company = models.ForeignKey(
        "companies.Company",
        on_delete=models.CASCADE
    )

    speaker = models.CharField(
        max_length=20,
        choices=[
            ("agent", "Agent"),
            ("user", "User"),
            ("advisor", "Advisor"),
        ],
        null=True,
        blank=True
    )

    confidence = models.FloatField(
        null=True,
        blank=True,
        help_text="Confidence score between 0 and 1"
    )

    source = models.CharField(
        max_length=20,
        choices=[
            ("string", "String Match"),
            ("assemblyai", "AssemblyAI"),
            ("llm", "LLM"),
        ]
    )

    matched_text = models.TextField(
        blank=True,
        help_text="Exact utterance text that triggered the phrase"
    )

    detected_at = models.DateTimeField(
        auto_now_add=True,
        help_text="When phrase was detected"
    )

    model_version = models.CharField(
        max_length=50,
        blank=True,
        help_text="AI / model version used for detection"
    )

    class Meta:
        unique_together = ("call", "phrase")
        indexes = [
            models.Index(fields=["company", "phrase"]),
            models.Index(fields=["call"]),
        ]

    def __str__(self):
        return f"{self.call_id} → {self.phrase.phrase}"

class CompanyHistory(AbstractTimeStampModel):
    """History tracking for all company-related changes"""
    company = models.ForeignKey('Company', on_delete=models.CASCADE, related_name='history')
    updated_by = models.ForeignKey('core.User', on_delete=models.CASCADE, related_name='company_actions')
    action = models.CharField(max_length=50, help_text="Action performed: created, updated, deleted")
    model_name = models.CharField(max_length=100, help_text="Name of the model that was changed")
    object_id = models.IntegerField(null=True, blank=True, help_text="ID of the changed object")
    details = models.TextField(help_text="Human readable description of what changed", null=True, blank=True)
    
    class Meta:
        verbose_name_plural = "Company History"
        ordering = ['-created_at']  # Most recent first
    
    def __str__(self):
        return f"{self.company.name} - {self.action} {self.model_name} (ID: {self.object_id}) by {self.updated_by.username}"

class Company(AbstractTimeStampModel):
    """Company model for multi-tenancy"""
    ghl_company_id = models.CharField(
        max_length=255, unique=True,
        null=True, blank=True,
        help_text="Unique identifier from GHL system"
    )
    name = models.CharField(max_length=255)
    phone = models.CharField(max_length=50, blank=True, null=True)
    office_time = models.TextField(blank=True, null=True)
    bdc_number = models.CharField(max_length=50, blank=True, null=True)
    timezone = models.CharField(max_length=50, default='UTC')
    missing_call_notify_to = models.ManyToManyField(
        User,
        blank=True,
        related_name='missing_call_notify_companies',
        help_text="Users who will be notified when calls are missed"
    )
    default_transfer_staff = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='default_transfer_companies',
        help_text="Default user to receive calls when no other agents are available"
    )
    website = models.TextField(blank=True, null=True)
    bio = models.TextField(blank=True, null=True)

    company_type = models.CharField(
        max_length=50,
        default=CompanyType.SIMPLE,
        choices=CompanyType.model_choices(),
        null=True, blank=True
    )
    is_dealership = models.BooleanField(default=False, null=True, blank=True)
    contact_person_name = models.CharField(max_length=255, null=True, blank=True)
    contact_phone = models.CharField(max_length=50, null=True, blank=True)

    is_active = models.BooleanField(default=True)

    def __str__(self):
        return f"{self.name} - {"Active" if self.is_active else "Inactive"}"
    
    def get_user_count(self):
        """Get the number of users in this company"""
        return self.users.count()
    
    def get_active_users(self):
        """Get all active users in this company"""
        return self.users.filter(is_active=True)

    def get_all_users(self):
        """Get all active users in this company"""
        return self.users.filter()

    def get_default_transfer_staff_name(self):
        """Get the name of default transfer staff"""
        if self.default_transfer_staff:
            return self.default_transfer_staff.username
        return "Not Set"
    
    def get_office_hours_for_day(self, day_name):
        """Get office hours for a specific day"""
        try:
            return self.office_hours.get(day=day_name.lower())
        except OfficeHours.DoesNotExist:
            return None
    
    def get_office_hours_summary(self):
        """Get a summary of office hours for display"""
        summary = []
        
        for day in ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']:
            hours = self.get_office_hours_for_day(day)
            if hours and hours.is_open:
                summary.append(f"{hours.get_day_display()}: {hours.start_time} - {hours.end_time}")
            else:
                # Handle case where no office hours exist for this day
                day_display = day.title()  # Capitalize first letter
                summary.append(f"{day_display}: Closed")
        
        return summary
    
    def get_active_sales_timings(self):
        """Get all active sales timings for this company"""
        return self.sales_timings.filter(is_active=True)
    
    def get_sales_timings_summary(self):
        """Get a summary of sales timings for display"""
        summary = []
        for timing in self.get_active_sales_timings():
            summary.append(f"{timing.bot}: {timing.start_time} - {timing.end_time}")
        return summary if summary else ["No active sales timings"]
    
    def get_catch_phrases_list(self):
        """Get list of all catchphrases for this company"""
        return list(self.catch_phrases.values_list('phrase', flat=True))
    
    def get_catch_phrases_summary(self):
        """Get a summary of catchphrases for display"""
        phrases = self.get_catch_phrases_list()
        return phrases if phrases else ["No catch phrases set"]

    class Meta:
        verbose_name_plural = "Companies"

class Dealership(AbstractTimeStampModel):
    company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="dealerships")
    name = models.CharField(max_length=255)
    contact_person_name = models.CharField(max_length=255)
    contact_phone = models.CharField(max_length=50, unique=True)

    class Meta:
        unique_together = ('company', 'name')

    def __str__(self):
        return self.name + ' - ' + self.company.name

class DealershipDepartment(AbstractTimeStampModel):
    name = models.CharField(max_length=255)
    phone_number = models.CharField(
        max_length=50,
        null=True, blank=True
    )
    dealership = models.ForeignKey(
        Dealership,
        on_delete=models.CASCADE,
        related_name="departments"
    )

    class Meta:
        unique_together = ('dealership', 'name')

    def __str__(self):
        return self.name + ' - ' + self.dealership.name

class Service(AbstractTimeStampModel):
    """
    Represents a service offered by a company (fully dynamic, no BookingType).
    """
    company = models.ForeignKey(
        Company,
        on_delete=models.CASCADE,
        related_name="services",
        db_index=True,
    )

    name = models.CharField(
        max_length=150,
        db_index=True,
    )

    code = models.CharField(
        max_length=150,
        db_index=True,
        help_text="Unique service identifier inside a company, e.g. OIL_CHANGE"
    )

    description = models.TextField(null=True, blank=True)
    is_active = models.BooleanField(default=True)

    class Meta:
        unique_together = ("company", "code")
        indexes = [
            models.Index(fields=["company", "is_active"]),
            models.Index(fields=["company", "name"]),
        ]

    def __str__(self):
        return f"{self.company.name} - {self.name}"

class ServicePrice(AbstractTimeStampModel):
    """
    Price configuration for a service per company.
    Supports future price versions and labor based pricing.
    """

    service = models.ForeignKey(
        Service,
        on_delete=models.CASCADE,
        related_name="prices",
        db_index=True
    )

    company = models.ForeignKey(
        Company,
        on_delete=models.CASCADE,
        related_name="service_prices",
        db_index=True
    )

    base_price = models.DecimalField(max_digits=10, decimal_places=2)

    labor_rate_per_hour = models.DecimalField(
        max_digits=10, decimal_places=2,
        null=True, blank=True
    )

    estimated_hours = models.DecimalField(
        max_digits=5, decimal_places=2,
        null=True, blank=True
    )

    starting_at = models.BooleanField(default=True)
    currency = models.CharField(max_length=10, default="USD")

    is_active = models.BooleanField(default=True)

    effective_from = models.DateField(null=True, blank=True)
    effective_to = models.DateField(null=True, blank=True)

    class Meta:
        unique_together = ("service", "company", "effective_from")
        indexes = [
            models.Index(fields=["company", "is_active"]),
            models.Index(fields=["service", "is_active"]),
            models.Index(fields=["effective_from", "effective_to"]),
        ]

    def clean(self):
        """
        Prevent overlapping active price ranges for the same service and company.
        """
        if self.effective_from and self.effective_to:
            if self.effective_from > self.effective_to:
                raise ValidationError("effective_from cannot be after effective_to")

    @property
    def total_price(self):
        # we're treating estimated_hours as minutes
        if self.labor_rate_per_hour and self.estimated_hours:
            return self.base_price + (self.labor_rate_per_hour * (self.estimated_hours * 60))
        return self.base_price
