# -*- coding: utf-8 -*-
#
import time
import traceback
from functools import reduce
import uuid
from django.db.models import Q
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView, Response
from rest_framework.generics import (
    ListAPIView, get_object_or_404, RetrieveAPIView
)
from rest_framework.pagination import LimitOffsetPagination

from common.permissions import IsValidUser, IsOrgAdminOrAppUser
from common.tree import TreeNodeSerializer
from common.utils import get_logger
from ..utils import (
    AssetPermissionUtil, ParserNode,
)
from .mixin import UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin
from .. import const
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
from .. import serializers
from ..models import Action


logger = get_logger(__name__)

__all__ = [
    'UserGrantedAssetsApi', 'UserGrantedNodesApi',
    'UserGrantedNodesWithAssetsApi', 'UserGrantedNodeAssetsApi',
    'ValidateUserAssetPermissionApi', 'UserGrantedNodesAsTreeApi',
    'UserGrantedNodesWithAssetsAsTreeApi', 'GetUserAssetPermissionActionsApi',
]


class UserGrantedAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIView):
    """
    用户授权的所有资产
    """
    permission_classes = (IsOrgAdminOrAppUser,)
    pagination_class = LimitOffsetPagination

    def get_object(self):
        user_id = self.kwargs.get('pk', '')
        if user_id:
            user = get_object_or_404(User, id=user_id)
        else:
            user = self.request.user
        return user

    def get_queryset(self):
        user = self.get_object()
        util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
        queryset = util.get_assets()
        return queryset

    def get_permissions(self):
        if self.kwargs.get('pk') is None:
            self.permission_classes = (IsValidUser,)
        return super().get_permissions()


class UserGrantedNodeAssetsApi(UserPermissionCacheMixin, GrantAssetsMixin, ListAPIView):
    """
    查询用户授权的节点下的资产的api, 与上面api不同的是，只返回某个节点下的资产
    """
    permission_classes = (IsOrgAdminOrAppUser,)
    pagination_class = LimitOffsetPagination

    def get_object(self):
        user_id = self.kwargs.get('pk', '')

        if user_id:
            user = get_object_or_404(User, id=user_id)
        else:
            user = self.request.user
        return user

    def get_node_key(self):
        node_id = self.kwargs.get('node_id')
        if str(node_id) == const.UNGROUPED_NODE_ID:
            key = self.util.tree.ungrouped_key
        elif str(node_id) == const.EMPTY_NODE_ID:
            key = const.EMPTY_NODE_KEY
        else:
            node = get_object_or_404(Node, id=node_id)
            key = node.key
        return key

    def get_queryset(self):
        user = self.get_object()
        self.util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
        key = self.get_node_key()
        nodes_items = self.util.get_nodes_with_assets()
        assets_system_users = {}
        for item in nodes_items:
            if item["key"] == key:
                assets_system_users = item["assets"]
                break
        assets = []
        for asset_id, system_users in assets_system_users.items():
            assets.append({"id": asset_id, "system_users": system_users})
        return assets

    def get_permissions(self):
        if self.kwargs.get('pk') is None:
            self.permission_classes = (IsValidUser,)
        return super().get_permissions()


class UserGrantedNodesApi(UserPermissionCacheMixin, NodesWithUngroupMixin, ListAPIView):
    """
    查询用户授权的所有节点的API
    """
    permission_classes = (IsOrgAdminOrAppUser,)
    serializer_class = NodeSerializer
    pagination_class = LimitOffsetPagination
    only_fields = NodeSerializer.Meta.only_fields

    def get_object(self):
        user_id = self.kwargs.get('pk', '')
        if user_id:
            user = get_object_or_404(User, id=user_id)
        else:
            user = self.request.user
        return user

    def get_nodes(self, nodes_with_assets):
        node_keys = [n["key"] for n in nodes_with_assets]
        nodes = Node.objects.filter(key__in=node_keys).only(
            *self.only_fields
        )
        nodes_map = {n.key: n for n in nodes}
        self.add_ungrouped_nodes(nodes_map, node_keys)

        _nodes = []
        for n in nodes_with_assets:
            key = n["key"]
            node = nodes_map.get(key)
            node._assets_amount = n["assets_amount"]
            _nodes.append(node)
        return _nodes

    def get_serializer(self, nodes_with_assets, many=True):
        nodes = self.get_nodes(nodes_with_assets)
        return super().get_serializer(nodes, many=True)

    def get_queryset(self):
        user = self.get_object()
        self.util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
        nodes_with_assets = self.util.get_nodes_with_assets()
        return nodes_with_assets

    def get_permissions(self):
        if self.kwargs.get('pk') is None:
            self.permission_classes = (IsValidUser,)
        return super().get_permissions()


class UserGrantedNodesAsTreeApi(UserGrantedNodesApi):
    serializer_class = TreeNodeSerializer
    only_fields = ParserNode.nodes_only_fields

    def get_serializer(self, nodes_with_assets, many=True):
        nodes = self.get_nodes(nodes_with_assets)
        queryset = []
        for node in nodes:
            data = ParserNode.parse_node_to_tree_node(node)
            queryset.append(data)
        return self.get_serializer_class()(queryset, many=many)


