import re
from django import forms
from django.contrib.auth.models import User
from django.conf import settings
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _
from django.utils.safestring import mark_safe
from askbot.conf import settings as askbot_settings
from askbot.utils.slug import slugify
from askbot.utils.functions import split_list
from askbot import const
from longerusername import MAX_USERNAME_LENGTH
import logging
import urllib

DEFAULT_NEXT = '/' + getattr(settings, 'ASKBOT_URL')
def clean_next(next, default = None):
    if next is None or not next.startswith('/'):
        if default:
            return default
        else:
            return DEFAULT_NEXT
    if isinstance(next, str):
        next = unicode(urllib.unquote(next), 'utf-8', 'replace')
    next = next.strip()
    logging.debug('next url is %s' % next)
    return next

def get_next_url(request, default = None):
    return clean_next(request.REQUEST.get('next'), default)

def get_db_object_or_404(params):
    """a utility function that returns an object
    in return to the model_name and object_id

    only specific models are accessible
    """
    from askbot import models
    try:
        model_name = params['model_name']
        assert(model_name=='Group')
        model = models.get_model(model_name)
        obj_id = forms.IntegerField().clean(params['object_id'])
        return get_object_or_404(model, id=obj_id)
    except Exception:
        #need catch-all b/c of the nature of the function
        raise Http404

def format_errors(error_list):
    """If there is only one error - returns a string
    corresponding to that error, to remove the <ul> tag.

    If there is > 1 error - then convert the error_list into
    a string.
    """
    if len(error_list) == 1:
        return unicode(error_list[0])
    else:
        return unicode(error_list)

class StrippedNonEmptyCharField(forms.CharField):
    def clean(self, value):
        value = value.strip()
        if self.required and value == '':
            raise forms.ValidationError(_('this field is required'))
        return value

class NextUrlField(forms.CharField):
    def __init__(self):
        super(
            NextUrlField,
            self
        ).__init__(
            max_length = 255,
            widget = forms.HiddenInput(),
            required = False
        )
    def clean(self,value):
        return clean_next(value)

login_form_widget_attrs = { 'class': 'required login' }

class UserNameField(StrippedNonEmptyCharField):
    RESERVED_NAMES = (u'fuck', u'shit', u'ass', u'sex', u'add',
                       u'edit', u'save', u'delete', u'manage', u'update', 'remove', 'new')
    def __init__(
        self,
        db_model=User,
        db_field='username',
        must_exist=False,
        skip_clean=False,
        label=_('Choose a screen name'),
        widget_attrs=None,
        **kw
    ):
        self.must_exist = must_exist
        self.skip_clean = skip_clean
        self.db_model = db_model 
        self.db_field = db_field
        self.user_instance = None
        error_messages={
            'required': _('user name is required'),
            'taken': _('sorry, this name is taken, please choose another'),
            'forbidden': _('sorry, this name is not allowed, please choose another'),
            'missing': _('sorry, there is no user with this name'),
            'multiple-taken': _('sorry, we have a serious error - user name is taken by several users'),
            'invalid': _('user name can only consist of letters, empty space and underscore'),
            'meaningless': _('please use at least some alphabetic characters in the user name'),
            'noemail': _('symbol "@" is not allowed')
        }
        if 'error_messages' in kw:
            error_messages.update(kw['error_messages'])
            del kw['error_messages']

        if widget_attrs:
            widget_attrs.update(login_form_widget_attrs)
        else:
            widget_attrs = login_form_widget_attrs

        max_length = MAX_USERNAME_LENGTH()
        super(UserNameField,self).__init__(
                max_length=max_length,
                widget=forms.TextInput(attrs=widget_attrs),
                label=label,
                error_messages=error_messages,
                **kw
            )

    def clean(self,username):
        """ validate username """
        if self.skip_clean == True:
            logging.debug('username accepted with no validation')
            return username
        if self.user_instance is None:
            pass
        elif isinstance(self.user_instance, User):
            if username == self.user_instance.username:
                logging.debug('username valid')
                return username
        else:
            raise TypeError('user instance must be of type User')

        try:
            username = super(UserNameField, self).clean(username)
        except forms.ValidationError:
            raise forms.ValidationError(self.error_messages['required'])

        username_re_string = const.USERNAME_REGEX_STRING
        #attention: here we check @ symbol in two places: input and the regex
        if askbot_settings.ALLOW_EMAIL_ADDRESS_IN_USERNAME is False:
            if '@' in username:
                raise forms.ValidationError(self.error_messages['noemail'])

            username_re_string = username_re_string.replace('@', '')

        username_regex = re.compile(username_re_string, re.UNICODE)

        if self.required and not username_regex.search(username):
            raise forms.ValidationError(self.error_messages['invalid'])
        if username in self.RESERVED_NAMES:
            raise forms.ValidationError(self.error_messages['forbidden'])
        if slugify(username) == '':
            raise forms.ValidationError(self.error_messages['meaningless'])
        try:
            user = self.db_model.objects.get(
                    **{'%s' % self.db_field : username}
            )
            if user:
                if self.must_exist:
                    logging.debug('user exists and name accepted b/c here we validate existing user')
                    return username
                else:
                    raise forms.ValidationError(self.error_messages['taken'])
        except self.db_model.DoesNotExist:
            if self.must_exist:
                logging.debug('user must exist, so raising the error')
                raise forms.ValidationError(self.error_messages['missing'])
            else:
                logging.debug('user name valid!')
                return username
        except self.db_model.MultipleObjectsReturned:
            logging.debug('error - user with this name already exists')
            raise forms.ValidationError(self.error_messages['multiple-taken'])


