from django import forms
from django.contrib.auth.models import User

from .delivery import apply_profile_delivery_fields, derive_location_label, phone_has_valid_digits
from .models import Profile


class DeliveryLocationValidationMixin:
    def clean_phone(self):
        phone = (self.cleaned_data.get('phone') or '').strip()
        if not phone_has_valid_digits(phone):
            raise forms.ValidationError('Please enter a valid phone number.')
        return phone

    def clean_default_address(self):
        address = (self.cleaned_data.get('default_address') or '').strip()
        if not address:
            raise forms.ValidationError('Please enter your delivery address.')
        return address

    def clean(self):
        cleaned_data = super().clean()
        latitude = cleaned_data.get('latitude')
        longitude = cleaned_data.get('longitude')

        if (latitude is None) != (longitude is None):
            raise forms.ValidationError('GPS location is incomplete. Please retry the location button or save without GPS.')

        if latitude is not None and not (-90 <= latitude <= 90):
            raise forms.ValidationError('Latitude is outside the valid range.')

        if longitude is not None and not (-180 <= longitude <= 180):
            raise forms.ValidationError('Longitude is outside the valid range.')

        location_accuracy_m = cleaned_data.get('location_accuracy_m')
        if location_accuracy_m is not None and location_accuracy_m < 0:
            raise forms.ValidationError('Location accuracy cannot be negative.')

        if cleaned_data.get('default_address') and not cleaned_data.get('location_label'):
            cleaned_data['location_label'] = derive_location_label('', cleaned_data['default_address'])

        return cleaned_data

    def apply_location_to_profile(self, profile):
        return apply_profile_delivery_fields(
            profile,
            phone=self.cleaned_data.get('phone', ''),
            location_label=self.cleaned_data.get('location_label', ''),
            address=self.cleaned_data.get('default_address', ''),
            latitude=self.cleaned_data.get('latitude'),
            longitude=self.cleaned_data.get('longitude'),
            location_accuracy_m=self.cleaned_data.get('location_accuracy_m'),
        )


class RegistrationForm(DeliveryLocationValidationMixin, forms.Form):
    username = forms.CharField(
        min_length=3,
        max_length=150,
        widget=forms.TextInput(
            attrs={
                'placeholder': 'Choose a username',
                'autocomplete': 'username',
            }
        ),
    )
    first_name = forms.CharField(
        required=False,
        max_length=30,
        widget=forms.TextInput(attrs={'placeholder': 'First name', 'autocomplete': 'given-name'}),
    )
    last_name = forms.CharField(
        required=False,
        max_length=150,
        widget=forms.TextInput(attrs={'placeholder': 'Last name', 'autocomplete': 'family-name'}),
    )
    email = forms.EmailField(
        required=False,
        widget=forms.EmailInput(attrs={'placeholder': 'your@email.com', 'autocomplete': 'email'}),
    )
    password = forms.CharField(
        min_length=6,
        widget=forms.PasswordInput(attrs={'placeholder': 'At least 6 characters', 'autocomplete': 'new-password'}),
    )
    confirm_password = forms.CharField(
        min_length=6,
        widget=forms.PasswordInput(attrs={'placeholder': 'Confirm your password', 'autocomplete': 'new-password'}),
    )
    phone = forms.CharField(
        max_length=30,
        required=True,
        widget=forms.TextInput(attrs={'placeholder': 'Phone number', 'autocomplete': 'tel'}),
    )
    location_label = forms.CharField(
        max_length=255,
        required=False,
        widget=forms.TextInput(
            attrs={
                'placeholder': 'Area / city / delivery label',
                'autocomplete': 'address-level2',
            }
        ),
    )
    default_address = forms.CharField(
        required=True,
        widget=forms.Textarea(
            attrs={
                'rows': 4,
                'placeholder': 'Street, city, area, landmark',
                'autocomplete': 'street-address',
            }
        ),
    )
    latitude = forms.DecimalField(required=False, max_digits=9, decimal_places=6, widget=forms.HiddenInput())
    longitude = forms.DecimalField(required=False, max_digits=9, decimal_places=6, widget=forms.HiddenInput())
    location_accuracy_m = forms.DecimalField(required=False, max_digits=8, decimal_places=2, widget=forms.HiddenInput())
    register_as_seller = forms.BooleanField(required=False)

    def clean_username(self):
        username = (self.cleaned_data.get('username') or '').strip()
        if User.objects.filter(username__iexact=username).exists():
            raise forms.ValidationError('Username already exists.')
        return username

    def clean_email(self):
        email = (self.cleaned_data.get('email') or '').strip()
        if email and User.objects.filter(email__iexact=email).exists():
            raise forms.ValidationError('Email already registered.')
        return email

    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data.get('password')
        confirm_password = cleaned_data.get('confirm_password')

        if password and confirm_password and password != confirm_password:
            self.add_error('confirm_password', 'Passwords do not match.')

        return cleaned_data

    def save(self):
        user = User.objects.create_user(
            username=self.cleaned_data['username'],
            password=self.cleaned_data['password'],
            email=self.cleaned_data.get('email', ''),
        )
        user.first_name = self.cleaned_data.get('first_name', '').strip()
        user.last_name = self.cleaned_data.get('last_name', '').strip()
        user.save(update_fields=['first_name', 'last_name', 'email'])

        profile = user.profile
        self.apply_location_to_profile(profile)
        profile.is_seller = self.cleaned_data.get('register_as_seller', False)
        profile.save()
        return user


