from django.conf import settings
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse_lazy, reverse
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.translation import get_language
from djangocms_blog.models import Post
from django.contrib import messages
from django.views.generic import DetailView, ListView
from .models import Supporter
from .mixins import ChangeMembershipStatusMixin
from utils.forms import ContactUsForm
from utils.mailer import BaseEmail
from django.views.generic.edit import FormView
from membership.models import StripeCustomer
from utils.views import LoginViewMixin, SignupViewMixin, \
    PasswordResetViewMixin, PasswordResetConfirmViewMixin
from utils.forms import PasswordResetRequestForm, UserBillingAddressForm, EditCreditCardForm
from utils.stripe_utils import StripeUtils
from utils.models import UserBillingAddress

from .forms import LoginForm, SignupForm, MembershipBillingForm, BookingDateForm,\
    BookingBillingForm, CancelBookingForm

from .models import MembershipType, Membership, MembershipOrder, Booking, BookingPrice,\
    BookingOrder, BookingCancellation

from .mixins import MembershipRequiredMixin, IsNotMemberMixin


class IndexView(TemplateView):
    template_name = "digitalglarus/index.html"


class SupportusView(TemplateView):
    template_name = "digitalglarus/supportus.html"

    def get_context_data(self, *args, **kwargs):
        context = super(SupportusView, self).get_context_data(**kwargs)
        tags = ["dg-renovation"]
        posts = Post.objects.filter(tags__name__in=tags, publish=True).translated(get_language())
        context.update({
            'post_list': posts
        })
        return context


class LoginView(LoginViewMixin):
    template_name = "digitalglarus/login.html"
    form_class = LoginForm

    def get_success_url(self):
        # redirect to membership orders list if user has at least one.
        if self.request.user \
           and MembershipOrder.objects.filter(customer__user=self.request.user):

            return reverse_lazy('digitalglarus:membership_orders_list')

        return reverse_lazy('digitalglarus:membership_pricing')


class SignupView(SignupViewMixin):
    template_name = "digitalglarus/signup.html"
    form_class = SignupForm
    success_url = reverse_lazy('digitalglarus:login')


class PasswordResetView(PasswordResetViewMixin):
    template_name = 'digitalglarus/reset_password.html'
    success_url = reverse_lazy('digitalglarus:login')
    form_class = PasswordResetRequestForm
    template_email_path = 'digitalglarus/emails/'


class PasswordResetConfirmView(PasswordResetConfirmViewMixin):
    template_name = 'digitalglarus/confirm_reset_password.html'
    success_url = reverse_lazy('digitalglarus:login')


class HistoryView(TemplateView):
    template_name = "digitalglarus/history.html"

    def get_context_data(self, *args, **kwargs):
        context = super(HistoryView, self).get_context_data(**kwargs)
        supporters = Supporter.objects.all()
        context.update({
            'supporters': supporters
        })
        return context


class EditCreditCardView(FormView):
    template_name = "digitalglarus/edit_credit_card.html"
    form_class = EditCreditCardForm
    success_url = reverse_lazy('digitalglarus:booking_payment')

    def get_context_data(self, *args, **kwargs):
        context = super(EditCreditCardView, self).get_context_data(*args, **kwargs)
        context.update({
            'stripe_key': settings.STRIPE_API_PUBLIC_KEY
        })
        return context

    def form_valid(self, form):
        token = form.cleaned_data.get('token')
        user = self.request.user
        customer = user.stripecustomer
        stripe_utls = StripeUtils()
        card_response = stripe_utls.update_customer_card(customer.stripe_id, token)
        new_card_data = card_response.get('response_object')
        self.request.session.update({
            'new_change_credit_card': new_card_data
        })

        return super(EditCreditCardView, self).form_valid(form)


