import uuid
from django.shortcuts import redirect
from django.urls import reverse
from django.views.generic import TemplateView, DetailView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.paginator import Paginator
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.db.models.functions import Lower
from ..models import Storage, Stock
from ..forms import *
from .generic_views import BaseTemplateMixin

class StockView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
    template_name = 'parts/stocks.html'
    base_title = 'Stocks'
    navbar_selected = 'Stocks'
    default_pagination_size = 25

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

        storage_page = self.request.GET.get('storage_page')
        if storage_page is None:
            storage_page = 1
        low_stock_page = self.request.GET.get('low_stock_page')
        if low_stock_page is None:
            low_stock_page = 1

        storage_paginator = Paginator(Storage.objects.filter(parent_storage=None), self.default_pagination_size)
        low_stock_paginator = Paginator(Stock.get_under_watermark(),
            self.default_pagination_size)

        context['low_stocks'] = low_stock_paginator.get_page(low_stock_page)
        context['storages'] = storage_paginator.get_page(storage_page)
        add_stor_form = AddSubStorageForm()
        add_stor_form.fields['responsible'].initial = self.request.user.id
        context['add_storage_form'] = add_stor_form
        return context

    def handle_add_storage(self, request, **kwargs):
        f = AddSubStorageForm(data=request.POST)
        if f.is_valid():
            sub_name = f.cleaned_data['storage_name']
            try:
                Storage.objects.create(name=sub_name,
                                        responsible=f.cleaned_data['responsible'],
                                        is_template=f.cleaned_data['is_template'],
                                        template=f.cleaned_data.get('template'))
            except ValidationError as v_err:
                f.add_error('storage_name', '. '.join(v_err.messages))
        context = self.get_context_data(**kwargs)
        context['add_storage_form'] = f
        return self.render_to_response(context)

    def post(self, request, **kwargs):
        if 'submit-add-storage' in request.POST:
            return self.handle_add_storage(request, **kwargs)

        return super().post(request, **kwargs)

