import json
from django.core.mail import send_mail
import locale
import logging

from django.contrib.auth import login
from django.core.serializers.json import DjangoJSONEncoder
from django.core.urlresolvers import reverse
from django.conf import settings
from django.forms import model_to_dict
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext

from accounts.forms import (
    ProfileSecondaryAddressForm,
    ProfileUpdateForm,
    ProfileCreateForm,
)
from accounts.profile import get_profile, get_profile_secondary_address
from accounts import utils as account_utils

from cart.cart import add_to_cart, remove_subscriptions
from catalog import models as catalog_models
from catalog.models import Subscription
from catalog.utils import get_type_by_profile, get_type

from . import constants
from . import forms
from . import utils


LOG = logging.getLogger(__name__)
STOREFRONT_HELP_TO = getattr(settings, "STOREFRONT_HELP_TO")


def storefront_help(request):
    """ Store Help  """

    form = forms.HelpForm(request.POST or None)
    if request.method == "POST":
        if form.is_valid():
            message = "".join(
                [
                    "{0}: {1}\n".format(key, value)
                    for key, value in form.cleaned_data.iteritems()
                ]
            )
            LOG.info(
                "Help Form Contents (Sending To: {0}): {1}".format(
                    STOREFRONT_HELP_TO, message
                )
            )
            send_mail(
                "PBP Community Storefront Feedback",
                message,
                "subscriptions@pbp.me",
                [STOREFRONT_HELP_TO],
                fail_silently=True,
            )
            return HttpResponseRedirect(reverse("help_confirm"))

    return render_to_response(
        "storefront/help.html", {"form": form}, context_instance=RequestContext(request)
    )


def storefront_help_confirm(request):
    """ Store Help Confirmation Page"""
    return render_to_response(
        "storefront/help_confirm.html", context_instance=RequestContext(request)
    )


def storefront_privacy(request):
    """ Store Help  """
    return render_to_response(
        "storefront/privacy.html", context_instance=RequestContext(request)
    )


def storefront_index(request):
    """ Store Front Home page """
    return render_to_response(
        "storefront/index.html", context_instance=RequestContext(request)
    )


def category(request, category):
    """ Category page """
    # TODO: security, etc
    # TODO: sorting by price, or title.
    config = {
        "movie": catalog_models.Movie,
        "book": catalog_models.Book,
        "map": catalog_models.Map,
    }
    sort_type = {"p": "price", "-p": "-price", "t": "title", "-t": "-title"}
    sort = request.GET.get("sort", "t")
    sort_by = sort_type.get(sort)
    model = config.get(category)
    # sort by title or price, asc or desc default to title asc
    items = model.objects.active().order_by(sort_by)

    return render_to_response(
        "storefront/category.html",
        {"items": items, "category": category, "sort_by": sort_by},
        context_instance=RequestContext(request),
    )


def item_price(request):
    """ Ajax call to return the item price """

    def _currency(value):
        """ convert value to a nice looking currency """
        try:
            locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
        except Exception:
            locale.setlocale(locale.LC_ALL, "")
        loc = locale.localeconv()
        return locale.currency(value, loc["currency_symbol"], grouping=True)

    response_data = "$N/A"

    if request.method == "POST":
        try:
            LOG.debug("Post info: {0}".format(request.POST))

            # use the form data to determine the shipping_type
            region1 = request.POST.get("region1", None)
            postal_code1 = request.POST.get("postal_code1", None)
            country_code1 = request.POST.get("country_code1", None)
            region2 = request.POST.get("region2", None)
            postal_code2 = request.POST.get("postal_code2", None)
            country_code2 = request.POST.get("country_code2", None)
            shipping_type = get_type(
                postal_code1,
                region1,
                country_code1,
                postal_code2=postal_code2,
                region2=region2,
                country_code2=country_code2,
            )
            LOG.debug("Found shipping type as: {0}".format(shipping_type))

            # Recast the id values to searchable values
            title = request.POST.get("title", None)
            duration = request.POST.get("duration", None)
            shipping_method = request.POST.get("shipping_method", None)
            title = utils.get_value_by_index(utils.unique_titles(shipping_type), title)
            duration = utils.get_value_by_index(
                utils.unique_durations(shipping_type), duration
            )
            shipping_method = utils.get_value_by_index(
                utils.unique_shipping_methods(), shipping_method
            )
            LOG.debug(
                "{0}, {1}, {2}, {3}".format(
                    title, duration, shipping_method, shipping_type
                )
            )

            try:
                # Renewals are the same price as regular. This might
                # need to change if that changes
                subscription = Subscription.objects.get(
                    title=title,
                    duration=duration,
                    shipping_type=shipping_type,
                    shipping_method=shipping_method,
                    renewal=False,
                )
                response_data = _currency(subscription.price)
            except Subscription.DoesNotExist:
                pass
        except Exception, e:
            LOG.error(e)
            raise e

    return HttpResponse(
        json.dumps(response_data, cls=DjangoJSONEncoder),
        content_type="application/json",
    )


