from functools import wraps

from django.contrib.syndication import views
from django.utils import feedgenerator
from django.utils.timezone import get_fixed_timezone

from .models import Article, Entry


def wraps_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        value = f(*args, **kwargs)
        return f"{value} -- decorated by @wraps."

    return wrapper


def common_decorator(f):
    def wrapper(*args, **kwargs):
        value = f(*args, **kwargs)
        return f"{value} -- common decorated."

    return wrapper


class TestRss2Feed(views.Feed):
    title = "My blog"
    description = "A more thorough description of my blog."
    link = "/blog/"
    feed_guid = "/foo/bar/1234"
    author_name = "Sally Smith"
    author_email = "test@example.com"
    author_link = "http://www.example.com/"
    categories = ("python", "django")
    feed_copyright = "Copyright (c) 2007, Sally Smith"
    ttl = 600

    def items(self):
        return Entry.objects.all()

    def item_description(self, item):
        return "Overridden description: %s" % item

    def item_pubdate(self, item):
        return item.published

    def item_updateddate(self, item):
        return item.updated

    def item_comments(self, item):
        return "%scomments" % item.get_absolute_url()

    item_author_name = "Sally Smith"
    item_author_email = "test@example.com"
    item_author_link = "http://www.example.com/"
    item_categories = ("python", "testing")
    item_copyright = "Copyright (c) 2007, Sally Smith"


class TestRss2FeedWithCallableObject(TestRss2Feed):
    class TimeToLive:
        def __call__(self):
            return 700

    ttl = TimeToLive()


class TestRss2FeedWithDecoratedMethod(TestRss2Feed):
    class TimeToLive:
        @wraps_decorator
        def __call__(self):
            return 800

    @staticmethod
    @wraps_decorator
    def feed_copyright():
        return "Copyright (c) 2022, John Doe"

    ttl = TimeToLive()

    @staticmethod
    def categories():
        return ("javascript", "vue")

    @wraps_decorator
    def title(self):
        return "Overridden title"

    @wraps_decorator
    def item_title(self, item):
        return f"Overridden item title: {item.title}"

    @wraps_decorator
    def description(self, obj):
        return "Overridden description"

    @wraps_decorator
    def item_description(self):
        return "Overridden item description"


class TestRss2FeedWithWrongDecoratedMethod(TestRss2Feed):
    @common_decorator
    def item_description(self, item):
        return f"Overridden item description: {item.title}"


class TestRss2FeedWithGuidIsPermaLinkTrue(TestRss2Feed):
    def item_guid_is_permalink(self, item):
        return True


class TestRss2FeedWithGuidIsPermaLinkFalse(TestRss2Feed):
    def item_guid(self, item):
        return str(item.pk)

    def item_guid_is_permalink(self, item):
        return False


class TestRss091Feed(TestRss2Feed):
    feed_type = feedgenerator.RssUserland091Feed


class TestNoPubdateFeed(views.Feed):
    title = "Test feed"
    link = "/feed/"

    def items(self):
        return Entry.objects.all()


class TestAtomFeed(TestRss2Feed):
    feed_type = feedgenerator.Atom1Feed
    subtitle = TestRss2Feed.description


class TestLatestFeed(TestRss2Feed):
    """
    A feed where the latest entry date is an `updated` element.
    """

    feed_type = feedgenerator.Atom1Feed
    subtitle = TestRss2Feed.description

    def items(self):
        return Entry.objects.exclude(title="My last entry")


class ArticlesFeed(TestRss2Feed):
    """
    A feed to test no link being defined. Articles have no get_absolute_url()
    method, and item_link() is not defined.
    """

    def items(self):
        return Article.objects.all()


class TestSingleEnclosureRSSFeed(TestRss2Feed):
    """
    A feed to test that RSS feeds work with a single enclosure.
    """

    def item_enclosure_url(self, item):
        return "http://example.com"

    def item_enclosure_size(self, item):
        return 0

    def item_mime_type(self, item):
        return "image/png"


class TestMultipleEnclosureRSSFeed(TestRss2Feed):
    """
    A feed to test that RSS feeds raise an exception with multiple enclosures.
    """

    def item_enclosures(self, item):
        return [
            feedgenerator.Enclosure("http://example.com/hello.png", 0, "image/png"),
            feedgenerator.Enclosure("http://example.com/goodbye.png", 0, "image/png"),
        ]


class TemplateFeed(TestRss2Feed):
    """
    A feed to test defining item titles and descriptions with templates.
    """

    title_template = "syndication/title.html"
    description_template = "syndication/description.html"

    # Defining a template overrides any item_title definition
    def item_title(self):
        return "Not in a template"


class TemplateContextFeed(TestRss2Feed):
    """
    A feed to test custom context data in templates for title or description.
    """

    title_template = "syndication/title_context.html"
    description_template = "syndication/description_context.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["foo"] = "bar"
        return context


class TestLanguageFeed(TestRss2Feed):
    language = "de"


class TestGetObjectFeed(TestRss2Feed):
    def get_object(self, request, entry_id):
        return Entry.objects.get(pk=entry_id)

    def items(self, obj):
        return Article.objects.filter(entry=obj)

    def item_link(self, item):
        return "%sarticle/%s/" % (item.entry.get_absolute_url(), item.pk)

    def item_comments(self, item):
        return "%scomments" % self.item_link(item)

    def item_description(self, item):
        return "Article description: %s" % item.title

    def item_title(self, item):
        return "Title: %s" % item.title


class NaiveDatesFeed(TestAtomFeed):
    """
    A feed with naive (non-timezone-aware) dates.
    """

    def item_pubdate(self, item):
        return item.published


class TZAwareDatesFeed(TestAtomFeed):
    """
    A feed with timezone-aware dates.
    """

    def item_pubdate(self, item):
        # Provide a weird offset so that the test can know it's getting this
        # specific offset and not accidentally getting on from
        # settings.TIME_ZONE.
        return item.published.replace(tzinfo=get_fixed_timezone(42))


class TestFeedUrlFeed(TestAtomFeed):
    feed_url = "http://example.com/customfeedurl/"


class MyCustomAtom1Feed(feedgenerator.Atom1Feed):
    """
    Test of a custom feed generator class.
    """

    def root_attributes(self):
        attrs = super().root_attributes()
        attrs["django"] = "rocks"
        return attrs

    def add_root_elements(self, handler):
        super().add_root_elements(handler)
        handler.addQuickElement("spam", "eggs")

    def item_attributes(self, item):
        attrs = super().item_attributes(item)
        attrs["bacon"] = "yum"
        return attrs

    def add_item_elements(self, handler, item):
        super().add_item_elements(handler, item)
        handler.addQuickElement("ministry", "silly walks")


class TestCustomFeed(TestAtomFeed):
    feed_type = MyCustomAtom1Feed


class TestSingleEnclosureAtomFeed(TestAtomFeed):
    """
    A feed to test that Atom feeds work with a single enclosure.
    """

    def item_enclosure_url(self, item):
        return "http://example.com"

    def item_enclosure_size(self, item):
        return 0

    def item_mime_type(self, item):
        return "image/png"


class TestMultipleEnclosureAtomFeed(TestAtomFeed):
    """
    A feed to test that Atom feeds work with multiple enclosures.
    """

    def item_enclosures(self, item):
        return [
            feedgenerator.Enclosure("http://example.com/hello.png", "0", "image/png"),
            feedgenerator.Enclosure("http://example.com/goodbye.png", "0", "image/png"),
        ]
