import datetime

from django.conf import settings
import django.contrib.comments.views.comments as django_comments
from django.contrib.comments import signals
from django.contrib.comments.models import Comment
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.contrib import messages
from django.http import Http404
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.utils.translation import ugettext as _
from django.views.decorators.http import require_POST
from django.db.models import Count

from blog.models import Entry
from blog.enums import DRAFT_STATUS, HIDDEN_STATUS, LIVE_STATUS, MONTH_NAMES
from blog.forms import CommentForm

from templatable_view import templatable_view
from django_ajax.pagination import paginate


def _get_entries(limit=None, year=None, month=None):
    """
    Returns a filtered list of blog entries.

    :param limit: the maximum number to return
    :type limit: integer or None
    :param year: the year to filter on
    :type year: integer or None
    :param month: the month to filter on
    :type month: integer or None

    :rtype: django.db.models.QuerySet
    """
    object_list = Entry.objects.live()

    if year:
        object_list = object_list.filter(pub_date__year=year)

    if month:
        object_list = object_list.filter(pub_date__month=month)

    if limit:
        object_list = object_list[:limit]

    ids = list(object_list.values_list('id', flat=True))
    comments = Comment.objects.filter(content_type=ContentType.objects.get_for_model(Entry), object_pk__in=ids). \
        values('object_pk'). \
        annotate(num_comments=Count('object_pk'))
    # translate to dict for faster look-ups
    comments = dict([(c['object_pk'], c['num_comments']) for c in comments])
    # augment object_list
    for obj in object_list:
        obj.comment_count = comments.get(u'%d' % obj.id, 0)

    return object_list

@templatable_view('blog/entry_list.html')
def recent_entries(request):
    """
    A list of the last five entries.
    """

    object_list = _get_entries(limit=5)

    return {
        'object_list': object_list,
    }


@templatable_view('blog/entry_archive.html')
def archive(request):
    """
    A list of all entries.
    """
    object_list = _get_entries()

    context = {
        'object_list': object_list,
    }
    context.update(get_blog_context())

    return context


@templatable_view('blog/entry_detail.html')
def entry(request, slug, archive=False):
    """
    The detail view of an entry, with comments.
    """
    # News entry
    object = get_object_or_404(Entry, slug=slug, site=Site.objects.get_current())
    if object.status == DRAFT_STATUS and not request.user.is_staff or object.status == HIDDEN_STATUS:
        raise Http404

    # Comment form
    context = get_form_context(request, object)
    context.update(get_blog_context())

    # Paginated comments
    comments = Comment.objects.for_model(object).filter(is_removed=False, is_public=True)
    comments = paginate(request, comments, default_to_last_page=True)

    # Context
    context.update({'object':object, 'archive':archive, 'comments': comments })
    return context


@templatable_view('blog/entry_detail_comments.html')
def comment_detail(request, slug):
    """
    The detail view of a comment.
    """
    object = get_object_or_404(Entry, slug=slug)

    # Paginated comments
    comments = Comment.objects.for_model(object).filter(is_removed=False, is_public=True)
    comments = paginate(request, comments, default_to_last_page=True)

    return  { 'object': object, 'comments': comments }

@templatable_view('blog/entry_archive.html')
def archive_year(request, year):
    """
    A list of all entries in a given year.
    """
    object_list = _get_entries(year=year)

    context = {
        'year': year,
        'object_list': object_list,
    }
    context.update(get_blog_context())

    return context

@templatable_view('blog/entry_archive.html')
def archive_year_month(request, year, month):
    """
    A list of all entries in a given month.
    """
    object_list = _get_entries(year=year, month=month)

    context = {
        'year': year,
        'month': month,
        'object_list': object_list,
    }
    context.update(get_blog_context())

    return context

def get_blog_context():
    """
    Updates a context with archive counts and five latest comments.
    """
    context = {}

    # counts
    context.update({'archive_counts':get_archive_counts(Entry.objects.live())})

    # latest comments
    context.update({'latest_comments':Comment.objects.for_model(Entry).filter(is_removed=False, is_public=True).order_by('-submit_date')[:5]})

    return context

def get_archive_counts(entries):
    """
    Aggregates a list of entries into counts per month and year.
    """
    archive_data = []
    count = {}
    month_count = {}

    for entry in entries:
        year = entry.pub_date.year
        month = entry.pub_date.month

        if year not in count:
            count[year] = 1
            month_count[year] = {}
        else:
            count[year] += 1

        if month not in month_count[year]:
            month_count[year][month] = 1
        else:
            month_count[year][month] += 1

    for year in sorted(count.iterkeys(), reverse=True):
        archive_data.append({
            'is_year': True,
            'year': year,
            'count': count[year]
        })

        for month in sorted(month_count[year].iterkeys(), reverse=True):
            archive_data.append({
                'is_year': False,
                'year': year,
                'month': month,
                'month_name': MONTH_NAMES[month],
                'count': month_count[year][month]
            })
    return archive_data

###
### Below is ported from /python/site-packages/django/contrib/comments
###

def get_form_context(request, entry):
    """
    Post a comment.
    """
    # Fill out some initial data fields from an authenticated user, if present
    initial =  { }
    if request.user.is_authenticated():
            initial["name"] = request.user.get_full_name()
            initial["email"] = request.user.email

    # If there are errors or if we requested a preview show the comment
    if request.method == 'POST':
        form = CommentForm(entry, request.POST, initial=initial)
        if form.is_valid():
            # Create the comment
            comment = form.get_comment()

            comment.ip_address = request.META.get("REMOTE_ADDR", None)
            if request.user.is_authenticated():
                comment.user = request.user

            # Signal that the comment is about to be saved
            responses = signals.comment_will_be_posted.send(
                sender  = comment.__class__,
                comment = comment,
                request = request
            )

            for (receiver, response) in responses:
                if response == False:
                    return django_comments.CommentPostBadRequest(
                        "comment_will_be_posted receiver %r killed the comment" % receiver.__name__)

            comment.save()

            # Save the comment and signal that it was saved
            signals.comment_was_posted.send(
                sender  = comment.__class__,
                comment = comment,
                request = request
            )

            # Feedback --> check in the template for 'commented'. Users can comment
            # without having to log in so request.user.message_set can't be used
            return {
                        'commented' : True,

                        # Return a fresh form
                        'form': CommentForm(entry, initial=initial)
                    }
        else:
            if request.user.is_authenticated():
                messages.error(request, _(u"Your comment could not be posted. Please correct the errors below."))
    else:
        form = CommentForm(entry, initial=initial)
    return { 'commented': False, 'form': form }