def item_detail(request, sku):
    item = get_object_or_404(catalog_models.Item, sku=sku).child
    return render_to_response(
        "storefront/item_detail.html",
        {"item": item},
        context_instance=RequestContext(request),
    )


def subscription_home(request):
    """ Subscription Home page """
    return render_to_response(
        "storefront/subscriptions_home.html", context_instance=RequestContext(request)
    )


def choose_new_subscription(request, template="storefront/subscriptions_new.html"):
    """ View used to manage a new subscription """

    def _render(request, form, user_form, second_address_form, shipping_type):
        return render_to_response(
            template,
            {
                "form": form,
                "userform": user_form,
                "second_address": second_address_form,
                "type": shipping_type,
            },
            context_instance=RequestContext(request),
        )

    # set some sane defaults
    user = request.user
    subscription_initial = {"duration": 1}
    primary_initial = {"region": "ME", "country": "US"}
    secondary_initial = {}
    shipping_type = get_type(None, "ME", "US")

    if user.is_authenticated():
        profile = get_profile(user)
        secondary_address = get_profile_secondary_address(user)
        shipping_type = get_type_by_profile(profile)
        primary_initial = model_to_dict(user)
        primary_initial.update(model_to_dict(profile))
        secondary_initial = model_to_dict(secondary_address)

        user_form = ProfileUpdateForm(
            user, request.POST or None, initial=primary_initial, prefix="form2"
        )
    else:
        user_form = ProfileCreateForm(
            request.POST or None, initial=primary_initial, prefix="form2"
        )

    form = forms.SubscriptionForm(
        request.POST or None,
        initial=subscription_initial,
        shipping_type=shipping_type,
        prefix="form1",
    )
    second_address_form = ProfileSecondaryAddressForm(
        request.POST or None, initial=secondary_initial, prefix="form3"
    )

    if (
        request.method == "POST"
        and user_form.is_valid()
        and second_address_form.is_valid()
    ):

        # we've called is_valid for both the profile and address forms
        shipping_type = get_type(
            user_form.cleaned_data.get("postal_code", None),
            user_form.cleaned_data.get("region", None),
            user_form.cleaned_data.get("country", None),
            postal_code2=second_address_form.cleaned_data.get("postal_code", None),
            region2=second_address_form.cleaned_data.get("region", None),
            country_code2=second_address_form.cleaned_data.get("country", None),
        )
        form = forms.SubscriptionForm(
            request.POST or None,
            shipping_type=shipping_type,
            initial=subscription_initial,
            prefix="form1",
        )

        if form.is_valid():
            user = user_form.save(request=request)
            second_address_form.save(user=user)
            subscription = form.save()

            remove_subscriptions(request)
            add_to_cart(request, subscription, quantity=1)

            return HttpResponseRedirect(reverse("checkout"))

    return _render(request, form, user_form, second_address_form, shipping_type)


