import logging
from django.views.generic.base import TemplateView 
from stripe.error import CardError
from django.shortcuts import redirect, render
from django.contrib import messages
from django.utils.translation import get_language, ugettext_lazy as _
from django.contrib.auth.decorators import login_required
from django.views.decorators.cache import cache_control
from django.utils.decorators import method_decorator
from django.views import View
from django.views.generic import FormView, DetailView
from django.views.generic.list import ListView
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from .forms import InitialRequestForm, RequestDomainsNamesForm
from uncloud_pay.forms import BillingAddressForm
from django.urls import reverse
from django.conf import settings
from django.utils import timezone
from django_q.models import Schedule
from django_q.tasks import schedule
from django.http import (
    HttpResponseRedirect, JsonResponse, HttpResponse
)
from rest_framework import viewsets, permissions

from uncloud_pay.models import PricingPlan
from uncloud_pay.utils import get_order_total_with_vat
from uncloud_pay.models import *
from uncloud_pay.utils import validate_vat_number
from uncloud_pay.selectors import get_billing_address_for_user, get_balance_for_user
import uncloud_pay.stripe as uncloud_stripe
from .models import VMMachine
from .serializers import *
from .utils import *
from ldap.ldapobject import LDAPObject

logger = logging.getLogger(__name__)


class PricingView(TemplateView):
    template_name = "nextcloud/pricing.html"

class IndexView(FormView):
    template_name = "nextcloud/index.html"
    form_class = InitialRequestForm
    success_url = "/nextcloud#requestform"
    success_message = "Thank you, we will contact you as soon as possible"


    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['vm_pricing'] = PricingPlan.get_default_pricing()
        return context
    
    def form_valid(self, form):
        self.request.session['order'] = form.cleaned_data
        pricing = get_order_total_with_vat(
            form.cleaned_data['cores'],
            form.cleaned_data['memory'],
            form.cleaned_data['storage'],
            form.cleaned_data['pricing_name'], 
            False
        )
        self.request.session['pricing'] = pricing
        return HttpResponseRedirect(reverse('nextcloud:payment'))


class OrderPaymentView(FormView):
    template_name = 'nextcloud/order_details.html'
    success_url = 'nextcloud:order_confirmation'
    form_class = BillingAddressForm

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(OrderPaymentView, self).get_context_data(**kwargs)
        default_pricing = PricingPlan.get_default_pricing()
        if 'billing_address_data' in self.request.session:
            billing_address_form = BillingAddressForm(
                    initial=self.request.session['billing_address_data']
                )
        else:
            old_active = get_billing_address_for_user(self.request.user)
            billing_address_form = BillingAddressForm(
                    instance=old_active
                ) if old_active else BillingAddressForm(
                    initial={'active': True, 'owner': self.request.user.id}
                )
        if self.request.GET.get('product', False):
            matched_prod = Product.objects.filter(name=self.request.GET.get('product')).first()
            if matched_prod:
                self.request.session['order'] = matched_prod.config

        details_form = InitialRequestForm(
                initial=self.request.session.get('order', {'pricing_name': default_pricing.name})
            )
        balance = get_balance_for_user(self.request.user)
        customer_id = uncloud_stripe.get_customer_id_for(self.request.user)
        #TODO optimize this part for better performance
        uncloud_stripe.sync_cards_for_user(self.request.user)
        cards = uncloud_stripe.get_customer_cards(customer_id)
        pricing = self.request.session.get('pricing', {'name': default_pricing.name, 'total': 0})
        context.update({
            'vm_pricing': PricingPlan.get_by_name(pricing['name']), 
            'billing_address_form': billing_address_form,
            'details_form': details_form,
            'balance': balance,
            'cards': cards,
            'stripe_key': settings.STRIPE_PUBLIC_KEY,
            'show_cards': True if balance < pricing['total'] else False,
        })
        return context

    @cache_control(no_cache=True, must_revalidate=True, no_store=True)
    def get(self, request, *args, **kwargs):
        for k in ['vat_validation_status', 'token', 'id_payment_method', 'total']:
            if request.session.get(k):
                request.session.pop(k)
        return self.render_to_response(self.get_context_data())
    
    def post(self, request, *args, **kwargs):
        details_form = InitialRequestForm(request.POST)
        billing_address_form = BillingAddressForm(request.POST)
        context = self.get_context_data()
        if not details_form.is_valid() or not billing_address_form.is_valid():
            context.update({'details_form': details_form, 
                            'billing_address_form': billing_address_form})
            return self.render_to_response(context)
        address = get_billing_address_for_user(self.request.user)
        if address:
            form = BillingAddressForm(self.request.POST, instance=address)
        else:
            form = BillingAddressForm(self.request.POST)
        if form.is_valid:
            billing_address_ins = form.save()
            self.request.session["billing_address_id"] = billing_address_ins.id
            self.request.session['billing_address_data'] = billing_address_form.cleaned_data
            self.request.session['billing_address_data']['owner'] = self.request.user.id
        id_payment_method = self.request.POST.get('id_payment_method', False)
        selected_card = False
        if id_payment_method and id_payment_method != 'undefined':
            uncloud_stripe.attach_payment_method(id_payment_method, self.request.user)
            selected_card = StripeCreditCard.objects.filter(card_id=id_payment_method).first()
            selected_card.activate()
        vat_number = billing_address_form.cleaned_data.get('vat_number').strip()
        if vat_number:
            customer_id = uncloud_stripe.get_customer_id_for(self.request.user)
            validate_result = validate_vat_number(
                stripe_customer_id=customer_id,
                billing_address_id=billing_address_ins.id
            )
            if 'error' in validate_result and validate_result['error']:
                messages.add_message(
                    self.request, messages.ERROR, validate_result["error"],
                    extra_tags='error'
                )
                return HttpResponseRedirect(
                    reverse('nextcloud:payment')
                )
            self.request.session["vat_validation_status"] = validate_result["status"]
        specs = details_form.cleaned_data
        vat_rate = VATRate.get_vat_rate(billing_address_ins)
        vat_validation_status = "verified" if billing_address_ins.vat_number_validated_on and billing_address_ins.vat_number_verified else False
        pricing = get_order_total_with_vat(
            specs['cores'], specs['memory'], specs['storage'], context['vm_pricing'].name,
            vat_rate=vat_rate * 100, vat_validation_status = vat_validation_status
        )
        self.request.session['pricing'] = pricing
        self.request.session['order'] = specs
        self.request.session['vat_validation_status'] = vat_validation_status
        amount = get_balance_for_user(self.request.user) - pricing["total"] 
        if (amount < 0 and not selected_card):
            messages.add_message(
                    self.request, messages.ERROR, "You haven't enough balance please select credit card to continue",
                    extra_tags='error'
            )
            return HttpResponseRedirect(
                    reverse('nextcloud:payment')
                )
        return HttpResponseRedirect(reverse('nextcloud:order_confirmation'))