class BookingSelectDatesView(LoginRequiredMixin, MembershipRequiredMixin, FormView):
    template_name = "digitalglarus/booking.html"
    form_class = BookingDateForm
    membership_redirect_url = reverse_lazy('digitalglarus:membership_pricing')
    login_url = reverse_lazy('digitalglarus:login')
    success_url = reverse_lazy('digitalglarus:booking_payment')

    def get_form_kwargs(self):
        kwargs = super(BookingSelectDatesView, self).get_form_kwargs()
        kwargs.update({'user': self.request.user})
        return kwargs

    def form_valid(self, form):
        user = self.request.user
        start_date = form.cleaned_data.get('start_date')
        end_date = form.cleaned_data.get('end_date')
        booking_days = (end_date - start_date).days + 1

        price_per_day = BookingPrice.objects.get().price_per_day

        original_price, final_price, free_days = Booking.\
            booking_price(user, start_date, end_date)

        total_discount = price_per_day * free_days

        self.request.session.update({
            'original_price': original_price,
            'final_price': final_price,
            'total_discount': total_discount,
            'booking_price_per_day': price_per_day,
            'booking_days': booking_days,
            'free_days': free_days,
            'start_date': start_date.strftime('%m/%d/%Y'),
            'end_date': end_date.strftime('%m/%d/%Y'),
            'is_free': final_price == 0
        })
        return super(BookingSelectDatesView, self).form_valid(form)


class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView):
    template_name = "digitalglarus/booking_payment.html"
    form_class = BookingBillingForm
    membership_redirect_url = reverse_lazy('digitalglarus:membership_pricing')
    # success_url = reverse_lazy('digitalglarus:booking_payment')
    booking_needed_fields = ['original_price', 'final_price', 'booking_days', 'free_days',
                             'start_date', 'end_date', 'booking_price_per_day',
                             'total_discount', 'is_free']

    def dispatch(self, request, *args, **kwargs):
        from_booking = all(field in request.session.keys()
                           for field in self.booking_needed_fields)
        if not from_booking:
            return HttpResponseRedirect(reverse('digitalglarus:booking'))

        return super(BookingPaymentView, self).dispatch(request, *args, **kwargs)

    def get_success_url(self, order_id):
        return reverse('digitalglarus:booking_orders_detail', kwargs={'pk': order_id})

    def get_form_kwargs(self):
        current_billing_address = self.request.user.billing_addresses.first()
        form_kwargs = super(BookingPaymentView, self).get_form_kwargs()
        form_kwargs.update({
            'initial': {
                'start_date': self.request.session.get('start_date'),
                'end_date': self.request.session.get('end_date'),
                'price': self.request.session.get('final_price'),
                'street_address': current_billing_address.street_address,
                'city': current_billing_address.city,
                'postal_code': current_billing_address.postal_code,
                'country': current_billing_address.country,
            }
        })
        return form_kwargs

    def get_context_data(self, *args, **kwargs):
        context = super(BookingPaymentView, self).get_context_data(*args, **kwargs)

        booking_data = {key: self.request.session.get(key)
                        for key in self.booking_needed_fields}
        user = self.request.user
        last_booking_order = BookingOrder.objects.filter(customer__user=user).last()
        last_membership_order = MembershipOrder.objects.filter(customer__user=user).last()

        # check if user changes his credit card
        credit_card_data = self.request.session.get('new_change_credit_card')
        # import pdb
        # pdb.set_trace()

        if not credit_card_data:
            credit_card_data = last_booking_order.get_booking_cc_data() if last_booking_order \
                and last_booking_order.get_booking_cc_data() \
                else last_membership_order.get_membership_order_cc_data()

        booking_data.update({
            'credit_card_data': credit_card_data if credit_card_data else None,
            'stripe_key': settings.STRIPE_API_PUBLIC_KEY
        })
        context.update(booking_data)
        return context

    def form_valid(self, form):
        data = form.cleaned_data
        context = self.get_context_data()
        token = data.get('token')
        start_date = data.get('start_date')
        end_date = data.get('end_date')
        is_free = context.get('is_free')
        normal_price, final_price, free_days = Booking.\
            booking_price(self.request.user, start_date, end_date)
        charge = None

        # if not credit_card_needed:
        # Get or create stripe customer
        customer = StripeCustomer.get_or_create(email=self.request.user.email,
                                                token=token)
        if not customer:
            form.add_error("__all__", "Invalid credit card")
            return self.render_to_response(self.get_context_data(form=form))

        # If booking is not free, make the stripe charge
        if not is_free:
            # Make stripe charge to a customer
            stripe_utils = StripeUtils()
            charge_response = stripe_utils.make_charge(amount=final_price,
                                                       customer=customer.stripe_id)
            charge = charge_response.get('response_object')

            # Check if the payment was approved
            if not charge:
                context.update({
                    'paymentError': charge_response.get('error'),
                    'form': form
                })
                return render(self.request, self.template_name, context)

            charge = charge_response.get('response_object')

        # Create Billing Address for Membership Order
        billing_address = form.save()

        # Create Billing Address for User if he does not have one
        if not customer.user.billing_addresses.count():
            data.update({
                'user': customer.user.id
            })
            billing_address_user_form = UserBillingAddressForm(data)
            billing_address_user_form.is_valid()
            billing_address_user_form.save()

        # Create Booking
        booking_data = {
            'start_date': start_date,
            'end_date': end_date,
            'start_date': start_date,
            'free_days': free_days,
            'price': normal_price,
            'final_price': final_price,
        }
        booking = Booking.create(booking_data)

        # Create Booking order
        order_data = {
            'booking': booking,
            'customer': customer,
            'billing_address': billing_address,
            'stripe_charge': charge,
            'amount': final_price,
            'original_price': normal_price,
            'special_month_price': BookingPrice.objects.last().special_month_price,
        }
        order = BookingOrder.create(order_data)

        context = {
            'booking': booking,
            'order': order,
            'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host())
        }

        email_data = {
            'subject': 'Your booking order has been placed',
            'to': self.request.user.email,
            'context': context,
            'template_name': 'booking_order_email',
            'template_path': 'digitalglarus/emails/'
        }
        email = BaseEmail(**email_data)
        email.send()

        return HttpResponseRedirect(self.get_success_url(order.id))


