<reponame>tobast/care
from datetime import datetime
from decimal import Decimal
import logging

from django import forms
from django.shortcuts import get_object_or_404
from bootstrap3_datetime.widgets import DateTimePicker

from care.transaction.models import ShareGroupPreset
from care.transaction.models import Transaction
from care.transaction.models import TransactionConsumer
from care.transaction.models import TransactionRecurring
from care.transaction.models import TransactionReal
from care.transaction.models import Modification
from care.groupaccount.models import GroupAccount
from care.userprofile.models import UserProfile

from .fields import SelectShareConsumersWidget, ShareConsumersField

logger = logging.getLogger(__name__)


class ConsumersSelectMixin:
    """ Mixin for a ModelForm with `consumers`, `is_shared_equally` and
    `is_shared_by_all` fields """

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

    def get_initial_shared_equally(self):
        if 'consumers' in self.initial:
            weights = self.initial['consumers']
            if all(map(lambda x: x == 1., weights.values())):
                return True
            return False
        return True

    def get_initial_for_field(self, field, field_name):
        if field_name == 'is_shared_equally':
            return self.get_initial_shared_equally()
        return super().get_initial_for_field(field, field_name)

    def get_group_account(self, cleaned_data):
        return cleaned_data.get('group_account')

    def clean(self):
        cleaned_data = super().clean()
        if not self.has_consumers_data(cleaned_data):
            raise forms.ValidationError("Please select someone to share with.")
        cleaned_data['consumers'] = self.compose_consumers(cleaned_data)
        self.check_valid_consumers(cleaned_data['consumers'])
        return cleaned_data

    def compose_consumers(self, cleaned_data):
        if cleaned_data.get("is_shared_by_all"):
            all_users = UserProfile.objects.filter(
                group_accounts=self.get_group_account(cleaned_data)
            )
            return {
                'qs': all_users,
                'weights': {user.pk: 1.0 for user in all_users.all()},
            }
        if (cleaned_data.get("is_shared_equally", True)
                and cleaned_data['consumers']):
            return {
                'qs': cleaned_data['consumers']['qs'],
                'weights': {
                    user: 1.0 for user in cleaned_data['consumers']['weights'].keys()
                }
            }
        return cleaned_data['consumers']

    def check_valid_consumers(self, cleaned_data):
        print("Checking {}".format(cleaned_data))
        if not cleaned_data or not cleaned_data['qs'].exists():
            raise forms.ValidationError("Please select someone to share with.")
        if abs(sum(map(Decimal, cleaned_data['weights'].values()))) < 1e-6:
            raise forms.ValidationError("The share proportions' sum must be non-null")
        if any(map(
            lambda weight: float(weight) <= 0,
                cleaned_data['weights'].values())
               ):
            raise forms.ValidationError("The share proportions must be positive")

    def has_consumers_data(self, cleaned_data):
        return (
            cleaned_data.get('consumers', None)
            or cleaned_data.get('is_shared_by_all', False)
        )


