# -*- 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,
                                    VariableInterface, VariableError)
from calculate.lib.utils.tools import repeater
from calculate.lib.cl_ldap import LDAPConnect, LDAPConnectError
from helpers import ServerEnvHelper, HashHelper, RandomPasswordHelper
import re

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

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


class VariableLdBaseRoot(ReadonlyVariable):
    """
    Имя для базового суффикса
    """
    def get(self):
        base_dn = self.Get('ld_base_dn')
        dns = base_dn.split(',')
        if dns:
            return dns[0].rpartition('=')[2]
        return "calculate"

class VariableLdBaseDn(ServerEnvHelper, Variable):
    """
    Базовый суффикс LDAP
    """
    service = "admin"
    parameter = "DN"
    fallback_value = "dc=calculate,dc=org"

    opt = ("-b", "--basedn")
    metavalue = "BASEDN"

    def init(self):
        self.label = _("Base DN")
        self.help = "set base DN"

    def get(self):
        value = super(VariableLdBaseDn, self).get()
        values = [x for x in value.split(',') if x.startswith("dc=")]
        if values:
            return ",".join(values)
        return self.fallback_value

    def uncompatible(self):
        if self.Get('server.sr_ldap_set') == 'on':
            return "Unavailable for configured LDAP service"

    def check(self, value):
        if not re.match(r"^(\w+=[a-zA-Z0-9_-]+,)*\w+=[a-zA-Z0-9_-]+$", value):
            raise VariableError(_("Wrong base DN"))


class VariableLdBindLogin(Variable):
    """
    Пользователь для чтения
    """
    value = "proxyuser"


class VariableLdBindDn(ReadonlyVariable):
    """
    Bind суффикс LDAP
    """
    value_format = "cn={ld_bind_login},{ld_base_dn}"


class VariableLdBindPw(ReadonlyVariable):
    """
    Пароль для подключения
    """
    value = "calculate"


class VariableLdBindHash(HashHelper, ReadonlyVariable):
    """
    Хэш рут пароля
    """
    source = "ld_bind_pw"


class VariableLdTempDn(ReadonlyVariable):
    """
    Временный DN для подключения
    """
    value_format = "cn=ldaproot,{ld_base_dn}"


class VariableLdTempPw(RandomPasswordHelper, ReadonlyVariable):
    """
    Временный пароль для подключения к LDAP
    """
    password_len = 9

    def get(self):
        return "test"


class VariableLdTempHash(HashHelper, ReadonlyVariable):
    """
    Временный пароль для подключения к LDAP
    """
    source = "ld_temp_pw"


class VariableClLdapPreconfigureSet(ReadonlyVariable):
    """
    Предварительное наложение шаблонов
    """
    type = "bool"
    value = "off"


class VariableLdAdminDn(ServerEnvHelper, ReadonlyVariable):
    """
    DN root пользователя
    """
    service = "admin"
    parameter = "DN"
    value_format = "cn={ld_admin_login},{ld_base_dn}"

    @property
    def fallback_value(self):
        return self._get_format_value()


class VariableLdAdminLogin(ReadonlyVariable):
    """
    Имя пользователя root
    """
    value = "ldapadmin"


class VariableLdEncrypt(ReadonlyVariable):
    """
    Алгоритм шифрования пароля
    """
    value = "{SSHA}"


class VariableLdAdminHash(HashHelper, ReadonlyVariable):
    """
    Хэш рут пароля
    """
    source = "ld_admin_pw"


class VariableLdAdminPw(ServerEnvHelper, RandomPasswordHelper, Variable):
    """
    Пароль root
    """
    password_len = 9
    service = "admin"
    parameter = "PASS"

    @property
    def fallback_value(self):
        return RandomPasswordHelper.get(self)

    def get(self):
        if self.Get('cl_ldap_pw_generate_set') == 'on':
            return RandomPasswordHelper.get(self)
        else:
            return super(VariableLdAdminPw, self).get()


class VariableLdServices(ReadonlyVariable):
    """
    Имя всех сервисов
    """
    value = "Services"


class VariableLdServicesDn(ReadonlyVariable):
    """
    DN для всех сервисов
    """
    value_format = "ou={ld_services},{ld_base_dn}"


class VariableLdDatabasePath(Variable):
    """
    Путь до базы LDAP
    """
    value = "/var/lib/openldap-data"


class VariableClLdapHost(Variable):
    """
    Узел LDAP
    """
    value = "localhost"


class VariableClLdapBindDn(Variable):
    """
    Переменная используется для соединения с LDAP
    """
    value = ""


class VariableClLdapBindPw(Variable):
    """
    Переменная используется для соединения с LDAP
    """
    value = ""


class VariableClLdapConnect(ReadonlyVariable):
    """
    Объект соединение с LDAP
    """
    type = "object"

    def get(self):
        bind_dn = self.Get('cl_ldap_bind_dn')
        bind_pw = self.Get('cl_ldap_bind_pw')
        ldap_host = self.Get('cl_ldap_host')
        if bind_dn and bind_pw:
            error = ""
            for x in repeater(0.2, 0.4, 0.8):
                try:
                    return LDAPConnect(bind_dn, bind_pw, host=ldap_host)
                except LDAPConnectError as e:
                    error = str(e)
            raise VariableError(_("Failed to connect to LDAP server")
                                + _(": ") + error)
        return ""


class VariableClLdapPwGenerateSet(Variable):
    """
    Перегенерировать пароль или нет
    """
    type = "bool"

    opt = ("-g", "--gen-password")
    value = "off"

    def init(self):
        self.label = _("Generate new service password")
        self.help = _("generate new service password")

    def uncompatible(self):
        if self.Get('server.sr_ldap_set') != 'on':
            return "Unavailable for unconfigured LDAP service"

class VariableClLdapRemoveSet(Variable):
    """
    Удалить сервис Unix
    """
    type = "bool"
    guitype = "hidden"

    opt = ("-r", "--remove")
    value = "off"

    def init(self):
        self.label = _("Remove service")
        self.help = _("remove service")

    def check(self, value):
        if value == 'on' and self.Get('server.sr_ldap_set') != 'on':
            raise VariableError(_("LDAP service is not setup"))

    def uncompatible(self):
        if self.Get('server.sr_ldap_set') != 'on':
            return "Unavailable for unconfigured LDAP service"
