# -*- 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
import re
from fnmatch import fnmatch
from calculate.lib.datavars import (Variable, VariableInterface, VariableError)
from calculate.unix.variables.helpers import (UnixUserHelper, UnixGroupHelper,
                                              UnixActionHelper)
from calculate.lib.cl_ldap import LDAPConnectError
import operator
from itertools import chain

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

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

"""
String
--filter-comment fasdfad faksjdflk*   ---fasdfas|fasdfasd

Integer
--filter-uid 321 >12000 <11000

--filter-uid 321, >=, <, >, >=  325-400

Boolean
--filter-password on/off/auto
"""


class FilterHelper(VariableInterface):
    def test(self, value):
        """
        Проверка попадания значения под фильтр
        :param value:
        :return:
        """
        return True

    def enabled(self):
        filter_values = self.Get()
        if not filter_values:
            return False
        return True


class FilterStringHelper(FilterHelper):
    """
    Для фильтрации строковых данных

    user10, user*, !user*, !user20
    """
    value = ""
    metavalue = "FILTER"

    def test_value(self, value):
        filter_values = self.Get()
        if not filter_values:
            return True

        for filter_value in filter_values.split('|'):
            revert = lambda x: x
            if filter_value[:1] == "!" or filter_value[:2] == r"\!":
                revert = lambda x: not x
                if filter_value[:2] == r"\!":
                    filter_value = filter_value[2:]
                else:
                    filter_value = filter_value[1:]
            if revert(fnmatch(value, filter_value)):
                return True
        else:
            return False


class FilterListHelper(FilterHelper):
    """
    Для фильтрации данных со списками

    user10, user*, !user*, !user20
    """
    value = ""
    metavalue = "FILTER"

    def test_value(self, value):
        value = list(value)
        filter_values = self.Get()
        if not filter_values:
            return True

        for filter_value in filter_values.split('|'):
            revert = lambda x: x
            if filter_value[:1] == "!" or filter_value[:2] == r"\!":
                revert = lambda x: not x
                if filter_value[:2] == r"\!":
                    filter_value = filter_value[2:]
                else:
                    filter_value = filter_value[1:]
            results = [fnmatch(x, filter_value) for x in value]
            if revert(any(results)):
                return True
        else:
            return False


class FilterIntegerHelper(FilterHelper):
    """
    Для фильтрации числовых данных

    >5, <5, >=5, <=5, 5, 5-10
    """
    value = ""
    filter_format = re.compile("^(\d+)-(\d+)|(<=|>=|<|>)?(\d+)$")
    metavalue = "FILTER"

    def test_value(self, value):
        filter_value = self.Get()
        if not filter_value:
            return True
        start, end, op, number = self.filter_format.search(
            filter_value).groups()

        if start and end:
            return int(start) <= value <= int(end)
        number = int(number)
        if op:
            return {"<=": operator.le,
                    ">=": operator.ge,
                    "<": operator.lt,
                    ">": operator.gt}.get(op)(value, number)
        else:
            return value == number

    def check(self, value):
        if not self.filter_format.match(value):
            raise VariableError(_("Wrong filter"))


class FilterBooleanHelper(FilterHelper):
    """
    Для фильтрации булевых данных
    """
    type = "boolauto"
    value = "auto"
    metavalue = "ON/OFF/AUTO"

    def enabled(self):
        filter_value = self.Get()
        if filter_value == "auto":
            return False
        return True

    def test_value(self, value):
        filter_value = self.Get()
        if filter_value == "auto":
            return True
        return Variable.isTrue(filter_value) == value


class VariableClUnixUserFilterLogin(FilterStringHelper, Variable):
    """
    Фильтр на login
    """
    opt = ["--filter-login"]

    def init(self):
        self.label = _("Login filter")
        self.help = _("set login filter")

    def test(self, user):
        return self.test_value(user.username)


class VariableClUnixUserFilterComment(FilterStringHelper, Variable):
    """
    Фильтр на полное имя пользователя
    """
    opt = ["--filter-fullname"]

    def init(self):
        self.label = _("Full name filter")
        self.help = _("set full name filter")

    def test(self, user):
        return self.test_value(user.comment)