class TransactionForm(ConsumersSelectMixin, forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['is_shared_by_all'] = forms.BooleanField(label='Shared by all', required=False)
        self.fields['is_shared_equally'] = forms.BooleanField(label='Shared equally', required=False)
        self.fields['preset'] = forms.ModelChoiceField(
            queryset=ShareGroupPreset.objects.none(),
            empty_label="(None)",
            label="Preset",
            required=False,
        )
        self.fields['what'].label = 'What'
        self.fields['amount'].label = 'Cost (€)'

        cur_groupaccount = self.initial.get('group_account', None)
        if cur_groupaccount:
            cur_groupaccount = get_object_or_404(GroupAccount, pk=cur_groupaccount)
            self.set_presets(cur_groupaccount)

    def set_presets(self, groupaccount):
        self.fields['preset'].queryset = groupaccount.share_presets

    def has_consumers_data(self, cleaned_data):
        return (
            super().has_consumers_data(cleaned_data)
            or cleaned_data.get('preset', None)
        )

    def compose_consumers(self, cleaned_data):
        preset = cleaned_data.get('preset', None)
        if preset:
            return {
                'qs': preset.consumers.all(),
                'weights': {
                    consumer.user.pk: consumer.share_weight
                    for consumer in preset.consumers_detailed.all()
                }
            }
        return super().compose_consumers(cleaned_data)

    def save(self, commit=True, *args, **kwargs):
        instance = super().save(*args, commit=False, **kwargs)
        instance.total_weight = 0
        if commit:
            instance.save()
            self.save_m2m()
        self.instance.update_total_weight()

    class Meta:
        model = Transaction
        fields = '__all__'


class NewTransactionForm(TransactionForm):
    def __init__(self, group_account_id, user, *args, **kwargs):
        self.groupaccount_instance = get_object_or_404(
            GroupAccount, pk=group_account_id
        )
        super().__init__(*args, **kwargs)

        self.set_presets(self.groupaccount_instance)

        self.fields['consumers'] = ShareConsumersField(
            queryset=GroupAccount.shown_members_queryset(group_account_id),
            label="Shared by some",
            required=False,
        )
        self.fields['buyer'] = forms.ModelChoiceField(
            queryset=GroupAccount.shown_members_queryset(group_account_id),
            empty_label=None
        )
        self.fields['buyer'].initial = UserProfile.objects.get(user=user)

        self.fields['group_account'] = forms.ModelChoiceField(
            queryset=UserProfile.objects.get(user=user).group_accounts,
            widget=forms.Select(attrs={"onChange":'form.submit()'}),
            empty_label=None,
            label='Group'
        )
        if GroupAccount.objects.filter(id=group_account_id).count():
            self.fields['group_account'].initial = GroupAccount.objects.get(id=group_account_id)

        self.fields['date'] = forms.DateTimeField(
            widget=DateTimePicker(options={"format": "YYYY-MM-DD"}),
            initial=datetime.now
        )

        self.fields['modifications'] = forms.ModelMultipleChoiceField(
            queryset=Modification.objects.all(),
            required=False,
            widget=forms.MultipleHiddenInput()
        )

    def set_group_account(self, group_account):
        self.fields['group_account'].initial = group_account


class EditTransactionForm(TransactionForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        transaction = self.instance

        self.fields['consumers'] = ShareConsumersField(
            queryset=GroupAccount.shown_members_queryset(transaction.group_account),
            label='Shared by some',
            required=False
        )

        self.fields['buyer'] = forms.ModelChoiceField(
            queryset=GroupAccount.shown_members_queryset(transaction.group_account),
            empty_label=None
        )

        self.fields['group_account'] = forms.ModelChoiceField(
            queryset=GroupAccount.objects.filter(id=transaction.group_account.id),
            empty_label=None,
            label='Group'
        )

        self.fields['date'] = forms.DateTimeField(widget=DateTimePicker(options={"format": "YYYY-MM-DD", "pickTime": False}))
        # self.fields['modifications'] = forms.ModelMultipleChoiceField(queryset=Modification.objects.all(),
        #                                                               required=False,
        #                                                               widget=forms.MultipleHiddenInput())
        self.fields['group_account'].widget.attrs['readonly'] = True


class NewRecurringTransactionForm(NewTransactionForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['date'].label = 'Starting on'

    class Meta:
        model = TransactionRecurring
        exclude = ('last_occurrence', )


class EditRecurringTransactionForm(EditTransactionForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['date'].label = 'Starting on'

    class Meta:
        model = TransactionRecurring
        exclude = ('last_occurrence', )


class NewRealTransactionForm(forms.ModelForm):
    def __init__(self, group_account_id, user, *args, **kwargs):
        super().__init__(*args, **kwargs)

        #  self.fields['sender'] = forms.ModelChoiceField(queryset=UserProfile.objects.get(user=user),
        #  widget = forms.HiddenInput, empty_label=None, label='From')
        self.fields['sender'] = forms.ModelChoiceField(
            queryset=UserProfile.objects.filter(user=user),
            empty_label=None,
            label='From',
            widget=forms.HiddenInput()
        )
        self.fields['sender'].initial = UserProfile.objects.get(user=user)
        self.fields['receiver'] = forms.ModelChoiceField(
            queryset=GroupAccount.shown_members_queryset(group_account_id),
            empty_label=None,
            label='To'
        )

        self.fields['comment'] = forms.CharField(required=False)
        self.fields['amount'].label = '€'

        self.fields['group_account'] = forms.ModelChoiceField(
            queryset=UserProfile.objects.get(user=user).group_accounts,
            widget=forms.Select(attrs={"onChange": 'form.submit()'}),
            empty_label=None,
            label='Group'
        )

        if GroupAccount.objects.filter(id=group_account_id).count():
            self.fields['group_account'].initial = GroupAccount.objects.get(id=group_account_id)
        # self.fields['modifications'] = forms.ModelMultipleChoiceField(queryset=Modification.objects.all(),
        #                                                               required=False,
        #                                                               widget=forms.MultipleHiddenInput())
        self.fields['date'] = forms.DateTimeField(
            widget=DateTimePicker(options={"format": "YYYY-MM-DD"}),
            initial=datetime.now
        )

    class Meta:
        model = TransactionReal
        fields = '__all__'


class EditRealTransactionForm(forms.ModelForm):
    def __init__(self, transaction_id, *args, **kwargs):
        super().__init__(*args, **kwargs)

        transaction = TransactionReal.objects.get(id=transaction_id)

        self.fields['amount'].label = '€'
        self.fields['sender'] = forms.ModelChoiceField(
            queryset=UserProfile.objects.filter(group_accounts=transaction.group_account.id),
            empty_label=None, label='From',
            widget=forms.HiddenInput)

        self.fields['receiver'] = forms.ModelChoiceField(
            queryset=GroupAccount.shown_members_queryset(transaction.group_account.id),
            empty_label=None, label='To')

        self.fields['group_account'] = forms.ModelChoiceField(
            queryset=GroupAccount.objects.filter(id=transaction.group_account.id),
            empty_label=None,
            label='Group')

        self.fields['group_account'].widget.attrs['readonly'] = True
        self.fields['modifications'] = forms.ModelMultipleChoiceField(queryset=Modification.objects.all(),
                                                                      required=False,
                                                                      widget=forms.MultipleHiddenInput())

        self.fields['date'] = forms.DateTimeField(widget=DateTimePicker(options={"format": "YYYY-MM-DD"}))

    class Meta:
        model = TransactionReal
        fields = '__all__'


class PresetForm(ConsumersSelectMixin, forms.ModelForm):
    ''' Edit a ShareGroupPreset '''

    consumers = ShareConsumersField(
        queryset=UserProfile.objects.none(),
        label="Shared by some",
        required=False,
    )
    group_account = forms.CharField(
        label="Group",
        disabled=True
    )
    is_shared_by_all = forms.BooleanField(label='Shared by all', required=False)
    is_shared_equally = forms.BooleanField(label='Shared equally', required=False)

    class Meta:
        model = ShareGroupPreset
        fields = ('name', 'consumers')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        try:
            self.group_account = self.initial['group_account']
        except KeyError as exn:
            raise ValueError(
                "PresetForm must be either bound or given an initial group_account"
            ) from exn
        self.fields['consumers'].queryset = GroupAccount.shown_members_queryset(
            self.group_account.id
        )
        self.fields['group_account'].queryset = GroupAccount.objects.filter(
            pk=self.group_account.pk
        ).all()

    def get_group_account(self, _):
        return self.group_account

    def save(self, commit=True, *args, **kwargs):
        instance = super().save(*args, commit=False, **kwargs)
        instance.group_account = self.group_account
        if commit:
            instance.save()
            self.save_m2m()

from datetime import datetime
from decimal import Decimal
import logging

from django import forms
from django.shortcuts import get_object_or_404
from bootstrap3_datetime.widgets import DateTimePicker

from care.transaction.models import ShareGroupPreset
from care.transaction.models import Transaction
from care.transaction.models import TransactionConsumer
from care.transaction.models import TransactionRecurring
from care.transaction.models import TransactionReal
from care.transaction.models import Modification
from care.groupaccount.models import GroupAccount
from care.userprofile.models import UserProfile

from .fields import SelectShareConsumersWidget, ShareConsumersField

logger = logging.getLogger(__name__)


class ConsumersSelectMixin:
    """ Mixin for a ModelForm with `consumers`, `is_shared_equally` and
    `is_shared_by_all` fields """

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

    def get_initial_shared_equally(self):
        if 'consumers' in self.initial:
            weights = self.initial['consumers']
            if all(map(lambda x: x == 1., weights.values())):
                return True
            return False
        return True

    def get_initial_for_field(self, field, field_name):
        if field_name == 'is_shared_equally':
            return self.get_initial_shared_equally()
        return super().get_initial_for_field(field, field_name)

    def get_group_account(self, cleaned_data):
        return cleaned_data.get('group_account')

    def clean(self):
        cleaned_data = super().clean()
        if not self.has_consumers_data(cleaned_data):
            raise forms.ValidationError("Please select someone to share with.")
        cleaned_data['consumers'] = self.compose_consumers(cleaned_data)
        self.check_valid_consumers(cleaned_data['consumers'])
        return cleaned_data

    def compose_consumers(self, cleaned_data):
        if cleaned_data.get("is_shared_by_all"):
            all_users = UserProfile.objects.filter(
                group_accounts=self.get_group_account(cleaned_data)
            )
            return {
                'qs': all_users,
                'weights': {user.pk: 1.0 for user in all_users.all()},
            }
        if (cleaned_data.get("is_shared_equally", True)
                and cleaned_data['consumers']):
            return {
                'qs': cleaned_data['consumers']['qs'],
                'weights': {
                    user: 1.0 for user in cleaned_data['consumers']['weights'].keys()
                }
            }
        return cleaned_data['consumers']

    def check_valid_consumers(self, cleaned_data):
        print("Checking {}".format(cleaned_data))
        if not cleaned_data or not cleaned_data['qs'].exists():
            raise forms.ValidationError("Please select someone to share with.")
        if abs(sum(map(Decimal, cleaned_data['weights'].values()))) < 1e-6:
            raise forms.ValidationError("The share proportions' sum must be non-null")
        if any(map(
            lambda weight: float(weight) <= 0,
                cleaned_data['weights'].values())
               ):
            raise forms.ValidationError("The share proportions must be positive")

    def has_consumers_data(self, cleaned_data):
        return (
            cleaned_data.get('consumers', None)
            or cleaned_data.get('is_shared_by_all', False)
        )


class TransactionForm(ConsumersSelectMixin, forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['is_shared_by_all'] = forms.BooleanField(label='Shared by all', required=False)
        self.fields['is_shared_equally'] = forms.BooleanField(label='Shared equally', required=False)
        self.fields['preset'] = forms.ModelChoiceField(
            queryset=ShareGroupPreset.objects.none(),
            empty_label="(None)",
            label="Preset",
            required=False,
        )
        self.fields['what'].label = 'What'
        self.fields['amount'].label = 'Cost (€)'

        cur_groupaccount = self.initial.get('group_account', None)
        if cur_groupaccount:
            cur_groupaccount = get_object_or_404(GroupAccount, pk=cur_groupaccount)
            self.set_presets(cur_groupaccount)

    def set_presets(self, groupaccount):
        self.fields['preset'].queryset = groupaccount.share_presets

    def has_consumers_data(self, cleaned_data):
        return (
            super().has_consumers_data(cleaned_data)
            or cleaned_data.get('preset', None)
        )

    def compose_consumers(self, cleaned_data):
        preset = cleaned_data.get('preset', None)
        if preset:
            return {
                'qs': preset.consumers.all(),
                'weights': {
                    consumer.user.pk: consumer.share_weight
                    for consumer in preset.consumers_detailed.all()
                }
            }
        return super().compose_consumers(cleaned_data)

    def save(self, commit=True, *args, **kwargs):
        instance = super().save(*args, commit=False, **kwargs)
        instance.total_weight = 0
        if commit:
            instance.save()
            self.save_m2m()
        self.instance.update_total_weight()

    class Meta:
        model = Transaction
        fields = '__all__'


class NewTransactionForm(TransactionForm):
    def __init__(self, group_account_id, user, *args, **kwargs):
        self.groupaccount_instance = get_object_or_404(
            GroupAccount, pk=group_account_id
        )
        super().__init__(*args, **kwargs)

        self.set_presets(self.groupaccount_instance)

        self.fields['consumers'] = ShareConsumersField(
            queryset=GroupAccount.shown_members_queryset(group_account_id),
            label="Shared by some",
            required=False,
        )
        self.fields['buyer'] = forms.ModelChoiceField(
            queryset=GroupAccount.shown_members_queryset(group_account_id),
            empty_label=None
        )
        self.fields['buyer'].initial = UserProfile.objects.get(user=user)

        self.fields['group_account'] = forms.ModelChoiceField(
            queryset=UserProfile.objects.get(user=user).group_accounts,
            widget=forms.Select(attrs={"onChange":'form.submit()'}),
            empty_label=None,
            label='Group'
        )
        if GroupAccount.objects.filter(id=group_account_id).count():
            self.fields['group_account'].initial = GroupAccount.objects.get(id=group_account_id)

        self.fields['date'] = forms.DateTimeField(
            widget=DateTimePicker(options={"format": "YYYY-MM-DD"}),
            initial=datetime.now
        )

        self.fields['modifications'] = forms.ModelMultipleChoiceField(
            queryset=Modification.objects.all(),
            required=False,
            widget=forms.MultipleHiddenInput()
        )

    def set_group_account(self, group_account):
        self.fields['group_account'].initial = group_account


class EditTransactionForm(TransactionForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        transaction = self.instance

        self.fields['consumers'] = ShareConsumersField(
            queryset=GroupAccount.shown_members_queryset(transaction.group_account),
            label='Shared by some',
            required=False
        )

        self.fields['buyer'] = forms.ModelChoiceField(
            queryset=GroupAccount.shown_members_queryset(transaction.group_account),
            empty_label=None
        )

        self.fields['group_account'] = forms.ModelChoiceField(
            queryset=GroupAccount.objects.filter(id=transaction.group_account.id),
            empty_label=None,
            label='Group'
        )

        self.fields['date'] = forms.DateTimeField(widget=DateTimePicker(options={"format": "YYYY-MM-DD", "pickTime": False}))
        # self.fields['modifications'] = forms.ModelMultipleChoiceField(queryset=Modification.objects.all(),
        #                                                               required=False,
        #                                                               widget=forms.MultipleHiddenInput())
        self.fields['group_account'].widget.attrs['readonly'] = True


class NewRecurringTransactionForm(NewTransactionForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['date'].label = 'Starting on'

    class Meta:
        model = TransactionRecurring
        exclude = ('last_occurrence', )


class EditRecurringTransactionForm(EditTransactionForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['date'].label = 'Starting on'

    class Meta:
        model = TransactionRecurring
        exclude = ('last_occurrence', )


class NewRealTransactionForm(forms.ModelForm):
    def __init__(self, group_account_id, user, *args, **kwargs):
        super().__init__(*args, **kwargs)

        #  self.fields['sender'] = forms.ModelChoiceField(queryset=UserProfile.objects.get(user=user),
        #  widget = forms.HiddenInput, empty_label=None, label='From')
        self.fields['sender'] = forms.ModelChoiceField(
            queryset=UserProfile.objects.filter(user=user),
            empty_label=None,
            label='From',
            widget=forms.HiddenInput()
        )
        self.fields['sender'].initial = UserProfile.objects.get(user=user)
        self.fields['receiver'] = forms.ModelChoiceField(
            queryset=GroupAccount.shown_members_queryset(group_account_id),
            empty_label=None,
            label='To'
        )

        self.fields['comment'] = forms.CharField(required=False)
        self.fields['amount'].label = '€'

        self.fields['group_account'] = forms.ModelChoiceField(
            queryset=UserProfile.objects.get(user=user).group_accounts,
            widget=forms.Select(attrs={"onChange": 'form.submit()'}),
            empty_label=None,
            label='Group'
        )

        if GroupAccount.objects.filter(id=group_account_id).count():
            self.fields['group_account'].initial = GroupAccount.objects.get(id=group_account_id)
        # self.fields['modifications'] = forms.ModelMultipleChoiceField(queryset=Modification.objects.all(),
        #                                                               required=False,
        #                                                               widget=forms.MultipleHiddenInput())
        self.fields['date'] = forms.DateTimeField(
            widget=DateTimePicker(options={"format": "YYYY-MM-DD"}),
            initial=datetime.now
        )

    class Meta:
        model = TransactionReal
        fields = '__all__'


class EditRealTransactionForm(forms.ModelForm):
    def __init__(self, transaction_id, *args, **kwargs):
        super().__init__(*args, **kwargs)

        transaction = TransactionReal.objects.get(id=transaction_id)

        self.fields['amount'].label = '€'
        self.fields['sender'] = forms.ModelChoiceField(
            queryset=UserProfile.objects.filter(group_accounts=transaction.group_account.id),
            empty_label=None, label='From',
            widget=forms.HiddenInput)

        self.fields['receiver'] = forms.ModelChoiceField(
            queryset=GroupAccount.shown_members_queryset(transaction.group_account.id),
            empty_label=None, label='To')

        self.fields['group_account'] = forms.ModelChoiceField(
            queryset=GroupAccount.objects.filter(id=transaction.group_account.id),
            empty_label=None,
            label='Group')

        self.fields['group_account'].widget.attrs['readonly'] = True
        self.fields['modifications'] = forms.ModelMultipleChoiceField(queryset=Modification.objects.all(),
                                                                      required=False,
                                                                      widget=forms.MultipleHiddenInput())

        self.fields['date'] = forms.DateTimeField(widget=DateTimePicker(options={"format": "YYYY-MM-DD"}))

    class Meta:
        model = TransactionReal
        fields = '__all__'


class PresetForm(ConsumersSelectMixin, forms.ModelForm):
    ''' Edit a ShareGroupPreset '''

    consumers = ShareConsumersField(
        queryset=UserProfile.objects.none(),
        label="Shared by some",
        required=False,
    )
    group_account = forms.CharField(
        label="Group",
        disabled=True
    )
    is_shared_by_all = forms.BooleanField(label='Shared by all', required=False)
    is_shared_equally = forms.BooleanField(label='Shared equally', required=False)

    class Meta:
        model = ShareGroupPreset
        fields = ('name', 'consumers')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        try:
            self.group_account = self.initial['group_account']
        except KeyError as exn:
            raise ValueError(
                "PresetForm must be either bound or given an initial group_account"
            ) from exn
        self.fields['consumers'].queryset = GroupAccount.shown_members_queryset(
            self.group_account.id
        )
        self.fields['group_account'].queryset = GroupAccount.objects.filter(
            pk=self.group_account.pk
        ).all()

    def get_group_account(self, _):
        return self.group_account

    def save(self, commit=True, *args, **kwargs):
        instance = super().save(*args, commit=False, **kwargs)
        instance.group_account = self.group_account
        if commit:
            instance.save()
            self.save_m2m()
Mixin for a ModelForm with `consumers`, `is_shared_equally` and     `is_shared_by_all` fields # self.fields['modifications'] = forms.ModelMultipleChoiceField(queryset=Modification.objects.all(), #                                                               required=False, #                                                               widget=forms.MultipleHiddenInput()) #  self.fields['sender'] = forms.ModelChoiceField(queryset=UserProfile.objects.get(user=user), #  widget = forms.HiddenInput, empty_label=None, label='From') # self.fields['modifications'] = forms.ModelMultipleChoiceField(queryset=Modification.objects.all(), #                                                               required=False, #                                                               widget=forms.MultipleHiddenInput()) Edit a ShareGroupPreset