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

# Copyright 2015-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.
from collections import namedtuple

import sys

_ = lambda x: x
from ..cl_lang import setLocalTranslate

setLocalTranslate('cl_lib3', sys.modules[__name__])


class AccountsError(Exception):
    pass


class UnixUser():
    """
    UnixUser = namedtuple("UnixUser", ["name", "password", "uid",
                                       "gid", "gecos", "homedir", "shell"])
    """

    def __init__(self, name, password, uid, gid, gecos, homedir, shell):
        self.name = name
        self.password = password
        self.uid = uid
        self.gid = gid
        self.gecos = gecos
        self.homedir = homedir
        self.shell = shell


class Passwd():
    def __init__(self, data):
        self.users = [
            UnixUser(*x.split(":"))
            for x in data.split('\n')
            if x.count(":") == 6
            ]

    def equal_user(self, user1, user2):
        """
        Проверка эквивалентности пользователей
        :param user1:
        :param user2:
        :return:
        """
        if user1.name == user2.name or user1.uid == user2.uid:
            return True
        return False

    def join(self, passwd):
        """
        Объединить пользователей
        :param passwd:
        :return:
        """
        for user in self.users:
            for backup_user in passwd.users:
                if self.equal_user(user, backup_user):
                    user.gecos = backup_user.gecos
                    user.password = backup_user.password
                    user.homdedir = backup_user.homedir
                    user.shell = backup_user.shell
                    break
        for backup_user in self.new_users(passwd):
            self.users.append(backup_user)

    def new_users(self, passwd):
        """
        Список пользовтелей которые есть только в passwd
        :param passwd:
        :return:
        """
        return [
            user for user in passwd.users
            if not any(self.equal_user(user, x) for x in self.users)]

    def get_uid_map(self, passwd):
        """
        Получить словарь соответствий одних uid другим
        :param passwd:
        :return:
        """
        uid_map = {}
        for user in self.users:
            for user2 in passwd.users:
                if user.name == user2.name and user.uid != user2.uid:
                    try:
                        uid_map[int(user.uid)] = int(user2.uid)
                    except ValueError:
                        raise AccountsError(
                            _("Wrong UIDs {uid1} {uid2}").format(
                                uid1=user.uid, uid2=user2.uid))
        return uid_map

    def format_user(self, user):
        return "{0}:{1}:{2}:{3}:{4}:{5}:{6}".format(
            user.name, user.password, user.uid,
            user.gid, user.gecos, user.homedir, user.shell)

    def write(self, f):
        for user in self.users:
            f.write("%s\n" % self.format_user(user))


class UnixGroup():
    """
    UnixGroup = namedtuple("UnixGroup",
     ["name", "password", "gid", "user_list"])
    """

    def __init__(self, name, password, gid, user_list):
        self.name = name
        self.password = password
        self.gid = gid
        self.user_list = user_list


class Group():
    def __init__(self, data):
        self.groups = [
            UnixGroup(*x.split(":"))
            for x in data.split('\n')
            if x.count(":") == 3]

    def equal_group(self, group1, group2):
        """
        Проверка эквивалентности групп
        :param group1:
        :param group2:
        :return:
        """
        if group1.name == group2.name or group1.gid == group2.gid:
            return True
        return False

    def new_groups(self, groupobj):
        """
        Список групп которые есть только в groupobj
        :param groupobj:
        :return:
        """
        return [
            user for user in groupobj.groups
            if not any(self.equal_group(user, x) for x in self.groups)]

    def join(self, groupobj, keep_users=()):
        """
        Объдинить группы
        :param groupobj:
        :param keep_users: список пользователей, чьи группы нужно сохранить
        :return:
        """
        for group in self.groups:
            for backup_group in groupobj.groups:
                if self.equal_group(group, backup_group):
                    group.password = backup_group.password
                    # backup_user_list = list(filter(None,
                    #                           backup_group.user_list.split(',')))
                    backup_user_list = [x for x in backup_group.user_list.split(',') if x]
                                             
                    # current_user_list = list(filter(None,
                    #                            group.user_list.split(',')))
                    current_user_list = [x for x in group.user_list.split(',') if x]                        
                    user_list = [
                        x for x in current_user_list
                        if x in keep_users and x not in backup_user_list]
                    new_user_list = backup_user_list + user_list
                    # не меняем список групп, если изменился только порядок
                    if set(current_user_list) != set(new_user_list):
                        group.user_list = ",".join(new_user_list)
                    break
        for backup_group in self.new_groups(groupobj):
            self.groups.append(backup_group)

    def get_gid_map(self, groupobj):
        """
        Получить словарь соответствий одних gid другим
        :param groupobj:
        :return:
        """
        gid_map = {}
        for group in self.groups:
            for group2 in groupobj.groups:
                if group.name == group2.name and group.gid != group2.gid:
                    try:
                        gid_map[int(group.gid)] = int(group2.gid)
                    except ValueError:
                        raise AccountsError(
                            _("Wrong GIDs {gid1} {gid2}").format(
                                gid1=group.gid, gid2=group2.gid))
        return gid_map

    def format_group(self, group):
        return "{0}:{1}:{2}:{3}".format(
            group.name, group.password, group.gid, group.user_list)

    def write(self, f):
        for group in self.groups:
            f.write("%s\n" % self.format_group(group))


unix_shadow_fields = ["name", "password", "last_change",
                      "min_age", "max_age", "warn_period",
                      "inactive_period", "expire", "reserved"]
UnixShadow = namedtuple("UnixShadow", unix_shadow_fields)


class Shadow():
    def __init__(self, data):
        self.passwords = [
            UnixShadow(*x.split(":"))
            for x in data.split('\n')
            if x.count(":") == len(unix_shadow_fields) - 1]

    def equal_shadow(self, shadow1, shadow2):
        """
        Проверка эквивалентности записи
        :param shadow1:
        :param shadow2:
        :return:
        """
        if shadow1.name == shadow2.name:
            return True
        return False

    def changed_passwords(self, shadowobj):
        """
        Список записей в которых будет восстановлен пароль
        :param shadowobj:
        :return:
        """
        return [shadow
                for shadow in shadowobj.passwords
                for shadow2 in self.passwords
                if (self.equal_shadow(shadow, shadow2)
                    and shadow.password != shadow2.password)]

    def join(self, shadowobj):
        def select_shadow(shadow):
            for shadow2 in shadowobj.passwords:
                if self.equal_shadow(shadow, shadow2):
                    return shadow2
            else:
                return shadow

        self.passwords = [
            select_shadow(shadow)
            for shadow in self.passwords]
        passwords = [
            shadow
            for shadow in shadowobj.passwords
            if not any(self.equal_shadow(shadow, x)
                       for x in self.passwords)]
        self.passwords.extend(passwords)

    def format_shadow(self, shadow):
        return ":".join(getattr(shadow, x) for x in unix_shadow_fields)

    def write(self, f):
        for shadow in self.passwords:
            f.write("%s\n" % self.format_shadow(shadow))
