"""Project forms"""

from random import choice
from urlparse import urlparse

from django import forms
from django.conf import settings
from django.contrib.auth.models import User
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from django.utils.safestring import mark_safe
from textclassifier.validators import ClassifierValidator

from guardian.shortcuts import assign

from readthedocs.builds.constants import TAG
from readthedocs.core.utils import trigger_build, slugify
from readthedocs.redirects.models import Redirect
from readthedocs.projects import constants
from readthedocs.projects.exceptions import ProjectSpamError
from readthedocs.projects.models import Project, EmailHook, WebHook, Domain
from readthedocs.privacy.loader import AdminPermission


class ProjectForm(forms.ModelForm):

    """Project form

    :param user: If provided, add this user as a project user on save
    """

    required_css_class = "required"

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super(ProjectForm, self).__init__(*args, **kwargs)

    def save(self, commit=True):
        project = super(ProjectForm, self).save(commit)
        if commit:
            if self.user and not project.users.filter(pk=self.user.pk).exists():
                project.users.add(self.user)
        return project


class ProjectTriggerBuildMixin(object):

    """Mixin to trigger build on form save

    This should be replaced with signals instead of calling trigger_build
    explicitly.
    """

    def save(self, commit=True):
        """Trigger build on commit save"""
        project = super(ProjectTriggerBuildMixin, self).save(commit)
        if commit:
            trigger_build(project=project)
        return project


class ProjectBackendForm(forms.Form):

    """Get the import backend"""

    backend = forms.CharField()


class ProjectBasicsForm(ProjectForm):

    """Form for basic project fields"""

    class Meta:
        model = Project
        fields = ('name', 'repo', 'repo_type')

    def __init__(self, *args, **kwargs):
        show_advanced = kwargs.pop('show_advanced', False)
        super(ProjectBasicsForm, self).__init__(*args, **kwargs)
        if show_advanced:
            self.fields['advanced'] = forms.BooleanField(
                required=False,
                label=_('Edit advanced project options')
            )
        self.fields['repo'].widget.attrs['placeholder'] = self.placehold_repo()
        self.fields['repo'].widget.attrs['required'] = True

    def clean_name(self):
        name = self.cleaned_data.get('name', '')
        if not self.instance.pk:
            potential_slug = slugify(name)
            if Project.objects.filter(slug=potential_slug).exists():
                raise forms.ValidationError(
                    _('Invalid project name, a project already exists with that name'))
        return name

    def clean_repo(self):
        repo = self.cleaned_data.get('repo', '').strip()
        pvt_repos = getattr(settings, 'ALLOW_PRIVATE_REPOS', False)
        if '&&' in repo or '|' in repo:
            raise forms.ValidationError(_(u'Invalid character in repo name'))
        elif '@' in repo and not pvt_repos:
            raise forms.ValidationError(
                _(u'It looks like you entered a private repo - please use the '
                  u'public (http:// or git://) clone url'))
        return repo

    def placehold_repo(self):
        return choice([
            'https://bitbucket.org/cherrypy/cherrypy',
            'https://bitbucket.org/birkenfeld/sphinx',
            'https://bitbucket.org/hpk42/tox',
            'https://github.com/zzzeek/sqlalchemy.git',
            'https://github.com/django/django.git',
            'https://github.com/fabric/fabric.git',
            'https://github.com/ericholscher/django-kong.git',
        ])


class ProjectExtraForm(ProjectForm):

    """Additional project information form"""

    class Meta:
        model = Project
        fields = (
            'description',
            'documentation_type',
            'language', 'programming_language',
            'project_url',
            'tags',
        )

        description = forms.CharField(
           validators=[ClassifierValidator(raises=ProjectSpamError)],
           required=False,
           widget=forms.Textarea
        )


