from django import forms
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.forms import widgets
from parts import models as parts_models
from shimatta_modules.EngineeringNumberConverter import EngineeringNumberConverter
import uuid
from django.urls import reverse
from .qr_parser import QrCodeValidator


from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Row, Column

class AutoCompleteWidget(widgets.Input):
    template_name = 'widgets/autocomplete-foreign-key.html'

    def __init__(self, api_search_url, image_field_name, foreign_model, name_field_name, prepend=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.image_field_name = image_field_name
        self.foreign_model = foreign_model
        self.api_search_url = api_search_url
        self.name_field_name = name_field_name
        self.prepend = prepend

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        if value is not None:
            try:
                try:
                    my_id = uuid.UUID(value)
                except:
                    my_id = int(value)
                instance = self.foreign_model.objects.get(id=my_id)
            except Exception as ex:
                instance = None
        else:
            instance = None

        image = None
        if instance is not None and self.image_field_name is not None:
            image = getattr(instance, self.image_field_name)

        display_name = None
        if instance:
            display_name = getattr(instance, self.name_field_name)

        context['custom'] = {
            'search_url': reverse(self.api_search_url),
            'image_field_name': self.image_field_name,
            'current_instance': instance,
            'image': image,
            'name_field_name': self.name_field_name,
            'prepend': self.prepend,
            'name': display_name,
        }
        return context

class AutocompleteForeingKeyField(forms.UUIDField):
    def __init__(self,
                 foreign_model=None,
                 api_search_url=None,
                 image_field_name='image',
                 name_field_name='name',
                 prepend=None,
                 **kwargs):
        super().__init__(**kwargs)
        self.widget = AutoCompleteWidget(api_search_url,
                                         image_field_name,
                                         foreign_model,
                                         name_field_name,
                                         prepend)
        self.foreign_model = foreign_model


    def clean(self, value):
        try:
            pre_cleaned_uuid = super().clean(value)
        except Exception as ex:
            try:
                pre_cleaned_uuid = int(value)
            except:
                raise ex
        if pre_cleaned_uuid is None and not self.required:
            return None
        try:
            obj = self.foreign_model.objects.get(id=pre_cleaned_uuid)
        except self.foreign_model.DoesNotExist:
            raise ValidationError('Given element does not exist')
        return obj

class MyTestForm(forms.Form):
    pass

class ChangeStorageForm(forms.Form):
    storage_name = forms.CharField(label="Name", initial='')
    verbose_name = forms.CharField(label="Verbose Name", initial='', required=False)
    responsible = AutocompleteForeingKeyField(api_search_url='user-list',
                                              image_field_name=None,
                                              name_field_name='username',
                                              foreign_model=get_user_model(),
                                              prepend='@')

    expand_sub_storage_stocks = forms.BooleanField(label='Expand sub storage Stocks', required=False)
    is_template = forms.BooleanField(label='Is template', required=False)

class AddSubStorageForm(ChangeStorageForm):
    template = AutocompleteForeingKeyField(api_search_url='storage-template-list',
                                           image_field_name=None,
                                           foreign_model=parts_models.Storage,
                                           required=False)

class DeleteStockForm(forms.Form):
    stock_uuid = forms.UUIDField()

class EditStockBaseForm(forms.Form):
    stock_uuid = forms.UUIDField()

    def clean(self):
        cleaned_data = super().clean()
        id = cleaned_data.get("stock_uuid")

        if not id:
            raise ValidationError("No stock UUID given")

        stock = None
        try:
            stock = parts_models.Stock.objects.get(id=id)
        except:
            raise ValidationError("Stock with uuid %s does not exist" % (id))
        cleaned_data['stock'] = stock

        return cleaned_data

class EditWatermarkForm(EditStockBaseForm):
    watermark_active = forms.BooleanField(required=False) #If it is false, the webbrowser won't send it at all. Therefore we have to set it to required=False
    watermark = forms.IntegerField(min_value=0)

    def save(self):
        stock = self.cleaned_data['stock']
        active = self.cleaned_data['watermark_active']
        watermark = self.cleaned_data['watermark']

        if not active:
            watermark = -1

        stock.watermark = watermark
        stock.save()

class EditLotForm(EditStockBaseForm):
    lot = forms.IntegerField(min_value=0)

    def save(self):
        stock = self.cleaned_data['stock']
        lot = self.cleaned_data['lot']

        stock.lot = lot
        stock.save()

class RelocateStockForm(forms.ModelForm):
    storage = AutocompleteForeingKeyField(api_search_url='storage-list',
                                          foreign_model=parts_models.Storage,
                                          name_field_name='full_path_verbose',
                                          image_field_name=None,
                                          required=True)

    class Meta:
        model = parts_models.Stock
        fields = ['storage']

class EditStockAmountForm(EditStockBaseForm):
    amount = forms.IntegerField(min_value=0)

    def save(self, increase: bool):
        stock = self.cleaned_data['stock']
        amount = self.cleaned_data['amount']

        if not increase:
            amount = -amount

        return stock.atomic_increment(amount)

class AddStockForm(forms.Form):
    watermark_active = forms.BooleanField(required=False)
    watermark = forms.IntegerField(min_value=0, required=True, initial=0)
    amount = forms.IntegerField(min_value=0, required=True, initial=1)
    component_uuid = forms.UUIDField(required=True)
    lot = forms.CharField(max_length=255, required=False)

    def clean(self):
        cleaned_data = super().clean()

        id = cleaned_data.get('component_uuid')
        if not id:
            raise ValidationError('No valid component selected!')
        component = None
        try:
            component = parts_models.Component.objects.get(id=id)
        except:
            raise ValidationError("Invalid component selected!")
        cleaned_data['component'] = component

        return cleaned_data

    def save(self, storage):
        component = self.cleaned_data.get('component')
        amount = self.cleaned_data.get('amount')
        watermark = -1

        if self.cleaned_data.get('watermark_active'):
            watermark = self.cleaned_data.get('watermark')

        new_stock = parts_models.Stock.objects.create(storage=storage, component=component, watermark=watermark, amount=amount, lot=self.cleaned_data['lot'])
        new_stock.save()

class ComponentForm(forms.ModelForm):
    manufacturer = AutocompleteForeingKeyField(api_search_url='manufacturer-list', foreign_model=parts_models.Manufacturer, required=False)
    component_type = AutocompleteForeingKeyField(api_search_url='componenttype-list', foreign_model=parts_models.ComponentType, name_field_name='class_name', required=False, image_field_name=None)
    package = AutocompleteForeingKeyField(api_search_url='package-list', foreign_model=parts_models.Package, required=False)
    pref_distri = AutocompleteForeingKeyField(api_search_url='distributor-list', foreign_model=parts_models.Distributor, required=False)
    class Meta:
        model = parts_models.Component
        fields = '__all__'

class ImportComponentForm(forms.Form):
    csv_file = forms.FileField(label="CSV File")

class PackageForm(forms.ModelForm):
    class Meta:
        model = parts_models.Package
        fields = '__all__'

class DistributorForm(forms.ModelForm):
    class Meta:
        model = parts_models.Distributor
        fields = '__all__'
class ManufacturerForm(forms.ModelForm):
    class Meta:
        model = parts_models.Manufacturer
        fields = '__all__'

class DistributorNumberCreateForm(forms.ModelForm):
    distributor = AutocompleteForeingKeyField(api_search_url='distributor-list', foreign_model=parts_models.Distributor, required=True)

    class Meta:
        model = parts_models.DistributorNum
        fields = ['distributor', 'distributor_part_number']

class DistributorNumberDeleteForm(forms.Form):
    distributor_num = forms.UUIDField(required=True)

    def clean_distributor_num(self):
        my_uuid = self.cleaned_data['distributor_num']
        try:
            distributor_number = parts_models.DistributorNum.objects.get(id=my_uuid)
        except:
            raise ValidationError('distributor number invalid')
        return distributor_number

class ComponentParameterDeleteForm(forms.Form):
    param_num = forms.UUIDField(required=True)
    model = parts_models.ComponentParameter

    def clean_param_num(self):
        my_uuid = self.cleaned_data['param_num']
        try:
            param = self.model.objects.get(id=my_uuid)
        except:
            raise ValidationError('Parameter Number Invalid')
        return param

class PackageParameterDeleteForm(ComponentParameterDeleteForm):
    model = parts_models.PackageParameter

class AdvancedComponentSearchForm(forms.Form):
    name = forms.CharField(max_length=255, label='Component Name', required=False)
    package = AutocompleteForeingKeyField(required=False, api_search_url='package-list', foreign_model=parts_models.Package)
    package_pin_count = forms.IntegerField(min_value=0, label='Pin Count', required=False)
    distributor_num = forms.CharField(max_length=100, label='Distributor Part Number', required=False)
    distributor = AutocompleteForeingKeyField(required=False, api_search_url='distributor-list', foreign_model=parts_models.Distributor)
    component_type = AutocompleteForeingKeyField(required=False, api_search_url='componenttype-list', foreign_model=parts_models.ComponentType, image_field_name=None, name_field_name='class_name')
    manufacturer = AutocompleteForeingKeyField(required=False, api_search_url='manufacturer-list', foreign_model=parts_models.Manufacturer)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
            Row(
                Column('name'),
            ),
            Row(
                Column('package'),
                Column('package_pin_count'),
            ),
            Row(
                Column('component_type'),
                Column('manufacturer'),
            ),
            Row(
                Column('distributor'),
                Column('distributor_num'),
            ),
        )

