import logging
import json
from datetime import datetime
from django.conf import settings
from django.utils import timezone
from django import forms
from django.apps import apps
from django.shortcuts import render, reverse, redirect, get_object_or_404
from django.http import JsonResponse, HttpResponseRedirect
from django.urls import reverse, reverse_lazy
from django.views.generic.base import RedirectView, TemplateView
from django.views.generic.edit import (
    FormView, CreateView, UpdateView, DeleteView, FormMixin
)
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.generic.list import ListView
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
from django.contrib.auth.mixins import (
    LoginRequiredMixin, PermissionRequiredMixin
)
from django.forms import inlineformset_factory
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin

from django.db.models import (
    Exists, OuterRef, Prefetch, Subquery, Count, Sum, Avg, F, Q, Value,
    ExpressionWrapper, IntegerField
)
from django.db.models.functions import Coalesce

from accounts.models import User
from accounts.utils import get_or_create_customer
from accounts.forms import AddressForm
from core.models import (
    ProductCategory,
    Product,
    ProductPhoto,
    ProductVariant,
    ProductOption,
    Order,
    OrderLine,
    ShippingRate,
    Transaction,
    TrackingNumber,
    Coupon,
    SiteSettings
)

from core import (
    DiscountValueType,
    VoucherType,
    OrderStatus
)
from .forms import (
    ProductVariantUpdateForm,
    OrderLineFulfillForm,
    OrderLineFormset,
    OrderCancelForm,
    OrderTrackingFormset,
    CouponForm,
    ProductPhotoForm
)

logger = logging.getLogger(__name__)


class DashboardHomeView(LoginRequiredMixin, TemplateView):
    template_name = 'dashboard/home.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        today = timezone.localtime(timezone.now()).date()
        context['order_count'] = Order.objects.exclude(
            status=OrderStatus.DRAFT
        ).filter(
            created_at__date=today
        ).count()
        context['orders_unfulfilled'] = Order.objects.filter(
            status=OrderStatus.UNFULFILLED
        ).count()
        context['todays_sales'] = Order.objects.exclude(
            status=OrderStatus.DRAFT
        ).filter(
            created_at__date=today
        ).aggregate(total=Sum('total_amount'))['total']
        return context


class DashboardConfigView(LoginRequiredMixin, TemplateView):
    template_name = 'dashboard/config.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['shipping_rate_list'] = ShippingRate.objects.all()
        return context


class SiteSettingsUpdateView(
    LoginRequiredMixin, PermissionRequiredMixin, UpdateView
):
    permission_required = 'core.change_sitesettings'
    model = SiteSettings
    context_object_name = 'settings'
    template_name = 'dashboard/settings_form.html'
    fields = '__all__'
    success_url = reverse_lazy('dashboard:config')
    success_message = 'Settings saved.'


class CatalogView(LoginRequiredMixin, ListView):
    model = ProductCategory
    context_object_name = 'category_list'
    template_name = 'dashboard/catalog.html'

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context['uncategorized_products'] = Product.objects.filter(
            category=None
        )
        context['option_list'] = ProductOption.objects.all()
        return context


class StockView(LoginRequiredMixin, ListView):
    model = ProductVariant
    context_object_name = 'variant_list'
    template_name = 'dashboard/stock.html'

    def get_queryset(self):
        object_list = ProductVariant.objects.filter(
            track_inventory=True
        ).prefetch_related('order_lines', 'product').annotate(
            total_in_warehouse=F('stock') + Coalesce(Sum('order_lines__quantity', filter=Q(
                order_lines__order__status=OrderStatus.UNFULFILLED) | Q(
                order_lines__order__status=OrderStatus.PARTIALLY_FULFILLED)
                ) - Sum('order_lines__quantity_fulfilled', filter=Q(
                    order_lines__order__status=OrderStatus.UNFULFILLED) | Q(
                    order_lines__order__status=OrderStatus.PARTIALLY_FULFILLED)), 0)
        ).order_by('product')
        return object_list


class ShippingRateDetailView(LoginRequiredMixin, DetailView):
    model = ShippingRate
    context_object_name = 'rate'
    template_name = 'dashboard/rate/detail.html'


class ShippingRateCreateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
):
    permission_required = 'core.add_shippingrate'
    model = ShippingRate
    context_object_name = 'rate'
    template_name = 'dashboard/rate/create_form.html'
    fields = '__all__'
    success_message = '%(name)s created.'


class ShippingRateUpdateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
    permission_required = 'core.change_shippingrate'
    model = ShippingRate
    context_object_name = 'rate'
    template_name = 'dashboard/rate/form.html'
    success_message = 'ShippingRate saved.'
    fields = '__all__'


class ShippingRateDeleteView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
):
    permission_required = 'core.delete_shippingrate'
    model = ShippingRate
    context_object_name = 'rate'
    template_name = 'dashboard/rate/confirm_delete.html'
    success_message = 'ShippingRate deleted.'
    success_url = reverse_lazy('dashboard:config')


class CouponListView(LoginRequiredMixin, ListView):
    model = Coupon
    template_name = 'dashboard/coupon/list.html'


class CouponCreateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
):
    permission_required = 'core.add_coupon'
    model = Coupon
    template_name = 'dashboard/coupon/create_form.html'
    form_class = CouponForm
    success_message = '%(name)s created.'


class CouponDetailView(LoginRequiredMixin, DetailView):
    model = Coupon
    template_name = 'dashboard/coupon/detail.html'


class CouponUpdateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
    permission_required = 'core.change_coupon'
    model = Coupon
    template_name = 'dashboard/coupon/form.html'
    success_message = '%(name)s saved.'
    form_class = CouponForm


class CouponDeleteView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
):
    permission_required = 'core.delete_coupon'
    model = Coupon
    template_name = 'dashboard/coupon/confirm_delete.html'
    success_url = reverse_lazy('dashboard:coupon-list')
    success_message = 'Coupon deleted.'


class OrderListView(LoginRequiredMixin, ListView):
    model = Order
    template_name = 'dashboard/order/list.html'
    paginate_by = 50

    def get_queryset(self):
        query = self.request.GET.get('status')
        if query == 'unfulfilled':
            object_list = Order.objects.filter(
                Q(status=OrderStatus.UNFULFILLED) |
                Q(status=OrderStatus.PARTIALLY_FULFILLED)
            ).order_by('-created_at').select_related('customer')

        else:
            object_list = Order.objects.order_by(
                '-created_at'
            ).select_related('customer')

        return object_list


class OrderDetailView(LoginRequiredMixin, DetailView):
    model = Order
    template_name = 'dashboard/order/detail.html'

    def get_object(self):
        queryset = Order.objects.with_fulfillment_and_filter(
            self.kwargs.get(self.pk_url_kwarg)
        ).select_related(
            'customer',
            'billing_address',
            'shipping_address'
        ).prefetch_related(
            'lines__variant__product__productphoto_set'
        )
        obj = queryset.get()
        return obj

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


class OrderFulfillView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
    model = Order
    template_name = 'dashboard/order/fulfill.html'
    form_class = OrderLineFormset
    success_message = 'Order saved.'

    def form_valid(self, form):
        form.save()
        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        return reverse('dashboard:order-detail', kwargs={'pk': self.object.pk})


class OrderCancelView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
    permission_required = 'core.cancel_order'
    model = Order
    template_name = 'dashboard/order/cancel_form.html'
    form_class = OrderCancelForm
    success_message = 'Order canceled.'
    initial = {
        'status': OrderStatus.CANCELED
    }

    def form_valid(self, form):
        form.instance.add_stock()
        form.instance.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse('dashboard:order-detail', kwargs={'pk': self.object.pk})


class OrderTrackingView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
    model = Order
    template_name = 'dashboard/order/tracking_form.html'
    form_class = OrderTrackingFormset
    success_message = 'Order saved.'

    def form_valid(self, form):
        form.save()
        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        return reverse('dashboard:order-detail', kwargs={'pk': self.object.pk})


class CategoryListView(LoginRequiredMixin, ListView):
    model = ProductCategory
    context_object_name = 'category_list'
    template_name = 'dashboard/category/list.html'


class CategoryCreateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
):
    permission_required = 'core.add_productcategory'
    model = ProductCategory
    context_object_name = 'category'
    success_message = 'Category created.'
    template_name = 'dashboard/category/create_form.html'
    fields = '__all__'


class CategoryDetailView(LoginRequiredMixin, DetailView):
    model = ProductCategory
    context_object_name = 'category'
    template_name = 'dashboard/category/detail.html'


class CategoryUpdateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
    permission_required = 'core.change_productcategory'
    model = ProductCategory
    context_object_name = 'category'
    success_message = 'Category saved.'
    template_name = 'dashboard/category/form.html'
    fields = '__all__'


class CategoryDeleteView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
):
    permission_required = 'core.delete_productcategory'
    model = ProductCategory
    context_object_name = 'category'
    success_message = 'Category deleted.'
    template_name = 'dashboard/category/confirm_delete.html'
    success_url = reverse_lazy('dashboard:catalog')


class ProductListView(LoginRequiredMixin, ListView):
    model = Product
    template_name = 'dashboard/product/list.html'
    ordering = 'sorting'

    # def get_queryset(self):
    #     object_list = Product.objects.filter(
    #         status=OrderStatus.UNFULFILLED
    #     ).select_related(
    #         'customer'
    #     )
    #     return object_list


class ProductDetailView(LoginRequiredMixin, DetailView):
    model = Product
    template_name = 'dashboard/product/detail.html'

    def get_object(self):
        pk = self.kwargs.get(self.pk_url_kwarg)
        queryset = Product.objects.filter(
            pk=pk
        ).select_related(
            'category',
        ).prefetch_related(
            'options',
            'productphoto_set',
            Prefetch(
                'variants',
                queryset=ProductVariant.objects.all().order_by('sorting')
            )
        )
        obj = queryset.get()
        return obj


class ProductCreateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
):
    permission_required = 'core.add_product'
    model = Product
    template_name = 'dashboard/product/create_form.html'
    fields = '__all__'
    success_message = '%(name)s created.'


class ProductUpdateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
    permission_required = 'core.change_product'
    model = Product
    template_name = 'dashboard/product/form.html'
    fields = '__all__'
    success_message = '%(name)s saved.'


class ProductDeleteView(
    LoginRequiredMixin,
    PermissionRequiredMixin,
    SuccessMessageMixin,
    DeleteView
):
    permission_required = 'core.delete_product'
    model = Product
    template_name = 'dashboard/product/confirm_delete.html'
    success_url = reverse_lazy('dashboard:catalog')
    success_message = 'Product deleted.'


class ProductPhotoCreateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
):
    permission_required = 'core.add_productphoto'
    model = ProductPhoto
    pk_url_kwarg = 'photo_pk'
    template_name = 'dashboard/product/photo_create_form.html'
    form_class = ProductPhotoForm
    success_message = 'Photo added.'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['product'] = Product.objects.get(pk=self.kwargs['pk'])
        return context

    def form_valid(self, form):
        form.instance.product = Product.objects.get(pk=self.kwargs['pk'])
        return super().form_valid(form)

    def get_success_url(self):
        return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})


class ProductPhotoDeleteView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
):
    permission_required = 'core.delete_productphoto'
    model = ProductPhoto
    pk_url_kwarg = 'photo_pk'
    template_name = 'dashboard/prodphoto_confirm_delete.html'
    success_message = 'Photo deleted.'

    def get_success_url(self):
        return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})


class ProductVariantCreateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
):
    permission_required = 'core.add_productvariant'
    model = ProductVariant
    success_message = 'Variant created.'
    template_name = 'dashboard/variant/create_form.html'
    fields = [
        'name',
        'sku',
        'price',
        'weight',
        'visible_in_listings',
        'track_inventory',
        'stock',
    ]

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['product'] = Product.objects.get(pk=self.kwargs['pk'])
        return context

    def form_valid(self, form):
        form.instance.product = Product.objects.get(pk=self.kwargs['pk'])
        return super().form_valid(form)

    def get_success_url(self):
        return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})


class ProductVariantUpdateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
    permission_required = 'core.change_productvariant'
    model = ProductVariant
    pk_url_kwarg = 'variant_pk'
    success_message = 'Variant saved.'
    template_name = 'dashboard/variant/form.html'
    form_class = ProductVariantUpdateForm
    context_object_name = 'variant'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['product'] = Product.objects.get(pk=self.kwargs['pk'])
        return context

    def form_valid(self, form):
        form.instance.product = Product.objects.get(pk=self.kwargs['pk'])
        return super().form_valid(form)

    def get_success_url(self):
        return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})


