# coding: utf-8

from __future__ import absolute_import, unicode_literals
import collections

from common.utils import setattr_bulk, get_logger
from .tasks import push_users
from .hands import User, UserGroup, Asset, AssetGroup, SystemUser

logger = get_logger(__file__)


def get_user_group_granted_asset_groups(user_group):
    """Return asset groups granted of the user group

     :param user_group: Instance of :class: ``UserGroup``
     :return: {asset_group1: {system_user1, },
               asset_group2: {system_user1, system_user2}}
    """
    asset_groups = {}
    asset_permissions = user_group.asset_permissions.all()

    for asset_permission in asset_permissions:
        if not asset_permission.is_valid:
            continue
        for asset_group in asset_permission.asset_groups.all():
            if asset_group in asset_groups:
                asset_groups[asset_group] |= set(asset_permission.system_users.all())
            else:
                asset_groups[asset_group] = set(asset_permission.system_users.all())
    return asset_groups


def get_user_group_granted_assets(user_group):
    """Return assets granted of the user group

    :param user_group: Instance of :class: ``UserGroup``
    :return: {asset1: {system_user1, }, asset1: {system_user1, system_user2]}
    """
    assets = {}
    asset_permissions = user_group.asset_permissions.all()

    for asset_permission in asset_permissions:
        if not asset_permission.is_valid:
            continue
        for asset in asset_permission.get_granted_assets():
            if not asset.is_active:
                continue
            if asset in assets:
                assets[asset] |= set(asset_permission.system_users.all())
            else:
                assets[asset] = set(asset_permission.system_users.all())
    return assets


def get_user_granted_assets_direct(user):
    """Return assets granted of the user directly

     :param user: Instance of :class: ``User``
     :return: {asset1: {system_user1, system_user2}, asset2: {...}}
    """
    assets = {}
    asset_permissions_direct = user.asset_permissions.all()

    for asset_permission in asset_permissions_direct:
        if not asset_permission.is_valid:
            continue
        for asset in asset_permission.get_granted_assets():
            if not asset.is_active:
                continue
            if asset in assets:
                assets[asset] |= set(asset_permission.system_users.all())
            else:
                setattr(asset, 'inherited', False)
                assets[asset] = set(asset_permission.system_users.all())
    return assets


def get_user_granted_assets_inherit_from_user_groups(user):
    """Return assets granted of the user inherit from user groups

    :param user: Instance of :class: ``User``
    :return: {asset1: {system_user1, system_user2}, asset2: {...}}
    """
    assets = {}
    user_groups = user.groups.all()

    for user_group in user_groups:
        assets_inherited = get_user_group_granted_assets(user_group)
        for asset in assets_inherited:
            if not asset.is_active:
                continue
            if asset in assets:
                assets[asset] |= assets_inherited[asset]
            else:
                setattr(asset, 'inherited', True)
                assets[asset] = assets_inherited[asset]
    return assets


def get_user_granted_assets(user):
    """Return assets granted of the user inherit from user groups

    :param user: Instance of :class: ``User``
    :return: {asset1: {system_user1, system_user2}, asset2: {...}}
    """
    assets_direct = get_user_granted_assets_direct(user)
    assets_inherited = get_user_granted_assets_inherit_from_user_groups(user)
    assets = assets_inherited

    for asset in assets_direct:
        if not asset.is_active:
            continue
        if asset in assets:
            assets[asset] |= assets_direct[asset]
        else:
            assets[asset] = assets_direct[asset]
    return assets


def get_user_granted_asset_groups(user):
    """Return asset groups with assets and system users, it's not the asset
    group direct permed in rules. We get all asset and then get it asset group

    :param user: Instance of :class: ``User``
    :return: {asset_group1: [asset1, asset2], asset_group2: []}
    """
    asset_groups = collections.defaultdict(list)
    ungroups = [AssetGroup(name="UnGrouped")]
    for asset, system_users in get_user_granted_assets(user).items():
        groups = asset.groups.all()
        if not groups:
            groups = ungroups
        for asset_group in groups:
            asset_groups[asset_group].append((asset, system_users))
    return asset_groups


def get_user_group_asset_permissions(user_group):
    permissions = user_group.asset_permissions.all()
    return permissions


def get_user_asset_permissions(user):
    user_group_permissions = set()
    direct_permissions = set(setattr_bulk(user.asset_permissions.all(), 'inherited', 0))

    for user_group in user.groups.all():
        permissions = get_user_group_asset_permissions(user_group)
        user_group_permissions |= set(permissions)
    user_group_permissions = set(setattr_bulk(user_group_permissions, 'inherited', 1))
    return direct_permissions | user_group_permissions


def get_user_granted_system_users(user):
    """
    :param user: the user
    :return: {"system_user": ["asset", "asset1"], "system_user": []}
    """
    assets = get_user_granted_assets(user)
    system_users_dict = {}
    for asset, system_users in assets.items():
        for system_user in system_users:
            if system_user in system_users_dict:
                system_users_dict[system_user].append(asset)
            else:
                system_users_dict[system_user] = [asset]
    return system_users_dict


def get_user_groups_granted_in_asset(asset):
    pass


def get_users_granted_in_asset(asset):
    pass


def get_user_groups_granted_in_asset_group(asset):
    pass


def get_users_granted_in_asset_group(asset):
    pass


def push_system_user(assets, system_user):
    logger.info('Push system user %s' % system_user.name)
    for asset in assets:
        logger.info('\tAsset: %s' % asset.ip)
    if not assets:
        return None

    assets = [asset._to_secret_json() for asset in assets]
    system_user = system_user._to_secret_json()
    task = push_users.delay(assets, system_user)
    return task.id


def associate_system_users_and_assets(system_users, assets, asset_groups, force=False):
    """关联系统用户和资产, 目的是保存它们的关系, 然后新加入的资产或系统
    用户时,推送系统用户到资产

    Todo: 这里需要最终Api定下来更改一下, 现在策略是以系统用户为核心推送, 一个系统用户
    推送一次
    """
    assets_all = set(assets)

    for asset_group in asset_groups:
        assets_all |= set(asset_group.assets.all())

    for system_user in system_users:
        assets_need_push = []
        if system_user.auto_push:
            if force:
                assets_need_push = assets_all
            else:
                assets_need_push.extend(
                    [asset for asset in assets_all
                     if asset not in system_user.assets.all()
                     ]
                )
        system_user.assets.add(*(tuple(assets_all)))
        push_system_user(assets_need_push, system_user)

