
from django.db import models

from apps.core.models import BaseModel, Language
from apps.channels.utils import channels_logo_upload_path, networks_logo_upload_path


class Genre(BaseModel):
    """
    A model to represent a Genre within the application.

    Attributes:
        name (CharField): The name of the genre, such as 'Action', 'Comedy', etc.
        description (TextField, optional): A brief description or additional information about the genre.
    
    Meta:
        verbose_name (str): Specifies the singular name for the model in the admin interface ('Genre').
        verbose_name_plural (str): Specifies the plural name for the model in the admin interface ('Genres').
    
    Methods:
        __str__(): Returns a human-readable representation of the genre object, displaying its name.
    """

    # Define the 'name' field to store the name of the genre.
    name = models.CharField(
        max_length=100,             # Set maximum length for the name to 100 characters.
        unique=True,               # Ensure that each genre name is unique within the database.
        help_text="Name of the Genre"  # Provide help text for this field.
    ) 
    # Define an optional 'description' field to provide additional information about the genre.
    description = models.TextField(
        blank=True,                # Allow this field to be blank (optional).
        null=True,                 # Allow this field to be null in the database.
        help_text="Description of the Genre"  # Help text for this field.
    )

    # Meta class configuration for the model.
    class Meta:
        verbose_name = "Genre"      # Singular name for the model in the admin interface.
        verbose_name_plural = "Genres"  # Plural name for the model in the admin interface.

    # Method to return a human-readable representation of the genre object.
    def __str__(self):
        """Returns a human-readable representation of the genre object."""
        return self.name  # Display the name of the genre.

class Network(BaseModel):
    """
    A model to represent a TV Network within the application.

    Attributes:
        name (CharField): The name of the TV Network, such as 'ABC', 'NBC', etc.
        description (TextField, optional): A brief description or additional information about the TV Network.
        channels (Reverse relation): A reverse relation to access channels associated with this network.
        channel_number (PositiveIntegerField): The channel number associated with this network.
    
    Meta:
        verbose_name (str): Specifies the singular name for the model in the admin interface ('Network').
        verbose_name_plural (str): Specifies the plural name for the model in the admin interface ('Networks').
    
    Methods:
        __str__(): Returns a human-readable representation of the network object, displaying its name.
    """

    # Define the 'name' field to store the name of the TV Network.
    name = models.CharField(
        max_length=200,
        unique=True,
        help_text="Name of the TV Network"
    ) 
    # Define an optional 'description' field for additional information about the TV Network.
    description = models.TextField(
        blank=True,
        null=True,
        help_text="Description of the TV Network"
    ) 
    # Define an ImageField to store the logo of the TV Network. 
    logo = models.ImageField(
        upload_to=networks_logo_upload_path,
        null=True,                    
        blank=True,                   
        help_text="Logo of the TV Network",
        default='networks/default_logo.png'  
    )
    
    # Meta class configuration for the model.
    class Meta:
        verbose_name = "Network"
        verbose_name_plural = "Networks"

    # Method to return a human-readable representation of the network object.
    def __str__(self):
        """Returns a human-readable representation of the network object."""
        return self.name

