# -*- coding: utf-8 -*-

# Copyright 2016 Mir Calculate. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import sys
from calculate.lib.datavars import (ReadonlyVariable, Variable,
                                    VariableError, VariableInterface)
from calculate.ldap.variables.helpers import (LdapSearchHelper, LdapMaxHelper)
from calculate.unix.variables.helpers import (UnixUserHelper, UnixGroupHelper,
                                              UnixActionHelper)
from .action import Actions
import re

_ = lambda x: x
from calculate.lib.cl_lang import (setLocalTranslate, getLazyLocalTranslate)

setLocalTranslate('cl_unix3', sys.modules[__name__])
__ = getLazyLocalTranslate(_)

class GroupNameHelper(VariableInterface):
    """
    Вспомогательный объект для проверки имени группы
    """
    def check_group_name(self, gname):
        if len(gname) > 32:
            raise VariableError(_("Wrong group name length"))
        if ":" in gname:
            raise VariableError(_("Wrong group name"))

class VariableUrUnixGroupName(GroupNameHelper,
                              LdapSearchHelper, UnixActionHelper, Variable):
    """
    Название группы
    """
    base_dn = "{ld_unix_groups_dn}"
    search_filter = '(cn={value})'

    untrusted = True
    opt = ("ur_unix_group_name",)
    metavalue = "GROUP"

    def init(self):
        self.label = _("Group")
        self.help = _("set the group name")

    def get_new(self):
        return ""

    def get_exists(self):
        return self.Get('ur_unix_group_name_exists')

    def check(self, value):
        if not value:
            raise VariableError(_("You should specify group name"))
        self.check_group_name(value)
        if value == "list":
            raise VariableError(_("List is using as keyword"))
        if self.check_name(value):
            raise VariableError(_("Group name already exists"))


class VariableUrUnixGroupLabel(ReadonlyVariable):
    """
    Информационное поле для закладки
    """
    type = "label"

    def init(self):
        self.label = _("Group")

    def get(self):
        return self.Get('ur_unix_group_name')

    def uncompatible(self):
        return self.label


class VariableUrUnixGroupNameExists(UnixUserHelper, UnixGroupHelper, Variable):
    """
    Название редактируемой/просматриваемой группы
    """
    type = "choice"
    base_dn = "{ld_unix_groups_dn}"
    search_filter = '(cn={value})'
    guitype = "hidden"

    untrusted = True
    opt = ("ur_unix_group_name_exists",)
    metavalue = "GROUP"

    def init(self):
        self.label = _("Group")
        self.help = _("set the group name")

    def choice(self):
        if self.Get('cl_unix_action') == Actions.GroupDel:
            primary_groups = map(int, self.get_primary_gids())
            return [(group.group_name,
                     "%s (%s)" % (group.group_name, group.comment))
                    for group in self.iterate_ldap_group("cn=*")
                    if group.gid not in primary_groups]
        else:
            return [(group.group_name,
                     "%s (%s)" % (group.group_name, group.comment))
                    for group in self.iterate_ldap_group("cn=*")]

    def raiseWrongChoice(self, name, choiceVal, value, error):
        if self.Get('cl_unix_action') == Actions.GroupDel:
            group = self.search_ldap_group_name(value)
            if group:
                users = ", ".join(user.username for user in
                                  self.iterate_ldap_user(
                                      "gidNumber=%d" % group.gid))
                if users:
                    raise VariableError(
                        _("Group {group} is primary group for users {users}"
                          ).format(group=value, users=users))
        if not value:
            raise VariableError(_("Choice group"))
        raise VariableError(_("Group %s not found") % value)

class VariableClUnixGroupCommentDefault(Variable):
    """
    Комментарий для новой группы по умолчанию
    """
    value_format = "{ldap.ld_base_root.capitalize()} group"

class VariableUrUnixGroupComment(UnixGroupHelper, UnixActionHelper, Variable):
    """
    Комментарий к группе
    """
    opt = ('-c', '--comment')
    metavalue = "COMMENT"

    def init(self):
        self.label = _("Comment")
        self.help = _("set comment of the group")

    def get_new(self):
        return self.Get('unix.cl_unix_group_comment_default')

    def get_exists(self):
        return self.Get('ur_unix_group_comment_exists')


class VariableUrUnixGroupCount(UnixGroupHelper, UnixActionHelper,
                               ReadonlyVariable):
    """
    Количество групп
    """
    type = "int"

    def get_exists(self):
        return len(self.ldap_group_list())