class MembershipPricingView(TemplateView):
    template_name = "digitalglarus/membership_pricing.html"

    def get_context_data(self, **kwargs):
        context = super(MembershipPricingView, self).get_context_data(**kwargs)
        membership_type = MembershipType.objects.last()
        context.update({
            'membership_type': membership_type
        })
        return context


class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView):
    template_name = "digitalglarus/membership_payment.html"
    login_url = reverse_lazy('digitalglarus:signup')
    form_class = MembershipBillingForm
    already_member_redirect_url = reverse_lazy('digitalglarus:membership_orders_list')

    def get_form_kwargs(self):
        self.membership_type = MembershipType.objects.get(name='standard')
        form_kwargs = super(MembershipPaymentView, self).get_form_kwargs()
        form_kwargs.update({
            'initial': {
                'membership_type': self.membership_type.id
            }
        })
        return form_kwargs

    def get_context_data(self, **kwargs):
        context = super(MembershipPaymentView, self).get_context_data(**kwargs)
        context.update({
            'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
            'membership_type': self.membership_type
        })
        return context

    def post(self, request, *args, **kwargs):
        form = self.get_form()

        if form.is_valid():
            data = form.cleaned_data
            context = self.get_context_data()
            token = data.get('token')
            membership_type = data.get('membership_type')

            # Get or create stripe customer
            customer = StripeCustomer.get_or_create(email=self.request.user.email,
                                                    token=token)
            if not customer:
                form.add_error("__all__", "Invalid credit card")
                return self.render_to_response(self.get_context_data(form=form))

            # Make stripe charge to a customer
            stripe_utils = StripeUtils()
            charge_response = stripe_utils.make_charge(amount=membership_type.first_month_price,
                                                       customer=customer.stripe_id)
            charge = charge_response.get('response_object')

            # Check if the payment was approved
            if not charge:
                context.update({
                    'paymentError': charge_response.get('error'),
                    'form': form
                })
                return render(request, self.template_name, context)

            charge = charge_response.get('response_object')

            # Create Billing Address
            billing_address = form.save()

            # Create Billing Address for User if he does not have one
            if not customer.user.billing_addresses.count():
                data.update({
                    'user': customer.user.id
                })
                billing_address_user_form = UserBillingAddressForm(data)
                billing_address_user_form.is_valid()
                billing_address_user_form.save()

            # Get membership dates
            membership_start_date, membership_end_date = membership_type.first_month_range

            # Create or update membership plan
            membership_data = {
                'type': membership_type,
                'active': True,
                'start_date': membership_start_date,
                'end_date': membership_end_date
            }
            membership = Membership.activate_or_crete(membership_data, self.request.user)

            # Create membership order
            order_data = {
                'membership': membership,
                'customer': customer,
                'billing_address': billing_address,
                'stripe_charge': charge,
                'amount': membership_type.first_month_price,
                'start_date': membership_start_date,
                'end_date': membership_end_date
            }

            membership_order = MembershipOrder.create(order_data)

            request.session.update({
                'membership_price': membership.type.first_month_price,
                'membership_dates': membership.type.first_month_formated_range
            })

            context = {
                'membership': membership,
                'order': membership_order,
                'membership_start_date': membership_start_date,
                'membership_end_date': membership_end_date,
                'base_url': "{0}://{1}".format(request.scheme, request.get_host())

            }
            email_data = {
                'subject': 'Your membership has been charged',
                'to': request.user.email,
                'context': context,
                'template_name': 'membership_charge',
                'template_path': 'digitalglarus/emails/'
            }
            email = BaseEmail(**email_data)
            email.send()

            return HttpResponseRedirect(reverse('digitalglarus:membership_activated'))

        else:
            return self.form_invalid(form)


