
import logging

from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
from django.db.models.signals import post_save
from django.dispatch import receiver

log = logging.getLogger(__name__)


# region Server

class Server(models.Model):
    """
    Represents a Discord Server.
    Instances of this model are automatically handled, and manual intervension
    should be avoided if possible.    
    """

    id = models.PositiveBigIntegerField(primary_key=True)
    name = models.CharField(max_length=128)
    icon_hash = models.CharField(max_length=128, blank=True, null=True)
    is_bot_operational = models.BooleanField(default=None, null=True)
    active = models.BooleanField(default=True)

    class Meta:
        verbose_name = "server"
        verbose_name_plural = "servers"
        get_latest_by = "name"

    @property
    def icon_url(self):
        return f"https://cdn.discordapp.com/icons/{self.id}/{self.icon_hash}.webp?size=80"

    def __str__(self):
        return self.name


# region Content Filter

class ContentFilter(models.Model):
    """
    Filters for the content produced by Subscriptions.
    Owned by the related server.
    """

    id = models.AutoField(primary_key=True)
    server = models.ForeignKey(to=Server, on_delete=models.CASCADE)

    MATCH_NONE = 0
    MATCH_ANY = 1
    MATCH_ALL = 2
    MATCH_LITERAL = 3
    MATCH_REGEX = 4
    MATCH_FUZZY = 5
    MATCH_AUTO = 6

    MATCHING_ALGORITHMS = (
        (MATCH_NONE, _("None")),
        (MATCH_ANY, _("Any: Item contains any of these words (space separated)")),
        (MATCH_ALL, _("All: Item contains all of these words (space separated)")),
        (MATCH_LITERAL, _("Exact: Item contains this string")),
        (MATCH_REGEX, _("Regular expression: Item matches this regex")),
        (MATCH_FUZZY, _("Fuzzy: Item contains a word similar to this word")),
    )

    name = models.CharField(max_length=32)
    match = models.CharField(max_length=256, blank=False)
    matching_algorithm = models.PositiveIntegerField(choices=MATCHING_ALGORITHMS)
    is_insensitive = models.BooleanField()
    is_whitelist = models.BooleanField()

    class Meta:
        verbose_name = "filter"
        verbose_name_plural = "filters"
        get_latest_by = "id"

    def __str__(self):
        return self.name


# region Message Mutator

class MessageMutator(models.Model):
    """
    Mutators to be applied via the Bot.
    Instances of this model are predefined via migrations.
    Manual editing should be avoided at all costs!
    """

    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=64)
    value = models.CharField(max_length=32)

    class Meta:
        verbose_name = "message mutator"
        verbose_name_plural = "message mutators"
        get_latest_by = "id"

    def __str__(self):
        return self.name


# region Message Style

class MessageStyle(models.Model):
    """
    Custom styles to be applied via the Bot.
    Owned by the related server.
    """

    id = models.AutoField(primary_key=True)
    server = models.ForeignKey(to=Server, on_delete=models.CASCADE, null=True, blank=False)

    name = models.CharField(max_length=32)
    colour = models.CharField(max_length=6, default="3498db")
    is_embed = models.BooleanField()
    is_hyperlinked = models.BooleanField()  # title only
    show_author = models.BooleanField()
    show_timestamp = models.BooleanField()
    show_images = models.BooleanField()
    fetch_images = models.BooleanField()  # if not included with RSS item

    title_mutator = models.ForeignKey(
        to=MessageMutator,
        related_name="title_mutated_messagestyle",
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )
    description_mutator = models.ForeignKey(
        to=MessageMutator,
        related_name="desc_mutated_messagestyle",
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )

    auto_created = models.BooleanField(default=False, blank=True)

    class Meta:
        verbose_name = "message style"
        verbose_name_plural = "message styles"
        get_latest_by = "id"

    def delete(self, *args, **kwargs):
        if self.auto_created:
            raise ValidationError("Cannot delete 'MessageStyle' instance with 'auto_created=True'")

        # If this style is being used, reset the users to the default style
        default_message_style = MessageStyle.objects.get(server=self.server, auto_created=True)
        Subscription.objects \
            .filter(server=self.server, message_style=self) \
            .update(message_style=default_message_style)

        super().delete(*args, **kwargs)

    def __str__(self):
        return self.name