class Channel(BaseModel):
    """
    A model to represent a TV Channel within the application.

    Attributes:
        name (CharField): The name of the TV Channel.
        description (TextField, optional): A brief description of the TV Channel.
        channel_number (PositiveIntegerField): The channel number assigned to the TV Channel.
        networks (ManyToManyField): Represents multiple networks associated with the TV Channel.
        genres (ManyToManyField): Represents multiple genres associated with the TV Channel.
        languages (ManyToManyField): Represents multiple languages associated with the TV Channel.
        owner (CharField): The owner or broadcasting company of the TV Channel.
        launch_date (DateField): The date when the TV Channel was launched.
        website (URLField, optional): The official website URL of the TV Channel.
        headquarters_location (CharField, optional): The location of the TV Channel's headquarters. 
    """

    # Principal Fields
    # Define the channel number with a PositiveIntegerField and ensure it's unique.
    channel_number = models.PositiveIntegerField(
        unique=True, 
        help_text="Auto-incremented Channel Number"
    )
    # Define the name of the TV Channel with a maximum length of 200 characters and ensuring its uniqueness.
    name = models.CharField(
        max_length=200, 
        unique=True, 
        help_text="Name of the TV Channel"
    )
    # Define an optional description field for the TV Channel.
    description = models.TextField(
        blank=True, 
        null=True, 
        help_text="Description of the TV Channel"
    )
    # Define an ImageField to store the logo of the TV Channel. 
    logo = models.ImageField(
        upload_to=channels_logo_upload_path, 
        null=True,                    
        blank=True,                   
        help_text="Logo of the TV Channel",
        default='channels/default_logo.png'  
    )

    # # Relationships
    # Define a ManyToManyField relationship with the Network model to associate multiple networks with the TV Channel.
    networks = models.ManyToManyField(
        Network, 
        related_name='channels', 
        help_text="Networks associated with the TV Channel"
    )
    # Define a ManyToManyField relationship with the Genre model to associate multiple genres with the TV Channel.
    genres = models.ManyToManyField(
        Genre, 
        related_name='channels', 
        help_text="Genres associated with the TV Channel"
    )
    # Define a ManyToManyField relationship with the Language model to associate multiple languages with the TV Channel.
    languages = models.ManyToManyField(
        Language, 
        related_name='channels', 
        help_text="Languages associated with the TV Channel"
    )
    
    # # Additional Fields 
    # Define the owner or broadcasting company of the TV Channel.
    owner = models.CharField(
        max_length=200, 
        blank=True, 
        null=True, 
        help_text="Owner or Broadcasting Company of the TV Channel"
    ) 
    # Define the launch date of the TV Channel.
    launch_date = models.DateField(
        help_text="Date when the TV Channel was launched"
    ) 
    # Define an optional field for the official website URL of the TV Channel.
    website = models.URLField(
        blank=True, 
        null=True, 
        help_text="Official Website URL of the TV Channel"
    ) 
    # Define an optional field for the location of the TV Channel's headquarters.
    headquarters_location = models.CharField(
        max_length=200, 
        blank=True, 
        null=True, 
        help_text="Location of the TV Channel's Headquarters"
    )
    
    class Meta:
        # Specify the verbose name and verbose name plural for the model in the admin interface.
        verbose_name = "Channel"
        verbose_name_plural = "Channels"
        
        # Specify the default ordering of records based on the channel number.
        ordering = ['channel_number']

    def __str__(self):
        # Return a human-readable representation of the channel object displaying its name.
        return self.name
 
    # Override the save method to handle auto-incrementing logic.
    def save(self, *args, **kwargs):
        # Check if the object is being created (not updated).
        if not self.id:
            # Find the maximum channel_number and increment by 1.
            max_channel_number = Channel.objects.all().aggregate(models.Max('channel_number'))['channel_number__max']
            next_channel_number = (max_channel_number or 0) + 1
            self.channel_number = next_channel_number
        
        # Call the parent class's save method to save the object.
        super(Channel, self).save(*args, **kwargs)

class ChannelsZone(BaseModel):
    """
    Model representing a specific zone associated with a TV Channel.

    Attributes: 
        channel (ForeignKey): Reference to the associated TV Channel.
        region (CharField): Region name associated with the zone.
        zonename (CharField): Name of the specific zone.
    """
 
    # ForeignKey relationship to the Channels model.
    channel = models.ForeignKey(
        Channel, 
        models.DO_NOTHING, 
        db_column='channel', 
        blank=True, 
        null=True,
        help_text="Reference to the associated TV Channel."
    ) 
    # Field to store the region name associated with the zone.
    region = models.CharField(
        max_length=255, 
        blank=True, 
        null=True, 
        help_text="Region name associated with the zone."
    ) 
    # Field to store the name of the specific zone.
    zonename = models.CharField(
        max_length=255, 
        blank=True, 
        null=True, 
        help_text="Name of the specific zone."
    )

    class Meta:
        """
        Metadata for the ChannelsZone model.
        """
        verbose_name = "Channels Zone"
        verbose_name_plural = "Channels Zones"

    def __str__(self):
        """
        Returns a human-readable representation of the ChannelsZone object.
        
        Returns:
            str: A string representation of the ChannelsZone object.
        """
        return f"{self.zonename} ({self.region}) - Channel: {self.channel.name if self.channel else 'Unknown Channel'}"
 
