from __future__ import annotations

import time

import psutil
import requests

from clarabot_nano.utility.thread import AbstractThread
from clarabot_nano.core.spawn.base import AbstractSpawnWithThread


class AbstractSpawnHealthPoller(AbstractThread):
    def __init__(self, spawn):
        AbstractThread.__init__(self, spawn)

    def _thread(self, spawn: AbstractPolledSpawnWithThread):
        try:
            interval = max(1.0, (spawn.health_timeout / 2) - 1)
            while not spawn.is_disposed():
                if self._check_process_health(spawn):
                    spawn.set_healthy()
                time.sleep(interval)
        finally:
            spawn.clear_healthy()  # consider process unhealthy

    def _check_process_health(self, spawn) -> bool:
        """
        Perform a check specific for the type of process and return True if it is healthy.
        """
        raise NotImplementedError()


class AbstractSpawnHealthHTTPPoller(AbstractSpawnHealthPoller):
    def _thread(self, spawn: AbstractPolledSpawnWithThread):
        with requests.Session() as s:
            self._session = s
            AbstractSpawnHealthPoller._thread(self, spawn)

    def _check_process_health(self, spawn) -> bool:
        raise NotImplementedError()


class AbstractPolledSpawnWithThread(AbstractSpawnWithThread):
    DEFAULT_HEALTH_TIMEOUT = 20.0

    def __init__(
        self,
        health_timeout: float = DEFAULT_HEALTH_TIMEOUT,
        start_timeout: float = DEFAULT_HEALTH_TIMEOUT * 3,
    ):
        AbstractSpawnWithThread.__init__(self, health_timeout=health_timeout, start_timeout=start_timeout)

    def start(self):
        AbstractSpawnWithThread.start(self)
        self._create_health_poller().start()

    def kill(self):
        raise NotImplementedError()

    def _stop_spawn(self, *args):
        raise NotImplementedError()

    def _start_spawn(self, *args) -> psutil.Popen:
        raise NotImplementedError()

    def _create_health_poller(self) -> AbstractSpawnHealthPoller:
        raise NotImplementedError()