def email_is_allowed(
    email, allowed_emails='', allowed_email_domains=''
):
    """True, if email address is pre-approved or matches a allowed
    domain"""
    if allowed_emails:
        email_list = split_list(allowed_emails)
        allowed_emails = ' ' + ' '.join(email_list) + ' '
        email_match_re = re.compile(r'\s%s\s' % email)
        if email_match_re.search(allowed_emails):
            return True

    if allowed_email_domains:
        email_domain = email.split('@')[1]
        domain_list = split_list(allowed_email_domains)
        domain_match_re = re.compile(r'\s%s\s' % email_domain)
        allowed_email_domains = ' ' + ' '.join(domain_list) + ' '
        return domain_match_re.search(allowed_email_domains)

    return False

class UserEmailField(forms.EmailField):
    def __init__(self, skip_clean=False, **kw):
        self.skip_clean = skip_clean

        hidden = kw.pop('hidden', False)
        if hidden is True:
            widget_class = forms.HiddenInput
        else:
            widget_class = forms.TextInput

        super(UserEmailField,self).__init__(
            widget=widget_class(
                    attrs=dict(login_form_widget_attrs, maxlength=200)
                ),
            label=mark_safe(_('Your email <i>(never shared)</i>')),
            error_messages={
                'required':_('email address is required'),
                'invalid':_('please enter a valid email address'),
                'taken':_('this email is already used by someone else, please choose another'),
                'unauthorized':_('this email address is not authorized')
            },
            **kw
        )

    def clean(self, email):
        """ validate if email exist in database
        from legacy register
        return: raise error if it exist """
        email = super(UserEmailField,self).clean(email.strip())
        if self.skip_clean:
            return email
        
        allowed_domains = askbot_settings.ALLOWED_EMAIL_DOMAINS.strip()
        allowed_emails = askbot_settings.ALLOWED_EMAILS.strip()

        if allowed_emails or allowed_domains:
            if not email_is_allowed(
                    email,
                    allowed_emails=allowed_emails,
                    allowed_email_domains=allowed_domains
                ):
                raise forms.ValidationError(self.error_messages['unauthorized'])

        try:
            user = User.objects.get(email__iexact=email)
            logging.debug('email taken')
            raise forms.ValidationError(self.error_messages['taken'])
        except User.DoesNotExist:
            logging.debug('email valid')
            return email
        except User.MultipleObjectsReturned:
            logging.critical('email taken many times over')
            raise forms.ValidationError(self.error_messages['taken'])

class SetPasswordForm(forms.Form):
    password1 = forms.CharField(widget=forms.PasswordInput(attrs=login_form_widget_attrs),
                                label=_('Password'),
                                error_messages={'required':_('password is required')},
                                )
    password2 = forms.CharField(widget=forms.PasswordInput(attrs=login_form_widget_attrs),
                                label=mark_safe(_('Password <i>(please retype)</i>')),
                                error_messages={'required':_('please, retype your password'),
                                                'nomatch':_('entered passwords did not match, please try again')},
                                )

    def __init__(self, data=None, user=None, *args, **kwargs):
        super(SetPasswordForm, self).__init__(data, *args, **kwargs)

    def clean_password2(self):
        """
        Validates that the two password inputs match.
        
        """
        if 'password1' in self.cleaned_data:
            if self.cleaned_data['password1'] == self.cleaned_data['password2']:
                self.password = self.cleaned_data['password2']
                self.cleaned_data['password'] = self.cleaned_data['password2']
                return self.cleaned_data['password2']
            else:
                del self.cleaned_data['password2']
                raise forms.ValidationError(self.fields['password2'].error_messages['nomatch'])
        else:
            return self.cleaned_data['password2']