class UserGrantedNodesWithAssetsApi(UserPermissionCacheMixin, NodesWithUngroupMixin, ListAPIView):
    """
    用户授权的节点并带着节点下资产的api
    """
    permission_classes = (IsOrgAdminOrAppUser,)
    serializer_class = serializers.NodeGrantedSerializer
    pagination_class = LimitOffsetPagination

    nodes_only_fields = serializers.NodeGrantedSerializer.Meta.only_fields
    assets_only_fields = serializers.NodeGrantedSerializer.assets_only_fields
    system_users_only_fields = serializers.NodeGrantedSerializer.system_users_only_fields

    def get_object(self):
        user_id = self.kwargs.get('pk', '')
        if not user_id:
            user = self.request.user
        else:
            user = get_object_or_404(User, id=user_id)
        return user

    def get_maps(self, nodes_items):
        """
        查库，并加入构造的ungrouped节点
        :return:
        ({asset.id: asset}, {node.key: node}, {system_user.id: system_user})
        """
        _nodes_keys = set()
        _assets_ids = set()
        _system_users_ids = set()
        for item in nodes_items:
            _nodes_keys.add(item["key"])
            _assets_ids.update(set(item["assets"].keys()))
            for _system_users_id in item["assets"].values():
                _system_users_ids.update(_system_users_id.keys())

        _nodes = Node.objects.filter(key__in=_nodes_keys).only(
            *self.nodes_only_fields
        )
        _assets = Asset.objects.filter(id__in=_assets_ids).only(
            *self.assets_only_fields
        )
        _system_users = SystemUser.objects.filter(id__in=_system_users_ids).only(
            *self.system_users_only_fields
        )
        _nodes_map = {n.key: n for n in _nodes}
        self.add_ungrouped_nodes(_nodes_map, _nodes_keys)
        _assets_map = {a.id: a for a in _assets}
        _system_users_map = {s.id: s for s in _system_users}
        return _nodes_map, _assets_map, _system_users_map

    def get_serializer_queryset(self, nodes_items):
        """
        将id转为object，同时构造queryset
        :param nodes_items:
        [
            {
                'key': node.key,
                'assets_amount': 10
                'assets': {
                    asset.id: {
                        system_user.id: actions,
                    },
                },
            },
        ]
        """
        queryset = []
        _node_map, _assets_map, _system_users_map = self.get_maps(nodes_items)
        for item in nodes_items:
            key = item["key"]
            node = _node_map.get(key)
            if not node:
                continue
            node._assets_amount = item["assets_amount"]
            assets_granted = []
            for asset_id, system_users_ids_action in item["assets"].items():
                asset = _assets_map.get(asset_id)
                if not asset:
                    continue
                system_user_granted = []
                for system_user_id, action in system_users_ids_action.items():
                    system_user = _system_users_map.get(system_user_id)
                    if not system_user:
                        continue
                    system_user.actions = action
                    system_user_granted.append(system_user)
                asset.system_users_granted = system_user_granted
                assets_granted.append(asset)
            node.assets_granted = assets_granted
            queryset.append(node)
        return queryset

    def get_serializer(self, nodes_items, many=True):
        queryset = self.get_serializer_queryset(nodes_items)
        return super().get_serializer(queryset, many=many)

    def get_queryset(self):
        user = self.get_object()
        self.util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
        system_user_id = self.request.query_params.get('system_user')
        if system_user_id:
            self.util.filter_permissions(
                system_users=system_user_id
            )
        nodes_items = self.util.get_nodes_with_assets()
        return nodes_items

    def get_permissions(self):
        if self.kwargs.get('pk') is None:
            self.permission_classes = (IsValidUser,)
        return super().get_permissions()


class UserGrantedNodesWithAssetsAsTreeApi(UserGrantedNodesWithAssetsApi):
    serializer_class = TreeNodeSerializer
    permission_classes = (IsOrgAdminOrAppUser,)
    system_user_id = None
    nodes_only_fields = ParserNode.nodes_only_fields
    assets_only_fields = ParserNode.assets_only_fields
    system_users_only_fields = ParserNode.system_users_only_fields

    def get_serializer(self, nodes_items, many=True):
        _queryset = super().get_serializer_queryset(nodes_items)
        queryset = []

        for node in _queryset:
            data = ParserNode.parse_node_to_tree_node(node)
            queryset.append(data)
            for asset in node.assets_granted:
                system_users = asset.system_users_granted
                data = ParserNode.parse_asset_to_tree_node(node, asset, system_users)
                queryset.append(data)
        queryset = sorted(queryset)
        return self.serializer_class(queryset, many=True)


class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView):
    permission_classes = (IsOrgAdminOrAppUser,)
    
    def get(self, request, *args, **kwargs):
        user_id = request.query_params.get('user_id', '')
        asset_id = request.query_params.get('asset_id', '')
        system_id = request.query_params.get('system_user_id', '')
        action_name = request.query_params.get('action_name', '')
        cache_policy = self.request.query_params.get("cache_policy", '0')

        try:
            asset_id = uuid.UUID(asset_id)
            system_id = uuid.UUID(system_id)
        except ValueError:
            return Response({'msg': False}, status=403)

        user = get_object_or_404(User, id=user_id)
        util = AssetPermissionUtil(user, cache_policy=cache_policy)
        assets = util.get_assets()
        for asset in assets:
            if asset_id == asset["id"]:
                action = asset["system_users"].get(system_id)
                if action and action_name in Action.value_to_choices(action):
                    return Response({'msg': True}, status=200)
                break
        return Response({'msg': False}, status=403)


class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, RetrieveAPIView):
    permission_classes = (IsOrgAdminOrAppUser,)
    serializer_class = serializers.ActionsSerializer

    def get_object(self):
        user_id = self.request.query_params.get('user_id', '')
        asset_id = self.request.query_params.get('asset_id', '')
        system_id = self.request.query_params.get('system_user_id', '')

        user = get_object_or_404(User, id=user_id)

        util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
        assets = util.get_assets()
        actions = 0
        for asset in assets:
            if asset_id == asset["id"]:
                actions = asset["system_users"].get(system_id, 0)
                break
        return {"actions": actions}