class ProfileForm(DeliveryLocationValidationMixin, forms.ModelForm):
    first_name = forms.CharField(required=False, max_length=30)
    last_name = forms.CharField(required=False, max_length=150)
    email = forms.EmailField(required=False)
    phone = forms.CharField(
        max_length=30,
        required=True,
        widget=forms.TextInput(attrs={'placeholder': 'Phone number', 'autocomplete': 'tel'}),
    )
    location_label = forms.CharField(
        max_length=255,
        required=False,
        widget=forms.TextInput(
            attrs={
                'placeholder': 'Area / city / delivery label',
                'autocomplete': 'address-level2',
            }
        ),
    )
    default_address = forms.CharField(
        required=True,
        widget=forms.Textarea(
            attrs={
                'rows': 4,
                'placeholder': 'Street, city, area, landmark',
                'autocomplete': 'street-address',
            }
        ),
    )
    latitude = forms.DecimalField(required=False, max_digits=9, decimal_places=6, widget=forms.HiddenInput())
    longitude = forms.DecimalField(required=False, max_digits=9, decimal_places=6, widget=forms.HiddenInput())
    location_accuracy_m = forms.DecimalField(required=False, max_digits=8, decimal_places=2, widget=forms.HiddenInput())

    class Meta:
        model = Profile
        fields = ['image', 'bio', 'phone', 'location_label', 'default_address', 'latitude', 'longitude', 'location_accuracy_m']
        widgets = {
            'bio': forms.Textarea(attrs={'rows': 5, 'placeholder': 'Tell shoppers something about yourself'}),
        }

    def __init__(self, *args, user=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.user = user or getattr(self.instance, 'user', None)
        if self.user:
            self.fields['first_name'].initial = self.user.first_name
            self.fields['last_name'].initial = self.user.last_name
            self.fields['email'].initial = self.user.email

    def clean_email(self):
        email = (self.cleaned_data.get('email') or '').strip()
        if email and User.objects.exclude(pk=getattr(self.user, 'pk', None)).filter(email__iexact=email).exists():
            raise forms.ValidationError('That email is already being used by another account.')
        return email

    def save(self, commit=True):
        profile = super().save(commit=False)
        self.apply_location_to_profile(profile)

        user = self.user or profile.user
        user.first_name = self.cleaned_data.get('first_name', '').strip()
        user.last_name = self.cleaned_data.get('last_name', '').strip()
        user.email = self.cleaned_data.get('email', '').strip()

        if commit:
            user.save()
            profile.save()
        return profile


class DeliveryPreferencesForm(DeliveryLocationValidationMixin, forms.ModelForm):
    phone = forms.CharField(
        max_length=30,
        required=True,
        widget=forms.TextInput(attrs={'placeholder': 'Phone number', 'autocomplete': 'tel'}),
    )
    location_label = forms.CharField(
        max_length=255,
        required=False,
        widget=forms.TextInput(
            attrs={
                'placeholder': 'Area / city / delivery label',
                'autocomplete': 'address-level2',
            }
        ),
    )
    default_address = forms.CharField(
        required=True,
        widget=forms.Textarea(
            attrs={
                'rows': 4,
                'placeholder': 'Street, city, area, landmark',
                'autocomplete': 'street-address',
            }
        ),
    )
    latitude = forms.DecimalField(required=False, max_digits=9, decimal_places=6, widget=forms.HiddenInput())
    longitude = forms.DecimalField(required=False, max_digits=9, decimal_places=6, widget=forms.HiddenInput())
    location_accuracy_m = forms.DecimalField(required=False, max_digits=8, decimal_places=2, widget=forms.HiddenInput())

    class Meta:
        model = Profile
        fields = ['phone', 'location_label', 'default_address', 'latitude', 'longitude', 'location_accuracy_m']

    def save(self, commit=True):
        profile = super().save(commit=False)
        self.apply_location_to_profile(profile)
        if commit:
            profile.save()
        return profile