def choose_gift_subscription(
    request, template="storefront/subscriptions_gift_subs.html"
):
    def render(subscription_form, gift_from_form, gift_to_form, gift_info_form):
        return render_to_response(
            template,
            {
                "form": subscription_form,
                "gift_from": gift_from_form,
                "gift_to": gift_to_form,
                "gift_info_form": gift_info_form,
            },
            context_instance=RequestContext(request),
        )

    # set some sane defaults
    shipping_type = get_type(None, "ME", "US")
    subscription_initial = {"duration": 1}
    gift_from_initial = {"region": "ME", "country": "US"}
    gift_to_initial = {"region": "ME", "country": "US"}
    user = request.user

    LOG.debug("Checking authentication status...")
    if user.is_authenticated():
        LOG.debug("User is authenticated...creating user specific forms.")
        gift_from_initial = model_to_dict(user)
        gift_from_initial.update(model_to_dict(user.get_profile()))
        shipping_type = get_type_by_profile(get_profile(user))
        gift_from_form = ProfileUpdateForm(
            user, request.POST or None, initial=gift_from_initial, prefix="form5"
        )
    else:
        LOG.debug("User is not authenticated...creating user creation forms.")
        gift_from_form = ProfileCreateForm(
            request.POST or None, initial=gift_from_initial, prefix="form5"
        )

    subscription_form = forms.SubscriptionForm(
        request.POST or None,
        shipping_type=shipping_type,
        initial=subscription_initial,
        prefix="form1",
    )
    gift_to_form = forms.GiftToForm(
        request.POST or None, initial=gift_to_initial, prefix="form2"
    )
    gift_info_form = forms.GiftInformationForm(request.POST or None, prefix="form4")

    if request.method == "POST" and gift_from_form.is_valid():

        LOG.debug("Processing POST data...")
        # we've called is_valid on the gift from form and since we are
        # here..it is. We intentionally have not called saved so that a user
        # isn't created yet as this would cause confusion if the other forms
        # do not validate. We need the data from the cleaned form to pass to
        # the Subscription form which will determine if the subscription type
        # is valid.
        shipping_type = get_type(
            gift_from_form.cleaned_data.get("postal_code", None),
            gift_from_form.cleaned_data.get("region", None),
            gift_from_form.cleaned_data.get("country", None),
        )
        subscription_form = forms.SubscriptionForm(
            request.POST or None,
            shipping_type=shipping_type,
            initial=subscription_initial,
            prefix="form1",
        )

        LOG.debug("Checking validity of forms....")
        if (
            gift_to_form.is_valid()
            and subscription_form.is_valid()
            and gift_info_form.is_valid()
            and gift_from_form.is_valid()
        ):

            LOG.debug("Forms are valid...processing.")
            gift_from_form.save(request=request)
            subscription = subscription_form.save()
            gift_to_form.save()
            gift_detail = gift_to_form.cleaned_data or {}
            gift_detail.update(gift_info_form.cleaned_data)

            remove_subscriptions(request)
            add_to_cart(request, subscription, gift_detail=gift_detail, quantity=1)

            return HttpResponseRedirect(reverse("checkout"))

        else:
            LOG.debug("Forms are not valid.")
            LOG.debug("subscription_form: {0}".format(subscription_form))
            LOG.debug("gift_from_form: {0}".format(gift_from_form))
            LOG.debug("gift_to_form: {0}".format(gift_to_form))
            LOG.debug("gift_info_form: {0}".format(gift_info_form))

    return render(subscription_form, gift_from_form, gift_to_form, gift_info_form)