class ComponentParameterSearchForm(forms.Form):
    parameter = AutocompleteForeingKeyField(required=True, foreign_model=parts_models.ComponentParameterType, api_search_url='componentparametertype-list', image_field_name=None, name_field_name='parameter_name')
    value = forms.CharField(max_length=100, required=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
            Row(
                Column('parameter'),
                Column('value')
            )
        )
class ComponentParameterCreateForm(forms.Form):
    parameter_type = AutocompleteForeingKeyField(required=True, foreign_model=parts_models.ComponentParameterType, api_search_url='componentparametertype-list', image_field_name=None, name_field_name='descriptive_name')
    value = forms.CharField(required=True, max_length=256)
    model = parts_models.ComponentParameter

    def clean(self):
        data = super().clean()
        ptype = data.get('parameter_type')
        value = data.get('value')

        if not ptype:
            raise ValidationError('No valid parameter type selected')

        if not value:
            raise ValidationError('No valid parameter value')

        if ptype.parameter_type == 'E' or ptype.parameter_type == 'I' or ptype.parameter_type == 'N':
            try:
                number = EngineeringNumberConverter.engineering_to_number(value)
            except:
                raise ValidationError('Cannot convert value to number')
            data['number_value'] = number
        else:
            pass

    def save(self, component):
        param_type = self.cleaned_data['parameter_type']
        if param_type.parameter_type == 'F':
            text_value = self.cleaned_data['value']
            value = 0
        else:
            text_value = ''
            value = self.cleaned_data['number_value']
        self.model.objects.create(parameter_type=param_type, component=component, value=value, text_value=text_value)

class PackageParameterCreateForm(ComponentParameterCreateForm):
    model = parts_models.PackageParameter

    def save(self, package):
        param_type = self.cleaned_data['parameter_type']
        if param_type.parameter_type == 'F':
            text_value = self.cleaned_data['value']
            value = 0
        else:
            text_value = ''
            value = self.cleaned_data['number_value']
        self.model.objects.create(parameter_type=param_type, package=package, value=value, text_value=text_value)

class QrSearchForm(forms.Form):
    my_qr_validator = QrCodeValidator()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


    qr_search = forms.CharField(label='qr_search', validators=[my_qr_validator])