# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Django form for configuring Gitweb.
"""

import json
import re
from urllib.parse import urlparse

from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from django.utils.translation import gettext_lazy as _

from plinth import actions
from plinth.modules import gitweb


def _get_branches(repo):
    """Get all the branches in the repository."""
    branch_data = json.loads(
        actions.run('gitweb', ['get-branches', '--name', repo]))
    default_branch = branch_data['default_branch']
    branches = branch_data['branches']

    if default_branch not in branches:
        branches.insert(0, default_branch)

    return [(branch, branch) for branch in branches]


def get_name_from_url(url):
    """Get a repository name from URL"""
    return urlparse(url).path.split('/')[-1]


def is_repo_url(url):
    """Check if URL is valid."""
    try:
        RepositoryValidator(input_should_be='url')(url)
    except ValidationError:
        return False

    return True


class RepositoryValidator:
    input_should_be = 'name'

    def __init__(self, input_should_be=None):
        if input_should_be is not None:
            self.input_should_be = input_should_be

    def __call__(self, value):
        """Validate that the input is a correct repository name or URL"""
        if self.input_should_be in ('url', 'url_or_name'):
            try:
                URLValidator(schemes=['http', 'https'],
                             message=_('Invalid repository URL.'))(value)
            except ValidationError:
                if self.input_should_be == 'url':
                    raise
            else:
                value = get_name_from_url(value)

        if (not re.match(r'^[a-zA-Z0-9-._]+$', value)) \
                or value.startswith(('-', '.')) \
                or value.endswith('.git.git'):
            raise ValidationError(_('Invalid repository name.'), 'invalid')


class CreateRepoForm(forms.Form):
    """Form to create and edit a new repository."""

    name = forms.CharField(
        label=_(
            'Name of a new repository or URL to import an existing repository.'
        ), strip=True,
        validators=[RepositoryValidator(input_should_be='url_or_name')],
        widget=forms.TextInput(attrs={'autocomplete': 'off'}))

    description = forms.CharField(
        label=_('Description of the repository'), strip=True, required=False,
        help_text=_('Optional, for displaying on Gitweb.'))

    owner = forms.CharField(label=_('Repository\'s owner name'), strip=True,
                            required=False,
                            help_text=_('Optional, for displaying on Gitweb.'))

    is_private = forms.BooleanField(
        label=_('Private repository'), required=False,
        help_text=_('Allow only authorized users to access this repository.'))

    def __init__(self, *args, **kwargs):
        """Initialize the form with extra request argument."""
        super().__init__(*args, **kwargs)
        self.fields['name'].widget.attrs.update({'autofocus': 'autofocus'})

    def clean_name(self):
        """Check if the name is valid."""
        name = self.cleaned_data['name']

        repo_name = name
        if is_repo_url(name):
            repo_name = get_name_from_url(name)

        if repo_name.endswith('.git'):
            repo_name = repo_name[:-4]

        for repo in gitweb.get_repo_list():
            if repo_name == repo['name']:
                raise ValidationError(
                    _('A repository with this name already exists.'))

        if is_repo_url(name):
            if not gitweb.repo_exists(name):
                raise ValidationError('Remote repository is not available.')

        return name


class EditRepoForm(CreateRepoForm):
    """Form to create and edit a new repository."""

    name = forms.CharField(
        label=_('Name of the repository'),
        strip=True,
        validators=[RepositoryValidator()],
        help_text=_(
            'An alpha-numeric string that uniquely identifies a repository.'),
    )

    default_branch = forms.ChoiceField(
        label=_('Default branch'),
        help_text=_('Gitweb displays this as a default branch.'))

    def __init__(self, *args, **kwargs):
        """Initialize the form with extra request argument."""
        super().__init__(*args, **kwargs)
        branches = _get_branches(self.initial['name'])
        self.fields['default_branch'].choices = branches

    def clean_name(self):
        """Check if the name is valid."""
        name = self.cleaned_data['name']
        if 'name' in self.initial and name == self.initial['name']:
            return name

        if name.endswith('.git'):
            name = name[:-4]

        for repo in gitweb.get_repo_list():
            if name == repo['name']:
                raise ValidationError(
                    _('A repository with this name already exists.'))

        return name