@receiver(post_save, sender=Server)
def create_default_items(sender, instance, created, **kwargs):
    if not created:
        return

    # Create a default message style, so the user can get straight into creating subscriptions
    # (subscriptions require a message style to exist)
    MessageStyle.objects.create(
        server=instance,
        name=_("Default Message Style"),
        colour="3498db",
        is_embed=True,
        is_hyperlinked=True,
        show_author=True,
        show_timestamp=True,
        show_images=True,
        fetch_images=True,
        title_mutator=None,
        description_mutator=None,
        auto_created=True
    )


# region Discord Channel

class DiscordChannel(models.Model):
    """
    Store limited data on a relevant Channel from Discord, used to indicate
    where subscriptions should send content to. Instance creation & deletion
    is handled internally, when Subscriptions are modified.
    """

    id = models.PositiveBigIntegerField(primary_key=True)
    server = models.ForeignKey(to=Server, on_delete=models.CASCADE, blank=False)
    name = models.CharField(max_length=128)
    is_nsfw = models.BooleanField()

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


# region Subscription

class Subscription(models.Model):
    """
    These represent RSSFeeds, storing relevant settings for managing them.
    Owned by the related server.    
    """

    id = models.AutoField(primary_key=True)
    server = models.ForeignKey(to=Server, on_delete=models.CASCADE, blank=False)

    name = models.CharField(max_length=32, blank=False)
    url = models.URLField()
    created_at = models.DateTimeField(default=timezone.now, editable=False)
    updated_at = models.DateTimeField(default=timezone.now)
    extra_notes = models.CharField(max_length=250, default="", blank=True)
    active = models.BooleanField(default=True)

    publish_threshold = models.DateTimeField(default=timezone.now)
    channels = models.ManyToManyField(to=DiscordChannel, related_name="subscriptions", blank=False)
    filters = models.ManyToManyField(to=ContentFilter, blank=True)
    message_style = models.ForeignKey(to=MessageStyle, on_delete=models.SET_NULL, null=True, blank=False)

    class Meta:
        verbose_name = "subscription"
        verbose_name_plural = "subscriptions"
        get_latest_by = "updated_at"

    def __str__(self):
        return self.name


# region Content

class Content(models.Model):
    """
    Represents a processed item created from a Subscription.
    """

    id = models.AutoField(primary_key=True)
    subscription = models.ForeignKey(to=Subscription, on_delete=models.CASCADE)

    # 'item_' prefix is to differentiate between the internal identifiers and the stored data
    item_id = models.CharField(max_length=1024)
    item_guid = models.CharField(max_length=1024)
    item_url = models.CharField(max_length=1024)
    item_title = models.CharField(max_length=1024)
    item_description = models.CharField(max_length=1024)
    item_content_hash = models.CharField(max_length=1024)
    item_image_url = models.URLField(null=True, blank=True)
    item_thumbnail_url = models.URLField(null=True, blank=True)
    item_published = models.DateField(null=True, blank=True)
    item_author = models.CharField(max_length=256, null=True, blank=True)
    item_author_url = models.URLField(null=True, blank=True)
    item_feed_title = models.CharField(max_length=1024)
    item_feed_url = models.URLField()

    blocked = models.BooleanField(default=False)

    class Meta:
        verbose_name = "content"
        verbose_name_plural = "content"
        get_latest_by = "id"

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


# region Bot Logic Logs

# Relevant logs from the bot logic
class BotLogicLogs(models.Model):
    id = models.AutoField(primary_key=True)
    server = models.ForeignKey(to=Server, on_delete=models.CASCADE)

    level = models.CharField(max_length=32)
    message = models.CharField(max_length=256)
    created_at = models.DateTimeField(default=timezone.now, editable=False)

    class Meta:
        verbose_name = "bot logic log"
        verbose_name_plural = "bot logic logs"
        get_latest_by = "id"

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