class VariableUrUnixGroupCommentExists(UnixGroupHelper, UnixActionHelper,
                                       Variable):
    """
    Комментарий к группе
    """

    def get_exists(self):
        group_name = self.Get('ur_unix_group_name')
        if group_name:
            group = self.search_ldap_group_name(group_name)
            if group:
                return group.comment
        return ""


class VariableUrUnixGroupId(LdapSearchHelper, UnixActionHelper, Variable):
    """
    ID группы
    """
    base_dn = "{ld_unix_groups_dn}"
    search_filter = '(gidNumber={value})'
    type = "int"
    opt = ('-g', '--gid')
    metavalue = "GID"

    def init(self):
        self.label = _("Group ID")
        self.help = _("set the group ID")

    def get_new(self):
        return self.Get('ur_unix_next_gid')

    def get_exists(self):
        return self.Get('ur_unix_group_id_exists')

    def check_new(self, value):
        if not value:
            raise VariableError(_("You should specify group ID"))
        if self.check_name(value):
            raise VariableError(_("Group ID already exists"))

    def check_exists(self, value):
        old_value = self.Get('ur_unix_group_id_exists')
        if not value:
            raise VariableError(_("You should specify group ID"))
        if value != old_value and self.check_name(value):
            raise VariableError(_("Group ID already exists"))


class VariableUrUnixGroupIdExists(UnixGroupHelper, LdapSearchHelper,
                                  UnixActionHelper, Variable):
    """
    ID группы
    """
    type = "int"

    def get_exists(self):
        group_name = self.Get('ur_unix_group_name')
        if group_name:
            group = self.search_ldap_group_name(group_name)
            if group:
                return str(group.gid)
        return ""


class VariableUrUnixNextGid(LdapMaxHelper, ReadonlyVariable):
    """
    Следующий свободный Uid
    """
    base_dn = "{ld_unix_groups_dn}"
    search_filter = "cn=*"
    attr = "gidNumber"

    def get(self):
        value = self.get_max()
        if value is None:
            return self.Get('ur_unix_min_gid')
        else:
            return str(value + 1)


class VariableUrUnixMinGid(Variable):
    """
    Минимальный uid
    """
    type = "int"
    value = "10000"


class VariableUrUnixGroupUsers(UnixGroupHelper, ReadonlyVariable):
    """
    Текущий список пользователей в группе
    """
    type = "list"

    def init(self):
        self.label = _("Users")

    def get(self):
        group_name = self.Get('ur_unix_group_name')
        if group_name:
            group = self.search_ldap_group_name(group_name)
            if group:
                return group.user_list
        return []


class VariableUrUnixGroupUsersAdd(UnixUserHelper, Variable):
    """
    Пользователи добавляемые в группу
    """
    type = "choiceedit-list"
    opt = ("-a", "--add")
    guitype = "hidden"
    value = []
    metavalue = "USERS"

    def init(self):
        self.label = _("Include in group")
        self.help = _("add members")

    def choice(self):
        if self.Get('ur_unix_group_name'):
            exists_users = self.Get('ur_unix_group_users')
            return (x for x in self.ldap_user_list() if
                    x[0] not in exists_users)
        return []

    def get(self):
        exists_users = set(self.Get('ur_unix_group_users'))
        replace_users = set(self.Get('ur_unix_group_users_replace'))
        return sorted(list(replace_users - exists_users))

    def check(self, value):
        exists_users = self.Get('ur_unix_group_users')
        replace_users = self.Get('ur_unix_group_users_replace')
        if set(exists_users) != set(replace_users):
            raise VariableError(_(
                "Appending users to group unavailable "
                "with the replace user using"))
        group = self.Get('ur_unix_group_name')
        users = self.ldap_user_names()
        failed = [x for x in value if x not in users]
        if failed:
            raise VariableError(
                _("Wrong users {users}").format(
                    users=", ".join(failed)))
        already_exists = [x for x in value if x in exists_users]
        if already_exists:
            raise VariableError(
                _("Users {users} already exist in group {group}").format(
                    users=", ".join(already_exists), group=group))


class VariableUrUnixGroupUsersReplace(UnixUserHelper, UnixActionHelper,
                                      Variable):
    """
    Замена всех пользователей в группе
    """
    type = "choiceedit-list"
    opt = ("-U ", "--users")
    value = []
    metavalue = "USERS"

    def init(self):
        self.label = _("Users")
        self.help = _("new user list of group")

    def get_exists(self):
        return list(sorted(self.Get('ur_unix_group_users')))

    def choice_exists(self):
        return self.ldap_user_list()

    def check_exists(self, value):
        users = self.ldap_user_names()
        failed = [x for x in value if x not in users]
        if failed:
            raise VariableError(
                _("Wrong users {users}").format(
                    users=", ".join(failed)))


