import json

from django.contrib.syndication.views import Feed, FeedDoesNotExist
from django.shortcuts import get_object_or_404
from django.utils.feedgenerator import Atom1Feed
from django.core.urlresolvers import reverse
from django.db.models import Q

from euscan.version import gentoo_unstable

from djeuscan.models import Package, Herd, Maintainer, VersionLog

from euscan_accounts.helpers import get_profile


class BaseFeed(Feed):
    feed_type = Atom1Feed
    author_name = 'euscan'
    item_author_name = author_name
    ttl = 3600

    def item_title(self, vlog):
        return str(vlog)

    def item_description(self, vlog):
        if vlog.overlay:
            txt = 'Version %s-%s [%s] of package %s ' % \
                (vlog.version, vlog.revision, vlog.slot, vlog.package)
        else:
            txt = 'Version %s of package %s ' % (vlog.version, vlog.package)
        if vlog.action == vlog.VERSION_REMOVED:
            if not vlog.overlay:
                txt += 'has been removed upstream'
            else:
                txt += 'has been removed from overlay "%s"' % vlog.overlay
        if vlog.action == vlog.VERSION_ADDED:
            if not vlog.overlay:
                txt += 'has been added upstream'
            else:
                txt += 'has been added to overlay "%s"' % vlog.overlay

        return txt

    def item_link(self, vlog):
        kwargs = {'category': vlog.package.category,
                  'package': vlog.package.name}
        return "%s#version-%s" % (
            reverse('djeuscan.views.package', kwargs=kwargs), vlog.tag
        )

    def item_pubdate(self, vlog):
        return vlog.datetime

    def item_categories(self, vlog):
        return [vlog.package.category]

    def items(self, data=None):
        user = data.get("user", None) if data else None
        options = data.get("options", {}) if data else {}

        # first of all consider options, then user preferences
        try:
            upstream_info = json.loads(options.get("upstream_info", "1"))
            portage_info = json.loads(options.get("portage_info", "1"))
            show_adds = json.loads(options.get("show_adds", "1"))
            show_removals = json.loads(options.get("show_removals", "1"))
            ignore_pre = json.loads(options.get("ignore_pre", "0"))
            ignore_pre_if_stable = json.loads(
                options.get("ignore_pre_if_stable", "0")
            )
        except ValueError:
            return []

        if user and not options:
            profile = get_profile(user)
            upstream_info = profile.feed_upstream_info
            portage_info = profile.feed_portage_info
            show_adds = profile.feed_show_adds
            show_removals = profile.feed_show_removals
            ignore_pre = profile.feed_ignore_pre
            ignore_pre_if_stable = profile.feed_ignore_pre_if_stable

        ret, max_items = self._items(data)

        if not upstream_info:
            ret = ret.exclude(overlay="")
        if not portage_info:
            ret = ret.exclude(~Q(overlay=""))
        if not show_adds:
            ret = ret.exclude(action=VersionLog.VERSION_ADDED)
        if not show_removals:
            ret = ret.exclude(action=VersionLog.VERSION_REMOVED)
        if ignore_pre:
            ret = ret.exclude(vtype__in=gentoo_unstable)
        if ignore_pre_if_stable:
            ret = ret.exclude(
                ~Q(package__last_version_gentoo__vtype__in=gentoo_unstable),
                vtype__in=gentoo_unstable
            )

        return ret.order_by("-datetime")[:max_items]


class GlobalFeed(BaseFeed):
    title = "euscan"
    link = "/"
    description = "Last euscan changes"

    def get_object(self, request):
        return {"options": request.GET}

    def categories(self, data):
        categories = Package.objects.categories()
        return [category['category'] for category in categories]

    def _items(self, data):
        return VersionLog.objects.all(), 250


class PackageFeed(BaseFeed):
    feed_type = Atom1Feed

    def get_object(self, request, category, package):
        return {
            "obj": get_object_or_404(Package, category=category, name=package),
            "options": request.GET,
        }

    def title(self, data):
        return "%s" % data["obj"]

    def link(self, data):
        return reverse('djeuscan.views.package', args=[data["obj"].category,
                       data["obj"].name])

    def description(self, data):
        return data["obj"].description

    def _items(self, data):
        return VersionLog.objects.for_package(data["obj"]), 30

    def item_description(self, vlog):
        return ''


class MaintainerFeed(BaseFeed):
    feed_type = Atom1Feed

    def get_object(self, request, maintainer_id=None, maintainer_email=None):
        if maintainer_id:
            obj = get_object_or_404(Maintainer, id=maintainer_id)
        else:
            obj = get_object_or_404(Maintainer, email=maintainer_email)

        return {
            "obj": obj,
            "options": request.GET,
        }

    def title(self, data):
        return "%s" % data["obj"]

    def description(self, data):
        return "Last changes for %s" % data["obj"]

    def link(self, data):
        return reverse('djeuscan.views.maintainer', args=[data["obj"].email])

    def _items(self, data):
        return VersionLog.objects.for_maintainer(data["obj"]), 50


class HerdFeed(BaseFeed):
    feed_type = Atom1Feed

    def get_object(self, request, herd):
        return {
            "obj": get_object_or_404(Herd, herd=herd),
            "options": request.GET,
        }

    def title(self, data):
        return "%s" % data["obj"]

    def description(self, data):
        return "Last changes for %s" % data["obj"]

    def link(self, data):
        return reverse('djeuscan.views.herd',
                       kwargs={'herd': data["obj"].herd})

    def _items(self, data):
        return VersionLog.objects.for_herd(data["obj"]), 100


class CategoryFeed(BaseFeed):
    feed_type = Atom1Feed

    def get_object(self, request, category):
        if not Package.objects.categories().count():
            raise FeedDoesNotExist
        return {
            "obj": category,
            "options": request.GET,
        }

    def title(self, data):
        return "%s" % data["obj"]

    def description(self, data):
        return "Last changes for %s" % data["obj"]

    def link(self, data):
        return reverse('djeuscan.views.category', args=[data["obj"]])

    def _items(self, data):
        return VersionLog.objects.for_category(data["obj"]), 100


class WorldScanFeed(BaseFeed):
    link = "/"

    def get_object(self, request):
        return {
            "options": request.GET,
        }

    def _items(self, data):
        packages = []
        for pkg_name in data["options"].getlist("p", []):
            if "/" in pkg_name:
                cat, name = pkg_name.split("/", 1)
                pkg = Package.objects.filter(name=name, category=cat)
            else:
                pkg = Package.objects.filter(name=pkg_name)
            packages.extend(pkg)

        vlogs = VersionLog.objects.filter(package__in=packages)

        return vlogs, 100
