from decimal import Decimal

from django import forms
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.utils import timezone

from .models import BusinessProfile, Transaction


INPUT_CLASS = 'form-control form-control-lg momo-input'
SELECT_CLASS = 'form-select form-select-lg momo-input'


class SignUpForm(UserCreationForm):
    first_name = forms.CharField(max_length=150, required=True, widget=forms.TextInput(attrs={'class': INPUT_CLASS, 'placeholder': 'First name'}))
    last_name = forms.CharField(max_length=150, required=True, widget=forms.TextInput(attrs={'class': INPUT_CLASS, 'placeholder': 'Last name'}))
    email = forms.EmailField(required=True, widget=forms.EmailInput(attrs={'class': INPUT_CLASS, 'placeholder': 'Email address'}))

    class Meta(UserCreationForm.Meta):
        model = User
        fields = ('first_name', 'last_name', 'username', 'email')
        widgets = {
            'username': forms.TextInput(attrs={'class': INPUT_CLASS, 'placeholder': 'Username'}),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['username'].widget.attrs.update({'class': INPUT_CLASS, 'placeholder': 'Username'})
        self.fields['password1'].widget.attrs.update({'class': INPUT_CLASS, 'placeholder': 'Password'})
        self.fields['password2'].widget.attrs.update({'class': INPUT_CLASS, 'placeholder': 'Confirm password'})

    def save(self, commit=True):
        user = super().save(commit=False)
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.email = self.cleaned_data['email']
        if commit:
            user.save()
        return user


class LoginForm(AuthenticationForm):
    username = forms.CharField(widget=forms.TextInput(attrs={'class': INPUT_CLASS, 'placeholder': 'Username'}))
    password = forms.CharField(widget=forms.PasswordInput(attrs={'class': INPUT_CLASS, 'placeholder': 'Password'}))


class BusinessProfileForm(forms.ModelForm):
    class Meta:
        model = BusinessProfile
        fields = ['business_name', 'logo', 'opening_ecash', 'opening_physical_cash']
        widgets = {
            'business_name': forms.TextInput(attrs={'class': INPUT_CLASS, 'placeholder': 'e.g. BrightPay MoMo Point'}),
            'logo': forms.ClearableFileInput(attrs={'class': 'form-control momo-file', 'accept': 'image/*'}),
            'opening_ecash': forms.NumberInput(attrs={'class': INPUT_CLASS, 'step': '0.01', 'min': '0'}),
            'opening_physical_cash': forms.NumberInput(attrs={'class': INPUT_CLASS, 'step': '0.01', 'min': '0'}),
        }
        help_texts = {
            'opening_ecash': 'Used as your starting e-cash wallet for the first setup.',
            'opening_physical_cash': 'Used as your starting notes/coins balance for the first setup.',
        }

    def save(self, commit=True):
        profile = super().save(commit=False)
        should_sync = not profile.pk or not profile.transactions.exists()
        if should_sync:
            profile.sync_current_to_opening()
        if commit:
            profile.save()
        return profile


class TransactionForm(forms.Form):
    client_name = forms.CharField(max_length=120, widget=forms.TextInput(attrs={'class': INPUT_CLASS, 'placeholder': 'Client name'}))
    amount = forms.DecimalField(max_digits=12, decimal_places=2, min_value=Decimal('0.01'), widget=forms.NumberInput(attrs={'class': INPUT_CLASS, 'step': '0.01', 'min': '0.01'}))
    transaction_type = forms.ChoiceField(choices=Transaction.TYPE_CHOICES, widget=forms.Select(attrs={'class': SELECT_CLASS}))
    notes = forms.CharField(required=False, widget=forms.Textarea(attrs={'class': 'form-control momo-input', 'rows': 3, 'placeholder': 'Optional note about the transaction'}))

    def __init__(self, *args, business=None, instance=None, **kwargs):
        self.business = business
        self.instance = instance
        super().__init__(*args, **kwargs)

    def clean(self):
        cleaned_data = super().clean()
        transaction_type = cleaned_data.get('transaction_type')
        amount = cleaned_data.get('amount')
        if not self.business or not transaction_type or amount is None:
            return cleaned_data

        if self.instance is not None:
            return cleaned_data

        ecash_delta, physical_delta, _ = Transaction.calculate_effect(transaction_type, amount)
        if self.business.current_ecash + ecash_delta < 0:
            self.add_error('amount', 'Not enough e-cash for this transaction.')
        if self.business.current_physical_cash + physical_delta < 0:
            self.add_error('amount', 'Not enough physical cash for this transaction.')
        return cleaned_data

    def save(self, user):
        if not self.business:
            raise ValidationError('Business profile is required.')
        if self.instance is not None:
            return self.instance.update_logged_transaction(
                client_name=self.cleaned_data['client_name'],
                amount=self.cleaned_data['amount'],
                transaction_type=self.cleaned_data['transaction_type'],
                notes=self.cleaned_data['notes'],
            )
        return Transaction.create_logged_transaction(
            business=self.business,
            user=user,
            client_name=self.cleaned_data['client_name'],
            amount=self.cleaned_data['amount'],
            transaction_type=self.cleaned_data['transaction_type'],
            notes=self.cleaned_data['notes'],
        )


class ReportFilterForm(forms.Form):
    PERIOD_CHOICES = [
        ('daily', 'Daily'),
        ('weekly', 'Weekly'),
        ('monthly', 'Monthly'),
        ('yearly', 'Yearly'),
        ('custom', 'Custom range'),
    ]

    period = forms.ChoiceField(choices=PERIOD_CHOICES, initial='daily', widget=forms.Select(attrs={'class': 'form-select momo-input'}))
    start_date = forms.DateField(required=False, widget=forms.DateInput(attrs={'class': 'form-control momo-input', 'type': 'date'}))
    end_date = forms.DateField(required=False, widget=forms.DateInput(attrs={'class': 'form-control momo-input', 'type': 'date'}))

    def clean(self):
        cleaned_data = super().clean()
        period = cleaned_data.get('period')
        start_date = cleaned_data.get('start_date')
        end_date = cleaned_data.get('end_date')
        if period == 'custom':
            if not start_date or not end_date:
                raise forms.ValidationError('Choose both a start and end date for a custom report.')
            if end_date < start_date:
                raise forms.ValidationError('End date cannot be before start date.')
        return cleaned_data

    def get_range(self):
        today = timezone.localdate()
        period = self.cleaned_data.get('period') or 'daily'
        if period == 'daily':
            return today, today
        if period == 'weekly':
            start = today - timezone.timedelta(days=today.weekday())
            return start, start + timezone.timedelta(days=6)
        if period == 'monthly':
            start = today.replace(day=1)
            if start.month == 12:
                next_month = start.replace(year=start.year + 1, month=1, day=1)
            else:
                next_month = start.replace(month=start.month + 1, day=1)
            return start, next_month - timezone.timedelta(days=1)
        if period == 'yearly':
            start = today.replace(month=1, day=1)
            return start, today.replace(month=12, day=31)
        return self.cleaned_data['start_date'], self.cleaned_data['end_date']