class StockViewDetail(LoginRequiredMixin, BaseTemplateMixin, DetailView):
    template_name = 'parts/stocks-detail.html'
    model = Storage
    pk_url_kwarg = 'uuid'
    base_title = ''
    navbar_selected = 'Stocks'
    default_pagination_size = 8

    def get_breadcrumbs(self):
        crumbs = self.object.get_path_components()
        # Reverse list and drop the last element of the reversed list
        crumbs = crumbs[::-1][:-1]
        return crumbs

    def search_stock_queryset(self, search):

        if self.object.expand_sub_storage_stocks:
            stocks_in_storage = Stock.objects.filter(storage__in=self.object.get_storage_list())
        else:
            stocks_in_storage = Stock.objects.filter(storage=self.object)

        stocks_in_storage = stocks_in_storage.order_by(Lower('component__name'))

        if search is None or search == '':
            return stocks_in_storage

        if search.startswith('[comp_uuid]'):
            search = search.replace('[comp_uuid]', '')


        # Check if the searhc equals a UUID
        test_uuid = None
        try:
            test_uuid = uuid.UUID(search)
        except:
            pass

        if test_uuid is not None:
            stocks_in_storage = stocks_in_storage.filter(Q(component__id = test_uuid) | Q(id= test_uuid))
        else:
            stocks_in_storage = stocks_in_storage.filter(Q(component__name__icontains = search) |
                Q(component__package__name__icontains = search) |
                Q(component__manufacturer__name__icontains = search))

        return stocks_in_storage

    def get_context_data(self, **kwargs):
        self.base_title = 'Stocks / ' + self.object.name
        context = super().get_context_data(**kwargs)
        context['breadcrumbs'] = self.get_breadcrumbs()

        storage_page = self.request.GET.get('storage_page', default=1)
        storage_paginator = Paginator(Storage.objects.filter(parent_storage=self.object), self.default_pagination_size)
        stock_search_input = self.request.GET.get('search')

        componente_stock_page = self.request.GET.get('stock_page', default=1)

        stock_paginator = Paginator(self.search_stock_queryset(stock_search_input), self.default_pagination_size)

        context['storages'] = storage_paginator.get_page(storage_page)
        stocks = stock_paginator.get_page(componente_stock_page)
        context['stocks'] = stocks
        context['stocks_with_forms'] = [{'object': s, 'relocate_form': RelocateStockForm(instance=s, prefix=str(s.id))} for s in stocks]
        context['stock_search'] = stock_search_input
        add_storage_form = AddSubStorageForm()
        add_storage_form.fields['responsible'].initial = self.request.user.id
        context['add_storage_form'] = add_storage_form
        change_storage_form = ChangeStorageForm(prefix='change_storage')
        change_storage_form.fields['storage_name'].initial = self.object.name
        change_storage_form.fields['verbose_name'].initial = self.object.verbose_name
        change_storage_form.fields['responsible'].initial = self.object.responsible.id
        change_storage_form.fields['expand_sub_storage_stocks'].initial = self.object.expand_sub_storage_stocks
        change_storage_form.fields['is_template'].initial = self.object.is_template
        context['change_storage_form'] = change_storage_form
        context['delete_storage_error'] = None
        context['add_stock_form'] = AddStockForm()

        return context

    def handle_add_storage_post(self, request, **kwargs):
        f = AddSubStorageForm(data=request.POST)
        if f.is_valid():
            sub_name = f.cleaned_data['storage_name']
            try:
                Storage.objects.create(name=sub_name,
                                        verbose_name=f.cleaned_data.get('verbose_name'),
                                        parent_storage=self.object,
                                        responsible=f.cleaned_data['responsible'],
                                        expand_sub_storage_stocks=f.cleaned_data['expand_sub_storage_stocks'],
                                        is_template=f.cleaned_data['is_template'],
                                        template=f.cleaned_data.get('template'))
            except ValidationError as v_err:
                f.add_error('storage_name', '. '.join(v_err.messages))
        context = self.get_context_data(**kwargs)
        context['add_storage_form'] = f
        return self.render_to_response(context)

    def handle_change_storage_post(self, request, **kwargs):
        f = ChangeStorageForm(data=request.POST, prefix='change_storage')
        if f.is_valid():
            try:
                self.object.name = f.cleaned_data['storage_name']
                self.object.verbose_name = f.cleaned_data.get('verbose_name')
                self.object.responsible = f.cleaned_data['responsible']
                self.object.expand_sub_storage_stocks = f.cleaned_data['expand_sub_storage_stocks']
                self.object.is_template = f.cleaned_data['is_template']
                self.object.save()
            except ValidationError as v_err:
                f.add_error('storage_name', '. '.join(v_err.messages))
        context = self.get_context_data(**kwargs)
        context['change_storage_form'] = f
        return self.render_to_response(context)

    def handle_del_storage_post(self, request, **kwargs):
        parent = self.object.parent_storage
        try:
            self.object.delete()
        except:
            context = self.get_context_data(**kwargs)
            context['delete_storage_errors'] = ['Error deleting Storage '+str(self.object)]
            return self.render_to_response(context)

        if parent is None:
            return redirect('parts-stocks')
        else:
            return redirect(reverse('parts-stocks-detail', kwargs={'uuid':parent.id}))

    def handle_del_stock_post(self, request, **kwargs):
        del_error = None # TODO: Check error handling. This is clearly not working as intended :P
        if 'stock_uuid' in request.POST:
            f = DeleteStockForm(data=request.POST)
            if f.is_valid():
                try:
                    s = Stock.objects.get(id=f.cleaned_data['stock_uuid'])
                    print(s.storage)
                    print(self.object)
                    if s.storage == self.object:
                       s.delete()
                    else:
                        del_error = 'Cannot delete stock from another storage.'
                except:
                    del_error = 'Could not find requested stock in this storage.'

        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)

    def handle_update_watermark(self, request, **kwargs):
        edit_form = EditWatermarkForm(data=request.POST)
        if edit_form.is_valid():
            edit_form.save()
        else:
            pass # Todo: Handle error

        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)

    def handle_update_lot(self, request, **kwargs):
        edit_form = EditLotForm(data=request.POST)
        if edit_form.is_valid():
            edit_form.save()
        else:
            pass # Todo: Handle error

        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)

    def handle_relocate_stock(self, request, **kwargs):
        instance = Stock.objects.get(id=request.POST['prefix'])
        edit_form = RelocateStockForm(instance=instance, data=request.POST, prefix=request.POST['prefix'])
        if edit_form.is_valid():
            edit_form.save()
        else:
            pass # Todo: Handle error

        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)

    def handle_amount_change_post(self, request, increase, **kwargs):
        edit_form = EditStockAmountForm(data=request.POST)
        if edit_form.is_valid():
            edit_form.save(increase)

        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)

    def handle_add_stock_post(self, request, **kwargs):
        f = AddStockForm(data=request.POST)
        error_occured = False

        if f.is_valid():
            try:
                f.save(self.object)
            except Exception as ex:
                f.add_error('', str(ex))
                error_occured = True
        else:
            error_occured = True

        context = self.get_context_data(**kwargs)
        if error_occured:
            context['add_stock_form'] = f
        return self.render_to_response(context)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()

        if 'submit-add-storage' in request.POST:
            return self.handle_add_storage_post(request, **kwargs)
        elif 'submit-change-storage' in request.POST:
            return self.handle_change_storage_post(request, **kwargs)
        elif 'submit-delete-storage' in request.POST:
            return self.handle_del_storage_post(request, **kwargs)
        elif 'submit-delete-stock' in request.POST:
            return self.handle_del_stock_post(request, **kwargs)
        elif 'submit-edit-watermark' in request.POST:
            return self.handle_update_watermark(request, **kwargs)
        elif 'submit-edit-lot' in request.POST:
            return self.handle_update_lot(request, **kwargs)
        elif 'submit-relocate-stock' in request.POST:
            return self.handle_relocate_stock(request, **kwargs)
        elif 'submit-amount-reduce' in request.POST:
            return self.handle_amount_change_post(request, False, **kwargs)
        elif 'submit-amount-increase' in request.POST:
            return self.handle_amount_change_post(request, True, **kwargs)
        elif 'submit-add-stock' in request.POST:
            return self.handle_add_stock_post(request, **kwargs)

        return super().post(request, *args, **kwargs)