class ChannelNetworkMembership(BaseModel): 
    """
    Represents the relationship between a TV Channel and a TV Network.
    
    This model acts as an intermediary table to manage the many-to-many relationship 
    between the `Channel` and `Network` models.
    """
    
    # The name of the TV Network. This field is optional and can be left blank if necessary.
    channel_name = models.CharField(
        max_length=200,
        unique=True,           # Ensures each channel name is unique.
        blank=True,            # Allows the field to be optional.
        help_text="Name of the TV Network (optional)"
    ) 
    # The channel number associated with this network.
    channel_number = models.PositiveIntegerField(
        unique=True,           # Ensures each channel number is unique.
        help_text="Channel Number associated with this Network"
    ) 
    # A foreign key linking to the `Channel` model, representing the TV Channel associated with this membership.
    channel = models.ForeignKey(
        Channel,
        on_delete=models.CASCADE,    # If a channel is deleted, all its related memberships will also be deleted.
        related_name='channel_memberships',  # Reverse relationship name for accessing memberships from a channel object.
        help_text="The TV Channel associated with this membership"
    ) 
    # A foreign key linking to the `Network` model, representing the TV Network associated with this membership.
    network = models.ForeignKey(
        Network,
        on_delete=models.CASCADE,    # If a network is deleted, all its related memberships will also be deleted.
        related_name='network_memberships',  # Reverse relationship name for accessing memberships from a network object.
        help_text="The TV Network associated with this membership"
    )
    
    class Meta:
        # Ensures a channel can be associated with a network only once.
        unique_together = ('channel', 'network')
    
    def __str__(self):
        """
        Return a string representation of the ChannelNetworkMembership.
        
        The string representation includes the channel name (if available) and the channel number.
        """
        return f"Network {self.network.name} - {self.channel_name} - Channel Number: {self.channel_number}"
    
class Jingle(BaseModel):
    """
    Model representing a jingle or advertisement associated with a TV Channel.

    Attributes:
        channel (ForeignKey): Reference to the associated TV Channel.
        title (CharField): Title or name of the jingle.
        description (TextField, optional): Description or details about the jingle. 
        video_file (FileField): File field to upload the jingle video file.
        md5_file (FileField): File field to upload the MD5 file for the jingle.
        status (CharField): Status of the jingle (e.g., Active, Inactive, Pending). 
    """
    
    # Choices for the status field.
    STATUS_CHOICES = [
        ('Active', 'Active'),
        ('Inactive', 'Inactive'),
        ('Pending', 'Pending'),
    ]

    # ForeignKey relationship to the Channel model.
    channel = models.ForeignKey(
        'Channel',  # Using quotes for ForeignKey to avoid circular import issues.
        on_delete=models.CASCADE,  # If the associated channel is deleted, delete the jingle as well.
        related_name='channel_jingles',  # Reverse relation to access jingles from a channel instance.
        help_text="Reference to the associated TV Channel."
    ) 
    # Field to store the title or name of the jingle.
    title = models.CharField(
        max_length=200,
        help_text="Title or name of the jingle."
    ) 
    # Optional field to store a description or details about the jingle.
    description = models.TextField(
        blank=True, 
        null=True, 
        help_text="Description or details about the jingle."
    )        
    # Field to store the status of the jingle.
    status = models.CharField(
        max_length=10, 
        choices=STATUS_CHOICES, 
        default='Pending',
        help_text="Status of the jingle (e.g., Active, Inactive, Pending)."
    )
    
    # FileField to upload the jingle video file.
    video_file = models.FileField( 
        upload_to='jingles/video/',  # Upload directory for the jingle video files.
        help_text="Upload the jingle video file."
    ) 
    # FileField to upload the MD5 file for the jingle.
    md5_file = models.FileField(
        upload_to='jingles/md5/',  # Upload directory for the jingle MD5 files.
        help_text="Upload the MD5 file for the jingle."
    )  
     
    class Meta:
        """
        Metadata for the Jingle model.
        """
        verbose_name = "Jingle"
        verbose_name_plural = "Jingles"

    def __str__(self):
        """
        Returns a human-readable representation of the Jingle object.
        
        Returns:
            str: A string representation of the Jingle object.
        """
        return f"{self.title} - Channel: {self.channel.name if self.channel else 'Unknown Channel'}"

