"""
Models for app MisCosas
"""

import os

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
from django.utils.html import format_html, format_html_join

from .apps import MisCosasConfig as Config
from .feeds.feedhandler import FEEDS_DATA

def get_feed_help_texts():
    """Help texts for the feed model."""
    help_texts = [
        (Config.SOURCES[Config.YOUTUBE], _('A channel identifier')),
        (Config.SOURCES[Config.LASTFM], _('The name of a musical artist')),
        (Config.SOURCES[Config.REDDIT], _('The name of a subreddit')),
        (Config.SOURCES[Config.FLICKR], _('A tag for pictures')),
        (Config.SOURCES[Config.GOODREADS], _('The name of a book author')),
        (Config.SOURCES[Config.SPOTIFY], _('The name of a musical artist')),
    ]
    help_items = format_html_join('', '<li>{}: {}</li>', (help_text for help_text in help_texts))
    return format_html('<ul>{}</ul>', help_items)


class Feed(models.Model):
    key = models.CharField(max_length=64,
        verbose_name=_('key'),
        help_text=get_feed_help_texts())
    title = models.CharField(max_length=64, verbose_name=_('title'))

    # The code for the external web the data comes from
    source = models.CharField(max_length=32,
        choices=list(Config.SOURCES.items()),
        default=Config.YOUTUBE,
        verbose_name=_('source'))

    # Whether the feed is chosen to be shown on the main page or not
    chosen = models.BooleanField(default=True, verbose_name=_('chosen'))

    class Meta:
        verbose_name = _('feed')
        verbose_name_plural = _('feeds')

    def __str__(self):
        return Config.SOURCES[self.source] + ': ' + self.title

    @property
    def source_pretty(self):
        """Full string name of the source."""
        return Config.SOURCES[self.source]

    @property
    def score(self):
        """The sum of all the votes made on this feed's items."""
        upvotes = Vote.objects.filter(item__feed=self, positive=True).count()
        downvotes = Vote.objects.filter(item__feed=self, positive=False).count()
        return upvotes - downvotes

    @property
    def link(self):
        """Link to the external page for this feed."""
        return FEEDS_DATA[self.source].get_feed_url(self.key)


class Item(models.Model):
    key = models.CharField(max_length=64, verbose_name=_('key'))
    title = models.CharField(max_length=64, verbose_name=_('title'))
    # The feed the item came from
    feed = models.ForeignKey(Feed, models.CASCADE,
        related_name="items",
        verbose_name=_('feed'))
    description = models.TextField(blank=True, default='', verbose_name=_('description'))
    picture = models.URLField(blank=True, default='', verbose_name=_('picture'))

    class Meta:
        verbose_name = _('item')
        verbose_name_plural = _('items')

    def __str__(self):
        return str(self.feed) + ", " + self.title

    @property
    def upvote_count(self):
        """Number of upvotes to this item."""
        return self.votes.filter(positive=True).count()

    @property
    def downvote_count(self):
        """Number of downvotes to this item."""
        return self.votes.filter(positive=False).count()

    @property
    def upvoters(self):
        """List of ids of users that have upvoted this item."""
        ups = self.votes.filter(positive=True)
        return [vote.user.pk for vote in ups]

    @property
    def downvoters(self):
        """List of ids of users that have downvoted this item."""
        downs = self.votes.filter(positive=False)
        return [vote.user.pk for vote in downs]

    @property
    def link(self):
        """Link to the external page for this item."""
        return FEEDS_DATA[self.feed.source].get_item_url(self.feed.key, self.key)


class Vote(models.Model):
    # Whether it was a positive or a negative vote
    positive = models.BooleanField(verbose_name=_('positive'))
    date = models.DateTimeField(auto_now=True, verbose_name=_('date'))
    user = models.ForeignKey(User, models.CASCADE,
        related_name='votes', verbose_name=_('user'))
    item = models.ForeignKey(Item, models.CASCADE,
        related_name='votes', verbose_name=_('item'))

    class Meta:
        unique_together = ('user', 'item',)
        verbose_name = _('vote')
        verbose_name_plural = _('votes')


class Comment(models.Model):
    title = models.CharField(max_length=64, verbose_name=_('title'))
    content = models.CharField(max_length=256, verbose_name=_('content'))
    date = models.DateTimeField(auto_now=True, verbose_name=_('date'))
    item = models.ForeignKey(Item, models.CASCADE,
        related_name='comments', verbose_name=_('item'))
    user = models.ForeignKey(User, models.CASCADE,
        related_name='comments', verbose_name=_('user'))

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = _('comment')
        verbose_name_plural = _('comments')


class Profile(models.Model):
    DEFAULT_PICTURE = 'blank-profile-picture.png'

    user = models.OneToOneField(User, models.CASCADE, verbose_name=_('user'))
    _picture = models.ImageField(blank=True, null=True, verbose_name=_('picture'))
    theme = models.CharField(max_length=2, choices=Config.THEMES,
        default=Config.LIGHTMODE, verbose_name=_('theme'))
    font_size = models.CharField(max_length=2, choices=Config.FONT_SIZES,
        default=Config.MEDIUM_FONT, verbose_name=_('font size'))
    chosen_feeds = models.ManyToManyField(Feed, related_name='users')

    class Meta:
        verbose_name = _('profile')
        verbose_name_plural = _('profiles')

    def __str__(self):
        return str(self.user)

    @property
    def picture(self):
        """Current picture for the user."""
        try:
            if os.path.isfile(self._picture.path):
                return self._picture.name
            Profile.objects.filter(pk=self.pk).update(_picture=None)
            return self.DEFAULT_PICTURE
        except ValueError:
            return self.DEFAULT_PICTURE

    @property
    def vote_count(self):
        """Amount of votes the user has made."""
        return self.user.votes.count()

    @property
    def comment_count(self):
        """Amount of comments the user made."""
        return self.user.comments.count()


@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    """Makes sure that a profile is created for each user."""
    if created:
        Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    """Saves the profile data when the user is saved."""
    instance.profile.save()

@receiver(models.signals.post_delete, sender=Profile)
def auto_delete_file_on_delete(sender, instance, **kwargs):
    """Deletes file from filesystem
    when corresponding Profile object is deleted."""
    if instance.picture and instance.picture != Profile.DEFAULT_PICTURE:
        if os.path.isfile(instance._picture.path):
            os.remove(instance._picture.path)

@receiver(models.signals.pre_save, sender=Profile)
def auto_delete_file_on_change(sender, instance, **kwargs):
    """Deletes old file from filesystem
    when corresponding Profile object is updated
    with new file."""
    try:
        old_profile = Profile.objects.get(pk=instance.pk)
        new_file = instance.picture
        if old_profile.picture != new_file and old_profile.picture != Profile.DEFAULT_PICTURE:
            if os.path.isfile(old_profile._picture.path):
                os.remove(old_profile._picture.path)
    except Profile.DoesNotExist:
        return False
