from typing import List, Optional

import flask
from viggocore.common import exception, utils
from viggocore.common.subsystem import manager
from viggocore.common.subsystem import operation
from viggocore.subsystem.role.resource import Role, RoleDataViewType
from viggocore.subsystem.policy.resource import Policy
from sqlalchemy import insert


class Create(operation.Create):

    def pre(self, session, **kwargs):
        if kwargs.get('data_view', None) is None:
            kwargs['data_view'] = RoleDataViewType.DOMAIN
        return super().pre(session, **kwargs)


class CreatePolicies(operation.Operation):

    def _filter_capabilities(self, routes, capabilities, policies, resources):
        policies_capabilities_ids = [policy.capability_id
                                     for policy in policies]
        capabilities = [c for c in capabilities
                        if c.id not in policies_capabilities_ids]
        routes_ids = [route.id for route in routes
                      if (route.url, route.method) in resources]

        filtered_capabilities = [capability for capability in capabilities
                                 if capability.route_id in routes_ids]
        return filtered_capabilities

    def _create_policy(self, role_id: str, capability_id: str) -> Policy:
        return Policy(id=utils.random_uuid(),
                      role_id=role_id,
                      capability_id=capability_id)

    def pre(self, session, id: str, **kwargs) -> bool:
        self.role_id = id
        self.application_id = kwargs.get('application_id', None)
        resources = kwargs.get('resources', None)

        if ((not (self.role_id and self.application_id)) or resources is None):
            raise exception.OperationBadRequest()

        try:
            self.manager.api.applications().get(id=self.application_id)
        except exception.NotFound:
            raise exception.BadRequest('Invalid application')

        routes = self.manager.api.routes().list(
            sysadmin=False, bypass=False, active=True)
        capabilities = self.manager.api.capabilities().list(
            application_id=self.application_id)
        policies = self.manager.api.policies().list(role_id=self.role_id)
        self.capabilities = self._filter_capabilities(routes,
                                                      capabilities,
                                                      policies,
                                                      resources)
        return self.driver.get(id, session) is not None

    def do(self, session, **kwargs) -> None:
        policies = [self._create_policy(self.role_id, capability.id)
                    for capability in self.capabilities]
        session.bulk_save_objects(policies)


class CreateRoles(operation.Operation):

    def pre(self, roles: List[Role], session, **kwargs) -> bool:
        self.roles = roles
        return True

    def do(self, session, **kwargs) -> List[Role]:
        session.add_all(self.roles)
        return self.roles


class GetRoleByName(operation.Operation):

    def pre(self, role_name: str, session, **kwargs) -> bool:
        self.roles = self.manager.list(name=role_name)
        return self.roles is not []

    def do(self, session, **kwargs) -> Optional[Role]:
        return self.roles[0]


class Manager(manager.Manager):

    def __init__(self, driver):
        super().__init__(driver)
        self.create = Create(self)
        self.create_policies = CreatePolicies(self)
        self.create_roles = CreateRoles(self)
        self.get_role_by_name = GetRoleByName(self)