class OrderConfirmationView(DetailView):
    template_name = "nextcloud/order_confirmation.html"
    context_object_name = "order"
    model = Order

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = {
             'order': self.request.session.get('order'),
            'pricing': self.request.session.get('pricing'),
            'balance': get_balance_for_user(self.request.user)
        }
        return context

    @cache_control(no_cache=True, must_revalidate=True, no_store=True)
    def get(self, request, *args, **kwargs):
        context = self.get_context_data()
        context['domains_form'] = RequestDomainsNamesForm(initial={})
        if ('order' not in request.session):
            return HttpResponseRedirect(reverse('nextcloud:index'))
        elif 'pricing' not in self.request.session or 'vat_validation_status' not in self.request.session:
            return HttpResponseRedirect(reverse('nextcloud:payment')) 

        total = self.request.session['pricing']['total']       
        amount = get_balance_for_user(self.request.user) - total
        if (amount < 0):
            context['stripe_deposit_amount'] = max(amount, settings.MIN_PER_TRANSACTION)
        return render(request, self.template_name, context)

    def post(self, request, *args, **kwargs):
        domains_form = RequestDomainsNamesForm(self.request.POST)
        if domains_form.is_valid():
            customer = StripeCustomer.objects.get(owner=self.request.user)
            billing_address = BillingAddress.objects.get(id=request.session.get('billing_address_id')) 
            total = self.request.session['pricing']['total']       
            self.request.session['order']['domain'] = domains_form.cleaned_data.get('subdomain') + "." + domains_form.cleaned_data.get('main_domain')
            try:
                amount = get_balance_for_user(self.request.user) - total
                if (amount < 0):
                    Payment.deposit(request.user, max(abs(amount), settings.MIN_PER_TRANSACTION), source='stripe')
                amount = get_balance_for_user(self.request.user) - total
                if (amount < 0):
                    messages.add_message(
                    self.request, messages.ERROR, "Please make sure that you have enough balance in your wallet and try again later.",
                    extra_tags='error'
                    )
                    return HttpResponseRedirect(
                        reverse('nextcloud:order_confirmation')
                    )
                order, bill = finalize_order(request, customer, 
                        billing_address,
                        total, 
                        PricingPlan.get_by_name(self.request.session['pricing']['name']),
                        request.session.get('order'))
                if order and bill:
                    self.request.session['bill_id'] = bill.id
                    return HttpResponseRedirect(reverse('uncloud_pay:order_success'))   
            except CardError as e:
                messages.add_message(
                    self.request, messages.ERROR, e.user_message,
                    extra_tags='error'
                )
                return HttpResponseRedirect(
                    reverse('nextcloud:order_confirmation')
                )
        context = self.get_context_data()
        context['domains_form'] = domains_form
        return self.render_to_response(context)
    
class OrdersView(ListView):
    template_name = "nextcloud/orders.html"
    model = Order

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

    def get_queryset(self):
        return Order.objects.filter(owner=self.request.user).order_by('-creation_date')

    def post(self, request, *args, **kwargs):
        order = Order.objects.get(id=request.POST.get('order_id', 0))
        order.cancel()
        if hasattr(order, 'instance_id'):
            last_bill_record = BillRecord.objects.filter(order=order).order_by('id').last()
            schedule('nextcloud.tasks.delete_instance',
                order.instance_id,
                schedule_type=Schedule.ONCE,
                next_run=last_bill_record.ending_date or (timezone.now() + datetime.timedelta(hours=1)))
        return JsonResponse({'message': 'Successfully Cancelled'})

class InstancesView(ListView):
    template_name = "nextcloud/instances.html"
    model = VMMachine

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

    def get_queryset(self):
        return VMMachine.objects.filter(owner=self.request.user).order_by('-creation_date')