class ProductVariantDeleteView(
    LoginRequiredMixin,
    PermissionRequiredMixin,
    SuccessMessageMixin,
    DeleteView
):
    permission_required = 'core.delete_productvariant'
    model = ProductVariant
    pk_url_kwarg = 'variant_pk'
    success_message = 'ProductVariant deleted.'
    template_name = 'dashboard/variant/confirm_delete.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['product'] = Product.objects.get(pk=self.kwargs['pk'])
        return context

    def get_success_url(self):
        return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})


class ProductVariantStockUpdateView(
    LoginRequiredMixin, PermissionRequiredMixin, UpdateView
):
    permission_required = 'core.change_productvariant'
    model = ProductVariant
    pk_url_kwarg = 'variant_pk'
    success_message = 'ProductVariant saved.'
    success_url = reverse_lazy('dashboard:stock')
    template_name = 'dashboard/variant/restock.html'
    fields = [
        'stock',
    ]
    context_object_name = 'variant'

    def get_queryset(self):
        queryset = ProductVariant.objects.annotate(
            total_in_warehouse=F('stock') + Coalesce(Sum('order_lines__quantity', filter=Q(
                order_lines__order__status=OrderStatus.UNFULFILLED) | Q(
                order_lines__order__status=OrderStatus.PARTIALLY_FULFILLED)
                ) - Sum('order_lines__quantity_fulfilled', filter=Q(
                    order_lines__order__status=OrderStatus.UNFULFILLED) | Q(
                    order_lines__order__status=OrderStatus.PARTIALLY_FULFILLED)), 0)
        ).prefetch_related('order_lines', 'product')
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['product'] = Product.objects.get(pk=self.kwargs['pk'])
        return context

    def form_valid(self, form):
        form.instance.product = Product.objects.get(pk=self.kwargs['pk'])
        return super().form_valid(form)


class ProductOptionDetailView(LoginRequiredMixin, DetailView):
    model = ProductOption
    template_name = 'dashboard/option/detail.html'
    context_object_name = 'option'


class ProductOptionCreateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
):
    permission_required = 'core.add_productoption'
    model = ProductOption
    template_name = 'dashboard/option/create_form.html'
    fields = [
        'name',
        'options',
        'products',
    ]
    success_message = '%(name)s created.'


class ProductOptionUpdateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
    permission_required = 'core.change_productoption'
    model = ProductOption
    success_message = 'Option saved.'
    template_name = 'dashboard/option/form.html'
    fields = [
        'name',
        'options',
        'products',
    ]
    context_object_name = 'option'
    success_url = reverse_lazy('dashboard:catalog')


class ProductOptionDeleteView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
):
    permission_required = 'core.delete_productoption'
    model = ProductOption
    success_message = 'ProductOption deleted.'
    template_name = 'dashboard/option/confirm_delete.html'
    context_object_name = 'option'
    success_url = reverse_lazy('dashboard:catalog')


def sort(objs, order):
    for i, pk in enumerate(order):
        m = objs.get(pk=pk)
        m.sorting = i+1
        yield m


@require_POST
def update_sorting(request):
    data = json.loads(request.body)
    model = apps.get_model('core', data['model_name'])
    objs = model.objects.filter(
        Q((data['filter'], data['filter_id']))
    ).order_by('sorting')

    updated_objs = sort(objs, data['order'])

    model.objects.bulk_update(updated_objs, ['sorting'])
    return JsonResponse({'message': 'Sorting updated'})


class CustomerListView(LoginRequiredMixin, ListView):
    model = User
    template_name = 'dashboard/customer/list.html'
    paginate_by = 100

    def get_queryset(self):
        object_list = User.objects.filter(
            Exists(
                Order.objects.filter(customer=OuterRef('pk'))
            ) | Q(is_staff=False)
        ).prefetch_related(
            'orders'
        ).annotate(
            num_orders=Count('orders')
        ).order_by('first_name', 'last_name')

        return object_list


class CustomerDetailView(LoginRequiredMixin, DetailView):
    model = User
    template_name = 'dashboard/customer/detail.html'
    context_object_name = 'customer'


class CustomerUpdateView(
    LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
    permission_required = 'accounts.change_user'
    model = User
    template_name = 'dashboard/customer/form.html'
    context_object_name = 'customer'
    success_message = 'Customer saved.'
    fields = (
        'first_name',
        'last_name',
        'email',
        'is_staff',
        'addresses',
        'default_shipping_address'
    )

    def get_success_url(self):
        return reverse('dashboard:customer-detail', kwargs={'pk': self.object.pk})
