import logging
from threading import (
    Condition,
    Thread,
)
from typing import (
    Optional,
)

from ._Registration import Registration
from ._RegistrationRepository import RegistrationRepository

logger = logging.getLogger("ZeroMqServiceLocator.ServiceLocator")


class ServiceLocator(object):
    def __init__(
        self,
        registration_repository: RegistrationRepository,
        registration_expiration: float = 20,
    ):
        self.registration_repository = registration_repository
        self.clean_interval = registration_expiration / 2
        self.registration_expiration = registration_expiration

        self.clean_thread_running = True
        self.clean_thread_condition = Condition()
        self.clean_thread = Thread(target=self._run_clean_expired_registrations,
                                   name="ServiceLocatorCleanExpiredRegistrations")
        self.clean_thread.daemon = True
        self.clean_thread.start()

    def register_service(self, service_name, address):
        address = address.replace("*", "localhost")
        registration = Registration(
            service_name=service_name,
            service_address=address,
            registration_expiration=self.registration_expiration,
        )
        logger.debug("Created registration id: %s", registration.reg_id)

        self.registration_repository.add_registration(registration=registration)

        logger.debug("Current registrations: %s", self.registration_repository.get_all_registrations())
        logger.debug("Current services: %s", self.registration_repository.get_registered_services())

        return registration.reg_id

    def locate_service(self, service_name: str) -> Optional[str]:
        return self.registration_repository.lookup(service_name=service_name)

    def keep_alive(self, reg_id):
        self.registration_repository.keep_alive(reg_id=reg_id)

    def dispose(self):
        self.clean_thread_running = False
        with self.clean_thread_condition:
            self.clean_thread_condition.notify_all()
        self.clean_thread.join()

    def _run_clean_expired_registrations(self):
        while self.clean_thread_running:
            try:
                self.registration_repository.clean_registrations()
                if not self.clean_thread_running:
                    return

                with self.clean_thread_condition:
                    self.clean_thread_condition.wait(self.clean_interval)
            except (Exception,):
                logger.exception("Error cleaning expired registrations")