class VariableUrUnixGroupUsersDel(UnixUserHelper, Variable):
    """
    Пользователи добавляемые в группу
    """
    type = "choiceedit-list"
    opt = ("-r", "--remove")
    guitype = "hidden"
    value = []
    metavalue = "USERS"

    def init(self):
        self.label = _("Exclude from groups")
        self.help = _("remove members")

    def choice(self):
        exists_users = self.Get('ur_unix_group_users')
        return (x for x in self.ldap_user_list() if
                x[0] in exists_users)

    def get(self):
        exists_users = set(self.Get('ur_unix_group_users'))
        replace_users = set(self.Get('ur_unix_group_users_replace'))
        return sorted(list(exists_users - replace_users))

    def check(self, value):
        exists_users = self.Get('ur_unix_group_users')
        replace_users = self.Get('ur_unix_group_users_replace')
        if set(exists_users) != set(replace_users):
            raise VariableError(_(
                "Removing users from group unavailable "
                "with the replace user using"))
        if not exists_users and value:
            raise VariableError(_("Group has not members"))

        group = self.Get('ur_unix_group_name')
        users = self.ldap_user_names()
        failed = [x for x in value if x not in users]
        if failed:
            raise VariableError(
                _("Wrong users {users}").format(
                    users=", ".join(failed)))
        not_exists = [x for x in value if x not in exists_users]
        if not_exists:
            raise VariableError(
                _("Users {users} are not exist in group {group}").format(
                    users=", ".join(not_exists), group=group))

    def humanReadable(self):
        if not self.Get():
            return _("Not change")


class VariableUrUnixGroupNewname(GroupNameHelper, UnixGroupHelper, Variable):
    opt = ("-n", "--new-name")
    value = ""
    metavalue = "NEW_GROUP"

    def init(self):
        self.label = _("Group name")
        self.help = _("use NEW_GROUP name by GROUP")

    def get(self):
        return self.Get('ur_unix_group_name')

    def check(self, value):
        if value == self.Get('ur_unix_group_name'):
            return
        self.check_group_name(value)
        if self.search_ldap_group_name(value):
            raise VariableError(
                _("Group {name} already exists").format(name=value))

    def humanReadable(self):
        value = self.Get()
        if not value:
            return _("Not change")
        return value


class VariableClUnixGroupAliases(ReadonlyVariable):
    """
    Алиасы для переменных
    """
    type = "table"
    value = [  # ('name', 'ur_unix_group_name'),
               ('id', 'ur_unix_group_id'),
               ('comment', 'ur_unix_group_comment'),
               ('users', 'ur_unix_group_users')]


class VariableClUnixGroupFields(Variable):
    """
    Список полей для вывода данных группы
    """
    type = "choice-list"
    element = "selecttable"
    opt = ["--fields"]
    metavalue = "FIELDS"

    def init(self):
        self.label = _("Fields")
        self.help = _("fields for display")

    def choice(self):
        data = self.Get('cl_unix_group_aliases')
        return [(x, self.parent.getInfo(y).label) for x, y in data]

    def get(self):
        return [x for x, y in self.Get('cl_unix_group_aliases')]


class VariableClUnixGroupShowFields(ReadonlyVariable):
    """
    Список переменных полей при отображении списка групп
    """
    type = "list"

    def get(self):
        mapping = dict(self.Get('cl_unix_group_aliases'))
        return [mapping[x] for x in self.Get('cl_unix_group_fields')]


class VariableUrUnixGroupShow(UnixGroupHelper,
                              UnixActionHelper, Variable):
    """
    Фильтр на group name
    """
    type = "choiceedit"
    opt = ["ur_unix_group_show"]
    metavalue = "GROUP"
    value = ""

    def init(self):
        self.label = _("Group name")
        self.help = _("show group")

    def choice_exists(self):
        return [("", "")] + self.ldap_group_list()

    def check_exists(self, value):
        if value:
            if not self.search_ldap_group_name(value):
                raise VariableError(_("%s group not found") % value)

    def raiseWrongChoice(self, name, choiceVal, value, error):
        raise VariableError(_("Wrong group name"))
