"""Custom middleware used by the pages application."""
import os

import sys

from django.conf import settings
from django.core import urlresolvers
from django.core.handlers.base import BaseHandler
from django.http import Http404
from django.views.debug import technical_404_response
from django.shortcuts import redirect
from django.utils.functional import cached_property
from django.template.response import SimpleTemplateResponse

from cms.apps.pages.models import Page, Country

from django.contrib.gis.geoip import GeoIP


def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip


class RequestPageManager(object):

    """Handles loading page objects."""

    def __init__(self, request):
        """Initializes the RequestPageManager."""
        self._request = request
        self._path = self._request.path
        self._path_info = self._request.path_info

    def request_geo(self):
        cookie_code = self._request.COOKIES.get('country_code', None)
        if cookie_code:
            return {
                "country_code": cookie_code
            }

        # Country data from geoip
        g = GeoIP()
        return {
            "country_code": g.country(get_client_ip(self._request)).country_code
        }

    def request_country(self):

        country_data = self.request_geo()

        # No code, return None
        if country_data['country_code'] is None:
            return None

        # Get country matching code
        try:
            country = Country.objects.get(code=country_data['country_code'])
            if not country:
                return None
            else:
                return country
        except:
            return None

    @cached_property
    def country(self):
        return dict(
            actual=self.request_geo(),
            cms=self.request_country()
        )

    def request_country_group(self):
        country = self.request_country()

        if country and country.group:
            return country.group

        return None

    def alternate_page_version(self, page):

        try:
            # See if the page has any alternate versions for the current country
            alternate_version = Page.objects.get(is_content_object=True,
                                                 owner=page,
                                                 country_group=self.request_country_group())

            return alternate_version

        except:
            return page

    @cached_property
    def homepage(self):
        """Returns the site homepage."""
        try:
            return Page.objects.get_homepage()
        except Page.DoesNotExist:
            return None

    @property
    def is_homepage(self):
        """Whether the current request is for the site homepage."""
        return self._path == self.homepage.get_absolute_url()

    @cached_property
    def breadcrumbs(self):
        """The breadcrumbs for the current request."""
        breadcrumbs = []
        slugs = self._path_info.strip("/").split("/")
        slugs.reverse()

        def do_breadcrumbs(page):
            breadcrumbs.append(page)
            if slugs:
                slug = slugs.pop()
                for child in page.children:
                    if child.url_title == slug:
                        do_breadcrumbs(child)
                        break
        if self.homepage:
            do_breadcrumbs(self.homepage)
        return breadcrumbs

    @property
    def section(self):
        """The current primary level section, or None."""
        try:
            page = self.breadcrumbs[1]
            return self.alternate_page_version(page)
        except IndexError:
            return None

    @property
    def subsection(self):
        """The current secondary level section, or None."""
        try:
            page = self.breadcrumbs[2]
            return self.alternate_page_version(page)
        except IndexError:
            return None

    @property
    def current(self):
        """The current best-matched page."""
        try:
            page = self.breadcrumbs[-1]
            return self.alternate_page_version(page)
        except IndexError:
            return None

    @property
    def is_exact(self):
        """Whether the current page exactly matches the request URL."""
        return self.current.get_absolute_url() == self._path


class PageMiddleware(object):

    """Serves up pages when no other view is matched."""

    def process_request(self, request):
        """Annotates the request with a page manager."""
        request.pages = RequestPageManager(request)

    def process_response(self, request, response):
        """If the response was a 404, attempt to serve up a page."""
        if response.status_code != 404:
            return response
        # Get the current page.
        page = request.pages.current
        if page is None:
            return response
        script_name = page.get_absolute_url()[:-1]
        path_info = request.path[len(script_name):]
        # Dispatch to the content.
        try:
            try:
                callback, callback_args, callback_kwargs = urlresolvers.resolve(path_info, page.content.urlconf)
            except urlresolvers.Resolver404:
                # First of all see if adding a slash will help matters.
                if settings.APPEND_SLASH:
                    new_path_info = path_info + "/"
                    try:
                        urlresolvers.resolve(new_path_info, page.content.urlconf)
                    except urlresolvers.Resolver404:
                        pass
                    else:
                        return redirect(script_name + new_path_info)
                return response
            response = callback(request, *callback_args, **callback_kwargs)
            # Validate the response.
            if not response:
                raise ValueError("The view {0!r} didn't return an HttpResponse object.".format(
                    callback.__name__
                ))
            if isinstance(response, SimpleTemplateResponse):
                return response.render()
            return response
        except Http404 as ex:
            if settings.DEBUG:
                return technical_404_response(request, ex)
            # Let the normal 404 mechanisms render an error page.
            return response
        except:
            return BaseHandler().handle_uncaught_exception(request, urlresolvers.get_resolver(None), sys.exc_info())
