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.db.models import Q
from django.forms import formset_factory
from django.db import IntegrityError
from django.db.models import ProtectedError
from ..models import Stock, Component, ComponentParameter, DistributorNum, PackageParameter
from ..forms import *
from .component_import import import_components_from_csv
from .generic_views import BaseTemplateMixin


ParameterSearchFormSet = formset_factory(ComponentParameterSearchForm, extra=1)

# Create your views here.

class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
    template_name = 'parts/components.html'
    base_title = 'Components'
    navbar_selected = 'Components'
    default_page_size = 25

    def get_component_query_set(self, search_string):
        queryset = Component.objects.all()

        if not search_string:
            return queryset

        search_fragments = search_string.strip().split()
        for search in search_fragments:
            queryset = queryset.filter(Q(name__icontains = search) | Q(manufacturer__name__icontains = search) | Q(package__name__icontains = search))

        return queryset

    def get_component_queryset_from_advanced_search(self, cleaned_data):
        queryset = Component.objects.all()

        if cleaned_data['name']:
            queryset = queryset.filter(Q(name__icontains=cleaned_data['name']))
        if cleaned_data['package']:
            queryset = queryset.filter(package=cleaned_data['package'])
        if cleaned_data['package_pin_count']:
            queryset = queryset.filter(package__pin_count=cleaned_data['package_pin_count'])
        if cleaned_data['component_type']:
            queryset = queryset.filter(component_type=cleaned_data['component_type'])
        if cleaned_data['distributor_num']:
            if cleaned_data['distributor']:
                distri = cleaned_data['distributor']
                queryset = queryset.filter(Q(distributornum__distributor_part_number__icontains=cleaned_data['distributor_num']) & Q(distributornum__distributor=distri))
            else:
                 queryset = queryset.filter(Q(distributornum__distributor_part_number__icontains=cleaned_data['distributor_num']))
        if cleaned_data['manufacturer']:
            queryset = queryset.filter(manufacturer=cleaned_data['manufacturer'])
        return queryset

    def get_context_data_int(self, advanced_search, parameter_formset : ParameterSearchFormSet, **kwargs):
        context = super().get_context_data(**kwargs)

        comp_page_num = self.request.GET.get('comp_page', default=1)

        if advanced_search and parameter_formset:
            search = None
            context['advanced_search_shown'] = True
            context['advanced_search_form'] = advanced_search
            context['advanced_search_param_formset'] = parameter_formset
            if advanced_search.is_valid():
                paginator_queryset = self.get_component_queryset_from_advanced_search(
			advanced_search.cleaned_data)
            else:
                paginator_queryset = Component.objects.all()

            if parameter_formset.is_valid():
                # Process parameters
                pass

        else:
            search = self.request.GET.get('search', default=None)
            paginator_queryset = self.get_component_query_set(search)

        comp_paginator = Paginator(paginator_queryset, self.default_page_size)

        context['components'] = comp_paginator.get_page(comp_page_num)
        context['comp_form'] = ComponentForm()
        context['import_comp_form'] = ImportComponentForm()
        context['search_string'] = search

        if not parameter_formset:
            context['advanced_search_param_formset'] = ParameterSearchFormSet()


        if not advanced_search:
            context['advanced_search_form'] = AdvancedComponentSearchForm(auto_id='adv_search_%s')

        return context

    def get_context_data(self, **kwargs):
        return self.get_context_data_int(advanced_search = None, parameter_formset=None, **kwargs)

    def handle_new_component_post(self, request, open=False, **kwargs):
        cform = ComponentForm(data=request.POST, files=request.FILES)
        new_component = None
        if cform.is_valid():
            new_component = cform.save()

        context = self.get_context_data(**kwargs)
        if not cform.is_valid():
            context['comp_form'] = cform

        if open and new_component:
            return redirect(reverse('parts-components-detail', kwargs={'uuid':new_component.id}))
        return self.render_to_response(context)

    def handle_import_components_post(self, request, open=False, **kwargs):
        cform = ImportComponentForm(data=request.POST, files=request.FILES)
        context = self.get_context_data(**kwargs)
        if cform.is_valid():
            try:
                import_components_from_csv(cform.files['csv_file'].file)
            except Exception as ex:
                cform.add_error('csv_file', str(ex))
                context['import_comp_form'] = cform
        else:
            context['import_comp_form'] = cform

        return self.render_to_response(context)

    def handle_advanced_search_post(self, request, **kwargs):

        form = AdvancedComponentSearchForm(auto_id='adv_search_%s', data=request.POST)
        param_formset = ParameterSearchFormSet(data=request.POST)

        if form.is_valid():
            print('Valid')

        if param_formset.is_valid():
            print('Formset is valid!')


        context = self.get_context_data_int(form, param_formset, **kwargs)
        return self.render_to_response(context)

    def post(self, request, *args, **kwargs):
        if 'submit-edit-component' in request.POST:
            return self.handle_new_component_post(request, open=False, **kwargs)
        elif 'submit-edit-component-open' in request.POST:
            return self.handle_new_component_post(request, open=True, **kwargs)
        elif 'submit-import-components' in request.POST:
            return self.handle_import_components_post(request, open=True, **kwargs)
        elif 'submit-advanced-search' in request.POST:
            return self.handle_advanced_search_post(request, **kwargs)
        else:
            return super().post(request, *args, **kwargs)

class ComponentDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
    template_name = 'parts/components-detail.html'
    model = Component
    pk_url_kwarg = 'uuid'
    base_title = ''
    navbar_selected = 'Components'

    def get_context_data(self, **kwargs):
        self.base_title = 'Component / '+self.object.name
        context = super().get_context_data(**kwargs)
        context['component'] = self.object
        context['stocks'] = Stock.objects.filter(component=self.object)
        context['comp_form'] = ComponentForm(instance=self.object)
        context['new_distri_num_form'] = DistributorNumberCreateForm()
        context['new_param_form'] = ComponentParameterCreateForm()
        context['distri_nums'] = DistributorNum.objects.filter(component=self.object).order_by(
            'distributor__name')
        context['parameters'] = ComponentParameter.objects.filter(component=self.object).order_by(
            'parameter_type__parameter_name')
        context['package_parameters'] = PackageParameter.objects.filter(package=self.object.package).order_by(
            'parameter_type__parameter_name')

        return context

    def handle_submit_edit_component_post(self, request, **kwargs):
        cform = ComponentForm(instance=self.object, data=request.POST, files=request.FILES)
        if cform.is_valid():
            cform.save()

        context = self.get_context_data(**kwargs)
        if not cform.is_valid():
            context['comp_form'] = cform

        return self.render_to_response(context)

    def handle_submit_delete_post(self, request, **kwargs):
        delete_error = None
        protected_stuff = None

        try:
            self.object.delete()
        except ProtectedError as pe:
            delete_error = 'Component is protected'
            protected_stuff = pe.protected_objects
        except:
            delete_error = 'Cannot delete component. Unknown error'

        if delete_error is None:
            return redirect('parts-components')
        else:
            context = self.get_context_data(**kwargs)
            context['delete_error'] = delete_error
            context['protected_stuff'] = protected_stuff
            return self.render_to_response(context)

    def handle_submit_new_distri_num_post(self, request, **kwargs):
        form = DistributorNumberCreateForm(data=request.POST)
        if form.is_valid():
            new_number = form.save(commit=False)
            new_number.component = self.object
            try:
                new_number.save()
            except IntegrityError as ie:
                form.add_error('__all__', 'Number for given distributor already exists')

        context = self.get_context_data(**kwargs)
        if not form.is_valid():
            context['new_distri_num_form'] = form
        return self.render_to_response(context)

    def handle_submit_delete_distri_num_post(self, request, **kwargs):
        form = DistributorNumberDeleteForm(data=request.POST)
        if form.is_valid():
            form.cleaned_data['distributor_num'].delete()

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

    def handle_submit_delete_param_post(self, request, **kwargs):
        form = ComponentParameterDeleteForm(data=request.POST)
        if form.is_valid():
            form.cleaned_data['param_num'].delete()

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

    def handle_submit_new_param_post(self, request, **kwargs):
        form = ComponentParameterCreateForm(data=request.POST)

        if form.is_valid():
            try:
                form.save(self.object)
            except IntegrityError:
                form.add_error('__all__', 'This parameter is already set')
        context = self.get_context_data(**kwargs)
        if not form.is_valid():
            context['new_param_form'] = form
        return self.render_to_response(context)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        if 'submit-edit-component' in request.POST:
            return self.handle_submit_edit_component_post(request, **kwargs)
        elif 'submit-component-delete' in request.POST:
            return self.handle_submit_delete_post(request, **kwargs)
        elif 'submit-create-new-distri-num' in request.POST:
            return self.handle_submit_new_distri_num_post(request, **kwargs)
        elif 'submit-delete-distributor-num' in request.POST:
            return self.handle_submit_delete_distri_num_post(request, **kwargs)
        elif 'submit-delete-param' in request.POST:
            return self.handle_submit_delete_param_post(request, **kwargs)
        elif 'submit-create-new-param' in request.POST:
            return self.handle_submit_new_param_post(request, **kwargs)
        else:
            return super().post(request, *args, **kwargs)


