import asyncio
import logging
from contextlib import suppress

from defence360agent.api import inactivity
from defence360agent.contracts.plugins import MessageSink
from defence360agent.subsys.persistent_state import register_lock_file
from defence360agent.utils import Scope, recurring_check
from defence360agent.utils.check_lock import check_lock
from defence360agent.subsys import nodejs_rasp

logger = logging.getLogger(__name__)

# An idle agent exits. Imunify360 wakes it once the time this lock file
# holds has come (check_recurrent.py, cron, every five minutes); ImunifyAV
# runs the overdue reconcile at its next start, which an RPC call (the
# hourly imunify-patch refresh cron among them) brings.
LOCK_FILE = register_lock_file("nodejs-rasp-reconcile", Scope.AV_IM360)


class NodejsRasp(MessageSink):
    SCOPE = Scope.AV_IM360
    # As often as the feature flags themselves sync (FeatureFlagsSync).
    PERIOD = 3600

    async def create_sink(self, loop):
        self._task = loop.create_task(self._reconcile_recurrent())

    async def shutdown(self):
        self._task.cancel()
        with suppress(asyncio.CancelledError):
            await self._task

    async def _reconcile_recurrent(self):
        # A download or Apache outage must not end the loop for good.
        await recurring_check(
            check_lock,
            consecutive_err_limit=float("inf"),
            check_period_first=True,
            check_lock_period=self.PERIOD,
            lock_file=LOCK_FILE,
        )(self._reconcile)()

    @staticmethod
    async def _reconcile():
        # An install must not be cut short by the idle shutdown.
        with inactivity.track.task("nodejs-rasp"):
            await nodejs_rasp.reconcile()
