import asyncio

from defence360agent.model.simplification import run_in_executor
from defence360agent.rpc_tools.lookup import RootEndpoints, bind
from defence360agent.rpc_tools.utils import run_in_executor_decorator
from defence360agent.utils import Scope
from im360.contracts.config import Protector
from im360.subsys.webshield import update_remote_proxy_config
from im360.internals.core.ipset.ip import IPSetRemoteProxy
from im360.model.firewall import RemoteProxy, RemoteProxyGroup


class RemoteProxyEndpoints(RootEndpoints):
    SCOPE = Scope.IM360

    @bind("remote-proxy", "list")
    @run_in_executor_decorator
    def remote_proxy_list(self, by_group=None, by_source=None):
        return {"items": RemoteProxy.list(by_group, by_source, None)}

    @bind("remote-proxy", "add")
    async def remote_proxy_add(self, name, networks):
        await run_in_executor(
            asyncio.get_event_loop(),
            lambda: RemoteProxy.add_many(
                name, RemoteProxyGroup.MANUAL, networks
            ),
        )
        async with Protector.RULE_EDIT_LOCK:
            for network in networks:
                await IPSetRemoteProxy().add(network, 0)
        await update_remote_proxy_config()

    @bind("remote-proxy", "delete")
    async def remote_proxy_delete(self, networks):
        deleted = await run_in_executor(
            asyncio.get_event_loop(),
            lambda: RemoteProxy.delete_networks(
                RemoteProxyGroup.MANUAL, networks
            ),
        )
        async with Protector.RULE_EDIT_LOCK:
            for network in deleted:
                await IPSetRemoteProxy().delete(network)
        if len(deleted):
            await update_remote_proxy_config()

    @bind("remote-proxy", "group", "enable")
    async def remote_proxy_group_enable(self, name, source):
        loop = asyncio.get_event_loop()
        changed = await run_in_executor(
            loop, lambda: RemoteProxyGroup.set_enabled(name, source, True)
        )
        if changed:
            networks = await run_in_executor(
                loop, lambda: RemoteProxy.list(name, source, None)
            )
            async with Protector.RULE_EDIT_LOCK:
                for network in networks:
                    await IPSetRemoteProxy().add(network["network"])
            if len(networks):
                await update_remote_proxy_config()

    @bind("remote-proxy", "group", "disable")
    async def remote_proxy_group_disable(self, name, source):
        loop = asyncio.get_event_loop()
        changed = await run_in_executor(
            loop, lambda: RemoteProxyGroup.set_enabled(name, source, False)
        )
        if changed:
            networks = await run_in_executor(
                loop, lambda: RemoteProxy.list(name, source, None)
            )
            async with Protector.RULE_EDIT_LOCK:
                for network in networks:
                    await IPSetRemoteProxy().delete(network["network"])
            if len(networks):
                await update_remote_proxy_config()