class ProjectAdvancedForm(ProjectTriggerBuildMixin, ProjectForm):

    """Advanced project option form"""
   

    python_interpreter = forms.ChoiceField(
        label=_('Python Interpreter'),
        choices=constants.PYTHON_CHOICES, initial='python',
        help_text=_("(Beta) The Python interpreter used to create the virtual "
                     "environment."))

    class Meta:
        model = Project
        fields = (
            # Standard build edits
            'install_project',
            'requirements_file',
            'single_version',
            'conf_py_file',
            'default_branch',
            'default_version',
            'enable_pdf_build',
            'enable_epub_build',
            # Privacy
            'privacy_level',
            # 'version_privacy_level',
            # Python specific
            'use_system_packages',
            'python_interpreter',
            # Fringe
            'analytics_code',
            # Version Support
            # 'num_major', 'num_minor', 'num_point',
        )

    def clean_conf_py_file(self):
        filename = self.cleaned_data.get('conf_py_file', '').strip()
        if filename and 'conf.py' not in filename:
            raise forms.ValidationError(
                _('Your configuration file is invalid, make sure it contains '
                  'conf.py in it.'))
        return filename


class UpdateProjectForm(ProjectTriggerBuildMixin, ProjectBasicsForm,
                        ProjectExtraForm):

    class Meta:
        model = Project
        fields = (
            # Basics
            'name', 'repo', 'repo_type',
            # Extra
            # 'allow_comments',
            # 'comment_moderation',
            'description',
            'documentation_type',
            'language', 'programming_language',
            'project_url',
            'tags',
            'document_type',
        )


class DualCheckboxWidget(forms.CheckboxInput):

    """Checkbox with link to the version's built documentation"""

    def __init__(self, version, attrs=None, check_test=bool):
        super(DualCheckboxWidget, self).__init__(attrs, check_test)
        self.version = version

    def render(self, name, value, attrs=None):
        checkbox = super(DualCheckboxWidget, self).render(name, value, attrs)
        icon = self.render_icon()
        return mark_safe(u'%s%s' % (checkbox, icon))

    def render_icon(self):
        context = {
            'MEDIA_URL': settings.MEDIA_URL,
            'built': self.version.built,
            'uploaded': self.version.uploaded,
            'url': self.version.get_absolute_url()
        }
        return render_to_string('projects/includes/icon_built.html', context)


class BaseVersionsForm(forms.Form):

    """Form for versions page"""

    def save(self):
        versions = self.project.versions.all()
        for version in versions:
            self.save_version(version)
        default_version = self.cleaned_data.get('default-version', None)
        if default_version:
            self.project.default_version = default_version
            self.project.save()

    def save_version(self, version):
        """Save version if there has been a change, trigger a rebuild"""
        new_value = self.cleaned_data.get('version-%s' % version.slug, None)
        privacy_level = self.cleaned_data.get('privacy-%s' % version.slug,
                                              None)
        if ((new_value is None or
             new_value == version.active) and (
                 privacy_level is None or
                 privacy_level == version.privacy_level)):
            return
        version.active = new_value
        version.privacy_level = privacy_level
        version.save()
        if version.active and not version.built and not version.uploaded:
            trigger_build(project=self.project, version=version)


def build_versions_form(project):
    """Versions form with a list of versions and version privacy levels"""
    attrs = {
        'project': project,
    }
    versions_qs = project.versions.all()  # Admin page, so show all versions
    active = versions_qs.filter(active=True)
    if active.exists():
        choices = [(version.slug, version.verbose_name) for version in active]
        attrs['default-version'] = forms.ChoiceField(
            label=_("Default Version"),
            choices=choices,
            initial=project.get_default_version(),
        )
    for version in versions_qs:
        field_name = 'version-%s' % version.slug
        privacy_name = 'privacy-%s' % version.slug
        if version.type == TAG:
            label = "%s (%s)" % (version.verbose_name, version.identifier[:8])
        else:
            label = version.verbose_name
        attrs[field_name] = forms.BooleanField(
            label=label,
            widget=DualCheckboxWidget(version),
            initial=version.active,
            required=False,
        )
        attrs[privacy_name] = forms.ChoiceField(
            # This isn't a real label, but just a slug for the template
            label="privacy",
            choices=constants.PRIVACY_CHOICES,
            initial=version.privacy_level,
        )
    return type('VersionsForm', (BaseVersionsForm,), attrs)


class BaseUploadHTMLForm(forms.Form):
    content = forms.FileField(label=_("Zip file of HTML"))
    overwrite = forms.BooleanField(required=False,
                                   label=_("Overwrite existing HTML?"))

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