class MembershipActivatedView(TemplateView):
    template_name = "digitalglarus/membership_activated.html"

    def get_context_data(self, **kwargs):
        context = super(MembershipActivatedView, self).get_context_data(**kwargs)
        membership_price = self.request.session.get('membership_price')
        membership_dates = self.request.session.get('membership_dates')
        context.update({
            'membership_price': membership_price,
            'membership_dates': membership_dates,
        })
        return context


class MembershipDeactivateView(LoginRequiredMixin, UpdateView):
    template_name = "digitalglarus/membership_deactivated.html"
    model = Membership
    success_message = "Your membership has been deactivated :("
    success_url = reverse_lazy('digitalglarus:membership_orders_list')
    login_url = reverse_lazy('digitalglarus:login')
    fields = '__all__'

    def get_object(self):
        membership_order = MembershipOrder.objects.\
            filter(customer__user=self.request.user).last()
        if not membership_order:
            raise AttributeError("Membership does not exists")
        membership = membership_order.membership
        return membership

    def post(self, *args, **kwargs):
        membership = self.get_object()
        membership.deactivate()

        messages.add_message(self.request, messages.SUCCESS, self.success_message)

        return HttpResponseRedirect(self.success_url)


class MembershipReactivateView(ChangeMembershipStatusMixin):
    success_message = "Your membership has been reactivate :)"
    template_name = "digitalglarus/membership_orders_list.html"

    def post(self, request, *args, **kwargs):
        membership = self.get_object()
        membership.activate()
        return super(MembershipReactivateView, self).post(request, *args, **kwargs)