class VariableClUnixUserFilterHomePath(FilterStringHelper, Variable):
    """
    Фильтр на домашнюю директорию
    """
    opt = ["--filter-homedir"]

    def init(self):
        self.label = _("Home directory filter")
        self.help = _("set home directory filter")

    def test(self, user):
        return self.test_value(user.homedir)


class VariableClUnixUserFilterShell(FilterStringHelper, Variable):
    """
    Фильтр на shell
    """
    opt = ["--filter-shell"]

    def init(self):
        self.label = _("Shell filter")
        self.help = _("set shell filter")

    def test(self, user):
        return self.test_value(user.shell)


class VariableClUnixUserFilterGid(FilterStringHelper, UnixGroupHelper,
                                  Variable):
    """
    Фильтр на primary group
    """
    opt = ["--filter-gid"]

    def init(self):
        self.label = _("Primary group filter")
        self.help = _("set primary group filter")

    def test(self, user):
        primary_group = str(self.gid_to_name(str(user.gid)))
        return self.test_value(primary_group)

    def set(self, value):
        if value.isdigit():
            value = self.gid_to_name(str(value))
        return str(value)


class VariableClUnixUserFilterGroups(FilterListHelper, UnixGroupHelper,
                                     Variable):
    """
    Фильтр на вхождение пользователя в группу
    """
    opt = ["--filter-groups"]

    def init(self):
        self.label = _("Supplimentary group filter")
        self.help = _("set supplimentary group filter")

    def test(self, user):
        return self.test_value(x.group_name for x in self.iterate_ldap_group(
            "memberUid=%s" % user.username))


class VariableClUnixUserFilterUid(FilterIntegerHelper, Variable):
    """
    Фильтр на id пользователя
    """
    opt = ["--filter-uid"]

    def init(self):
        self.label = _("User ID filter")
        self.help = _("set user ID filter")

    def test(self, user):
        return self.test_value(user.uid)


class VariableClUnixUserFilterPwSet(FilterBooleanHelper, Variable):
    """
    Фильтр на наличие пароля
    """
    opt = ["--filter-password"]

    def init(self):
        self.label = _("Password availability filter")
        self.help = _("set password availability filter")

    def test(self, user):
        return self.test_value(user.pass_set)


class VariableClUnixUserFilterVisibleSet(FilterBooleanHelper, Variable):
    """
    Фильтр на видимость
    """
    opt = ["--filter-visible"]

    def init(self):
        self.label = _("Visibility filter")
        self.help = _("set visibility filter")

    def test(self, user):
        return self.test_value(user.visible)


class VariableClUnixUserFilterLockSet(FilterBooleanHelper, Variable):
    """
    Фильтр на login
    """
    opt = ["--filter-lock"]

    def init(self):
        self.label = _("Lock filter")
        self.help = _("set lock filter")

    def test(self, user):
        return self.test_value(user.lock)


class VariableClUnixGroupFilterName(FilterStringHelper, Variable):
    """
    Фильтр на group name
    """
    opt = ["--filter-name"]

    def init(self):
        self.label = _("Name filter")
        self.help = _("set name filter")

    def test(self, group):
        return self.test_value(group.group_name)


class VariableClUnixGroupFilterId(FilterIntegerHelper, Variable):
    """
    Фильтр на group id
    """
    opt = ["--filter-gid"]

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

    def test(self, group):
        return self.test_value(group.gid)


class VariableClUnixGroupFilterComment(FilterStringHelper, Variable):
    """
    Фильтр на group comment
    """
    opt = ["--filter-comment"]

    def init(self):
        self.label = _("Comment filter")
        self.help = _("set comment filter")

    def test(self, group):
        return self.test_value(group.comment)


class VariableClUnixGroupFilterUsers(FilterListHelper, UnixUserHelper,
                                     Variable):
    """
    Фильтр на наличие пользователей в группе
    """
    opt = ["--filter-users"]

    def init(self):
        self.label = _("Users filter")
        self.help = _("set users filter")

    def test(self, group):
        return self.test_value(
            chain(group.user_list,
                  (user.username for user in self.iterate_ldap_user(
                      "gidNumber=%d" % group.gid))))