def change_of_address(request, template="storefront/subscriptions_change_address.html"):
    """ Submits a change of address form.  If the user is not logged in, it
    also creates a new user account.
    """

    # set some sane defaults
    user = request.user
    primary_initial = {"region": "ME", "country": "US"}
    old_address_initial = {"region": "ME", "country": "US"}
    form = forms.ChangeOfAddressForm(
        request.POST or None, initial={"date_effective": constants.NEXT_ISSUE}
    )

    if user.is_authenticated():
        profile = get_profile(user)
        old_address_initial = model_to_dict(user)
        old_address_initial.update(model_to_dict(profile))

        old_address = forms.AddressForm(
            request.POST or None, initial=old_address_initial, prefix="form5"
        )
        new_address = ProfileUpdateForm(
            user, request.POST or None, initial=primary_initial, prefix="form2"
        )
    else:
        old_address = forms.AddressForm(
            request.POST or None, initial=old_address_initial, prefix="form5"
        )
        new_address = ProfileCreateForm(
            request.POST or None, initial=primary_initial, prefix="form2"
        )

    if request.method == "POST":
        if form.is_valid() and old_address.is_valid() and new_address.is_valid():

            new_address.save(request=request)

            # recast the title index from the form to the
            #  actual title
            raw_title = form.cleaned_data.get("title", None)
            title = utils.get_value_by_index(utils.unique_titles(), raw_title)

            old_address_initial.update(old_address.cleaned_data)
            account_utils.send_change_of_address_email(
                title,
                form.cleaned_data.get("account_number", None),
                form.cleaned_data.get("date_effective", None),
                form.cleaned_data.get("from_date", None),
                form.cleaned_data.get("to_date", None),
                old_address_initial,
                new_address.cleaned_data,
            )

            return HttpResponseRedirect(
                reverse("subscriptions_address_change_complete")
            )

    return render_to_response(
        template,
        {"form": form, "old_address": old_address, "new_address": new_address},
        context_instance=RequestContext(request),
    )


def change_of_address_complete(
    request, template="storefront/subscriptions_change_address_complete.html"
):

    user = request.user
    new_address_initial = model_to_dict(user)
    new_address_initial.update(model_to_dict(user.get_profile()))
    new_address = forms.AddressForm(initial=new_address_initial)

    return render_to_response(
        template, {"new_address": new_address}, context_instance=RequestContext(request)
    )


def subscriptions_renew(request, template="storefront/subscriptions_renewals.html"):
    def _render(request, form, userform, second_address_form, shipping_type):
        return render_to_response(
            template,
            {
                "form": form,
                "userform": userform,
                "second_address": second_address_form,
                "type": shipping_type,
            },
            context_instance=RequestContext(request),
        )

    # set some sane defaults
    user = request.user
    subscription_initial = {"duration": 1}
    primary_initial = {"region": "ME", "country": "US"}
    secondary_initial = {}
    shipping_type = get_type(None, "ME", "US")

    if user.is_authenticated():
        profile = get_profile(user)
        secondary_address = get_profile_secondary_address(user)
        shipping_type = get_type_by_profile(profile)
        primary_initial = model_to_dict(user)
        primary_initial.update(model_to_dict(profile))
        secondary_initial = model_to_dict(secondary_address)

        userform = ProfileUpdateForm(
            user, request.POST or None, initial=primary_initial, prefix="form2"
        )
    else:
        userform = ProfileCreateForm(
            request.POST or None, initial=primary_initial, prefix="form2"
        )

    subscription_form = forms.SubscriptionForm(
        request.POST or None,
        initial=subscription_initial,
        shipping_type=shipping_type,
        prefix="form1",
        use_account_number=True,
        renewal=True,
    )
    second_address_form = ProfileSecondaryAddressForm(
        request.POST or None, initial=secondary_initial, prefix="form3"
    )

    if (
        request.method == "POST"
        and userform.is_valid()
        and second_address_form.is_valid()
    ):

        # we've called is_valid for both the profile and address forms
        shipping_type = get_type(
            userform.cleaned_data.get("postal_code", None),
            userform.cleaned_data.get("region", None),
            userform.cleaned_data.get("country", None),
            postal_code2=second_address_form.cleaned_data.get("postal_code", None),
            region2=second_address_form.cleaned_data.get("region", None),
            country_code2=second_address_form.cleaned_data.get("country", None),
        )
        subscription_form = forms.SubscriptionForm(
            request.POST or None,
            shipping_type=shipping_type,
            initial=subscription_initial,
            prefix="form1",
            use_account_number=True,
            renewal=True,
        )

        if subscription_form.is_valid():
            user = userform.save(request=request)
            if not user.is_authenticated():
                login(request, user)
            second_address_form.save(user=user)
            subscription = subscription_form.save()

            account_number = subscription_form.cleaned_data["account_number"]
            remove_subscriptions(request)
            add_to_cart(
                request, subscription, account_number=account_number, quantity=1
            )
            return HttpResponseRedirect(reverse("checkout"))

    return _render(
        request, subscription_form, userform, second_address_form, shipping_type
    )