class UserBillingAddressView(LoginRequiredMixin, UpdateView):
    model = UserBillingAddress
    form_class = UserBillingAddressForm
    template_name = "digitalglarus/user_billing_address.html"
    success_url = reverse_lazy('digitalglarus:user_billing_address')
    success_message = "Billing Address Updated"

    def get_success_url(self):
        next_url = self.request.POST.get('next') if self.request.POST.get('next')\
            else self.success_url

        return next_url

    def get_context_data(self, **kwargs):
        context = super(UserBillingAddressView, self).get_context_data(**kwargs)
        current_billing_address = self.request.user.billing_addresses.all().first()
        context.update({
            'current_billing_address': current_billing_address
        })
        return context

    def form_valid(self, form):
        """
        If the form is valid, save the associated model.
        """
        messages.add_message(self.request, messages.SUCCESS, self.success_message)
        self.object = form.save()
        return super(UserBillingAddressView, self).form_valid(form)

    def get_form_kwargs(self):
        current_billing_address = self.request.user.billing_addresses.first()
        form_kwargs = super(UserBillingAddressView, self).get_form_kwargs()

        if not current_billing_address:
            return form_kwargs

        form_kwargs.update({
            'initial': {
                'street_address': current_billing_address.street_address,
                'city': current_billing_address.city,
                'postal_code': current_billing_address.postal_code,
                'country': current_billing_address.country,
            }
        })
        return form_kwargs

    def get_object(self):
        current_billing_address = self.request.user.\
            billing_addresses.filter(current=True).last()
        # if not current_billing_address:
        #     raise AttributeError("Billing Address does not exists")
        return current_billing_address


class MembershipDeactivateSuccessView(LoginRequiredMixin, TemplateView):
    template_name = "digitalglarus/membership_deactivated_success.html"


class MembershipOrdersListView(LoginRequiredMixin, ListView):
    template_name = "digitalglarus/membership_orders_list.html"
    context_object_name = "orders"
    login_url = reverse_lazy('digitalglarus:login')
    model = MembershipOrder
    paginate_by = 10

    def get_context_data(self, **kwargs):
        context = super(MembershipOrdersListView, self).get_context_data(**kwargs)
        current_membership = Membership.get_current_membership(self.request.user)
        start_date, end_date = (current_membership.start_date, current_membership.end_date)\
            if current_membership else [None, None]

        next_start_date, next_end_date = MembershipOrder.next_membership_dates(self.request.user)
        current_billing_address = self.request.user.billing_addresses.filter(current=True).last()
        context.update({
            'membership_start_date': start_date,
            'membership_end_date': end_date,
            'current_membership': current_membership,
            'next_membership_start_date': next_start_date,
            'next_membership_end_date': next_end_date,
            'billing_address': current_billing_address
        })
        return context

    def get_queryset(self):
        queryset = super(MembershipOrdersListView, self).get_queryset()
        queryset = queryset.filter(customer__user=self.request.user)
        return queryset


class OrdersMembershipDetailView(LoginRequiredMixin, DetailView):
    template_name = "digitalglarus/membership_orders_detail.html"
    context_object_name = "order"
    login_url = reverse_lazy('digitalglarus:login')
    # permission_required = ['view_hostingorder']
    model = MembershipOrder

    def get_context_data(self, **kwargs):
        context = super(OrdersMembershipDetailView, self).get_context_data(**kwargs)
        start_date, end_date = self.object.get_membership_range_date()
        context.update({
            'membership_start_date': start_date,
            'membership_end_date': end_date,
        })
        return context


