# -*- coding: utf-8 -*-
# Copyright (C) 2014-2017 Andrey Antukh <niwi@niwi.nz>
# Copyright (C) 2014-2017 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2014-2017 David Barragán <bameda@dbarragan.com>
# Copyright (C) 2014-2017 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from taiga.base.exceptions import ValidationError
from taiga.base.utils import db
from taiga.users.models import User

from django.conf import settings
from django.core.validators import validate_email
from django.utils.translation import ugettext as _

from .. import models


def get_members_from_bulk(bulk_data, **additional_fields):
    """Convert `bulk_data` into a list of members.

    :param bulk_data: List of members in bulk format.
    :param additional_fields: Additional fields when instantiating each task.

    :return: List of `Member` instances.
    """
    members = []
    for data in bulk_data:
        data_copy = data.copy()
        username = data_copy.pop("username")
        try:
            validate_email(username)
            data_copy["email"] = username

        except ValidationError:
            user = User.objects.filter(username=username).first()
            data_copy["user_id"] = user.id

        data_copy.update(additional_fields)
        members.append(models.Membership(**data_copy))
    return members


def create_members_in_bulk(bulk_data, callback=None, precall=None, **additional_fields):
    """Create members from `bulk_data`.

    :param bulk_data: List of dicts `{"project_id": <>, "role_id": <>, "username": <>}`.
    :param callback: Callback to execute after each task save.
    :param additional_fields: Additional fields when instantiating each task.

    :return: List of created `Member` instances.
    """
    members = get_members_from_bulk(bulk_data, **additional_fields)
    db.save_in_bulk(members, callback, precall)
    return members


def remove_user_from_project(user, project):
    models.Membership.objects.get(project=project, user=user).delete()


def project_has_valid_admins(project, exclude_user=None):
    """
    Checks if the project has any owner membership with a user different than the specified
    """
    admin_memberships = project.memberships.filter(is_admin=True, user__is_active=True)
    if exclude_user:
        admin_memberships = admin_memberships.exclude(user=exclude_user)

    return admin_memberships.count() > 0


def can_user_leave_project(user, project):
    membership = project.memberships.get(user=user)
    if not membership.is_admin:
        return True

    # The user can't leave if is the real owner of the project
    if project.owner == user:
        return False

    if not project_has_valid_admins(project, exclude_user=user):
        return False

    return True


def get_max_memberships_for_project(project):
    """Return tha maximun of membersh for a concrete project.

    :param project: A project object.

    :return: a number or null.
    """
    if project.owner is None:
        return None

    if project.is_private:
        return project.owner.max_memberships_private_projects
    return project.owner.max_memberships_public_projects


def get_total_project_memberships(project):
    """Return tha total of memberships of a project (members and unaccepted invitations).

    :param project: A project object.

    :return: a number.
    """
    return project.memberships.count()


def check_if_project_can_have_more_memberships(project, total_new_memberships):
    """Return if a project can have more n new memberships.

    :param project: A project object.
    :param total_new_memberships: the total of new memberships to add (int).

    :return: {bool, error_mesage} return a tuple (can add new members?, error message).
    """
    if project.owner is None:
        return False, _("Project without owner")

    if project.is_private:
        total_memberships = project.memberships.count() + total_new_memberships
        max_memberships = project.owner.max_memberships_private_projects
        error_members_exceeded = _("You have reached your current limit of memberships for private projects")
    else:
        total_memberships = project.memberships.count() + total_new_memberships
        max_memberships = project.owner.max_memberships_public_projects
        error_members_exceeded = _("You have reached your current limit of memberships for public projects")

    if max_memberships is not None and total_memberships > max_memberships:
        return False, error_members_exceeded

    if project.memberships.filter(user=None).count() + total_new_memberships > settings.MAX_PENDING_MEMBERSHIPS:
        error_pending_memberships_exceeded = _("You have reached the current limit of pending memberships")
        return False, error_pending_memberships_exceeded

    return True, None
