import abc
from urllib.parse import parse_qsl

from django.conf import settings
from django.db.models import F, Value, CharField
from rest_framework.generics import ListAPIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.generics import get_object_or_404

from assets.models import Asset, Account
from assets.utils import KubernetesTree
from assets.api import SerializeToTreeNodeMixin
from perms.hands import Node
from perms.models import PermNode
from perms.utils.user_permission import (
    UserGrantedNodesQueryUtils, UserGrantedAssetsQueryUtils,
)
from perms.utils import PermAccountUtil
from perms.utils.permission import AssetPermissionUtil
from common.utils import get_object_or_none, lazyproperty
from common.utils.common import timeit

from ..mixin import SelfOrPKUserMixin
from .mixin import RebuildTreeMixin

__all__ = [
    'UserGrantedK8sAsTreeApi',
    'UserPermedNodesWithAssetsAsTreeApi',
    'UserPermedNodeChildrenWithAssetsAsTreeApi'
]


class BaseUserNodeWithAssetAsTreeApi(SelfOrPKUserMixin, RebuildTreeMixin, SerializeToTreeNodeMixin,
                                     ListAPIView):

    def list(self, request, *args, **kwargs):
        nodes, assets = self.get_nodes_assets()
        tree_nodes = self.serialize_nodes(nodes, with_asset_amount=True)
        tree_assets = self.serialize_assets(assets, node_key=self.node_key_for_serializer_assets)
        data = list(tree_nodes) + list(tree_assets)
        return Response(data=data)

    @abc.abstractmethod
    def get_nodes_assets(self):
        return [], []

    @lazyproperty
    def node_key_for_serializer_assets(self):
        return None


class UserPermedNodesWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
    query_node_util: UserGrantedNodesQueryUtils
    query_asset_util: UserGrantedAssetsQueryUtils

    def get_nodes_assets(self):
        perm_ids = AssetPermissionUtil().get_permissions_for_user(self.request.user, flat=True)
        self.query_node_util = UserGrantedNodesQueryUtils(self.request.user, perm_ids)
        self.query_asset_util = UserGrantedAssetsQueryUtils(self.request.user, perm_ids)
        ung_nodes, ung_assets = self._get_nodes_assets_for_ungrouped()
        fav_nodes, fav_assets = self._get_nodes_assets_for_favorite()
        all_nodes, all_assets = self._get_nodes_assets_for_all()
        nodes = list(ung_nodes) + list(fav_nodes) + list(all_nodes)
        assets = list(ung_assets) + list(fav_assets) + list(all_assets)
        return nodes, assets

    @timeit
    def _get_nodes_assets_for_ungrouped(self):
        if not settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
            return [], []
        node = self.query_node_util.get_ungrouped_node()
        assets = self.query_asset_util.get_ungroup_assets()
        assets = assets.annotate(parent_key=Value(node.key, output_field=CharField())) \
            .prefetch_related('platform')
        return [node], assets

    @timeit
    def _get_nodes_assets_for_favorite(self):
        node = self.query_node_util.get_favorite_node()
        assets = self.query_asset_util.get_favorite_assets()
        assets = assets.annotate(parent_key=Value(node.key, output_field=CharField())) \
            .prefetch_related('platform')
        return [node], assets

    def _get_nodes_assets_for_all(self):
        nodes = self.query_node_util.get_whole_tree_nodes(with_special=False)
        if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
            assets = self.query_asset_util.get_direct_granted_nodes_assets()
        else:
            assets = self.query_asset_util.get_all_granted_assets()
        assets = assets.annotate(parent_key=F('nodes__key')).prefetch_related('platform')
        return nodes, assets


class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
    """ 用户授权的节点的子节点与资产树 """

    def get_nodes_assets(self):
        nodes = PermNode.objects.none()
        assets = Asset.objects.none()
        query_node_util = UserGrantedNodesQueryUtils(self.user)
        query_asset_util = UserGrantedAssetsQueryUtils(self.user)
        node_key = self.query_node_key
        if not node_key:
            nodes = query_node_util.get_top_level_nodes()
        elif node_key == PermNode.UNGROUPED_NODE_KEY:
            assets = query_asset_util.get_ungroup_assets()
        elif node_key == PermNode.FAVORITE_NODE_KEY:
            assets = query_asset_util.get_favorite_assets()
        else:
            nodes = query_node_util.get_node_children(node_key)
            assets = query_asset_util.get_node_assets(node_key)
        assets = assets.prefetch_related('platform')
        return nodes, assets

    @lazyproperty
    def query_node_key(self):
        node_key = self.request.query_params.get('key', None)
        if node_key is not None:
            return node_key
        node_id = self.request.query_params.get('id', None)
        node = get_object_or_none(Node, id=node_id)
        node_key = getattr(node, 'key', None)
        return node_key

    @lazyproperty
    def node_key_for_serializer_assets(self):
        return self.query_node_key


class UserGrantedK8sAsTreeApi(
    SelfOrPKUserMixin,
    ListAPIView
):
    """ 用户授权的K8s树 """

    @staticmethod
    def asset(asset_id):
        kwargs = {'id': asset_id, 'is_active': True}
        asset = get_object_or_404(Asset, **kwargs)
        return asset

    def get_accounts(self, asset):
        util = PermAccountUtil()
        accounts = util.get_permed_accounts_for_user(self.user, asset)
        ignore_username = [Account.AliasAccount.INPUT, Account.AliasAccount.USER]
        accounts = filter(lambda x: x.username not in ignore_username, accounts)
        accounts = list(accounts)
        return accounts

    def list(self, request: Request, *args, **kwargs):
        tree_id = request.query_params.get('tree_id')
        key = request.query_params.get('key', {})

        tree = []
        parent_info = dict(parse_qsl(key))
        account_username = parent_info.get('account')

        asset_id = parent_info.get('asset_id')
        asset_id = tree_id if not asset_id else asset_id

        if tree_id and not account_username:
            asset = self.asset(asset_id)
            accounts = self.get_accounts(asset)
            asset_node = KubernetesTree(tree_id).as_asset_tree_node(asset)
            tree.append(asset_node)
            for account in accounts:
                account_node = KubernetesTree(tree_id).as_account_tree_node(
                    account, parent_info,
                )
                tree.append(account_node)
        else:
            tree = KubernetesTree(key).async_tree_node(parent_info)
        return Response(data=tree)