class OrdersBookingDetailView(LoginRequiredMixin, UpdateView):
    template_name = "digitalglarus/booking_orders_detail.html"
    context_object_name = "order"
    login_url = reverse_lazy('digitalglarus:login')
    form_class = CancelBookingForm
    success_message = "You booking has been cancelled"
    # permission_required = ['view_hostingorder']
    model = BookingOrder

    def get_success_url(self):
        pk = self.kwargs.get(self.pk_url_kwarg)
        return reverse_lazy('digitalglarus:booking_orders_detail', kwargs={'pk': pk})

    def form_valid(self, form):

        booking_order = self.get_object()
        booking_order.cancel()
        request = self.request

        BookingCancellation.create(booking_order)

        context = {
            'order': booking_order,
            'booking': booking_order.booking,
            'base_url': "{0}://{1}".format(request.scheme, request.get_host()),
            'user': request.user

        }

        email_data = {
            'subject': 'A cancellation has been requested',
            'to': 'info@ungleich.ch',
            'context': context,
            'template_name': 'booking_cancellation_notification',
            'template_path': 'digitalglarus/emails/'
        }

        email = BaseEmail(**email_data)
        email.send()

        context = {
            'order': booking_order,
            'booking': booking_order.booking,
            'base_url': "{0}://{1}".format(request.scheme, request.get_host())

        }
        email_data = {
            'subject': 'Your booking has been cancelled',
            'to': request.user.email,
            'context': context,
            'template_name': 'booking_cancellation',
            'template_path': 'digitalglarus/emails/'
        }
        email = BaseEmail(**email_data)
        email.send()

        messages.add_message(self.request, messages.SUCCESS, self.success_message)

        return HttpResponseRedirect(self.get_success_url())

    def get_context_data(self, **kwargs):

        context = super(OrdersBookingDetailView, self).get_context_data(**kwargs)

        bookig_order = self.object
        booking = bookig_order.booking

        start_date = booking.start_date
        end_date = booking.end_date
        free_days = booking.free_days

        booking_days = (end_date - start_date).days + 1
        original_price = booking.price
        final_price = booking.final_price

        context.update({
            'original_price': original_price,
            'total_discount': original_price - final_price,
            'final_price': final_price,
            'booking_days': booking_days,
            'free_days': free_days,
            'start_date': start_date.strftime('%m/%d/%Y'),
            'end_date': end_date.strftime('%m/%d/%Y'),
            'booking_required': bookig_order.refund_required(),
        })

        return context


class BookingOrdersListView(LoginRequiredMixin, ListView):
    template_name = "digitalglarus/booking_orders_list.html"
    context_object_name = "orders"
    login_url = reverse_lazy('digitalglarus:login')
    model = BookingOrder
    paginate_by = 10

    def get_context_data(self, **kwargs):
        context = super(BookingOrdersListView, self).get_context_data(**kwargs)
        current_billing_address = self.request.user.billing_addresses.filter(current=True).last()
        context.update({
            'billing_address': current_billing_address
        })
        return context

    def get_queryset(self):
        queryset = super(BookingOrdersListView, self).get_queryset()
        queryset = queryset.filter(customer__user=self.request.user)
        return queryset


class ContactView(FormView):
    template_name = 'contact.html'
    form_class = ContactUsForm
    success_url = reverse_lazy('digitalglarus:contact')
    success_message = _('Message Successfully Sent')

    def form_valid(self, form):
        form.save()
        form.send_email()
        messages.add_message(self.request, messages.SUCCESS, self.success_message)
        return super(ContactView, self).form_valid(form)

############## OLD VIEWS 

def blog(request):
    tags = ["digitalglarus"]
    posts = Post.objects.filter(tags__name__in=tags, publish=True).translated(get_language())
    # posts = Post.objects.filter_by_language(get_language()).filter(tags__name__in=tags, publish=True)
    context = {
        'post_list': posts,
    }
    return render(request, 'glarus_blog/post_list.html', context)


def blog_detail(request, slug):
    # post = Post.objects.filter_by_language(get_language()).filter(slug=slug).first()

    post = Post.objects.translated(get_language(), slug=slug).first()
    context = {
        'post': post,
    }
    return render(request, 'glarus_blog/post_detail.html', context)


def support(request):
    return render(request, 'support.html')


def supporters(request):
    context = {
        'supporters': Supporter.objects.order_by('name')
    }
    return render(request, 'supporters.html', context)