"""
"""
import os
from tempfile import tempdir

from pulsar.manager_factory import build_managers
from pulsar.cache import Cache
from pulsar.tools import ToolBox
from pulsar.tools.authorization import get_authorizer
from pulsar import messaging
from galaxy.objectstore import build_object_store_from_config
try:
    # If galaxy-tool-util or Galaxy >=19.09 present.
    from galaxy.tool_util.deps import build_dependency_manager
    DependencyManager = None
except ImportError:
    # If galaxy-lib or Galaxy <19.05 present.
    from galaxy.tools.deps import DependencyManager
    build_dependency_manager = None
try:
    # If galaxy-lib or Galaxy <19.05 present.
    from galaxy.jobs.metrics import JobMetrics
except ImportError:
    # If galaxy-job-metrics or Galaxy >=19.09 present.
    from galaxy.job_metrics import JobMetrics
from galaxy.util.bunch import Bunch

from logging import getLogger
log = getLogger(__name__)

DEFAULT_PRIVATE_TOKEN = None
DEFAULT_FILES_DIRECTORY = "files"
DEFAULT_STAGING_DIRECTORY = os.path.join(DEFAULT_FILES_DIRECTORY, "staging")
DEFAULT_PERSISTENCE_DIRECTORY = os.path.join(DEFAULT_FILES_DIRECTORY, "persisted_data")


NOT_WHITELIST_WARNING = "Starting the Pulsar without a toolbox to white-list." + \
                        "Ensure this application is protected by firewall or a configured private token."
MULTIPLE_MANAGERS_MESSAGE = "app.only_manager accessed with multiple managers configured"


class PulsarApp(object):

    def __init__(self, **conf):
        if conf is None:
            conf = {}
        self.__setup_staging_directory(conf.get("staging_directory", DEFAULT_STAGING_DIRECTORY))
        self.__setup_private_token(conf.get("private_token", DEFAULT_PRIVATE_TOKEN))
        self.__setup_persistence_directory(conf.get("persistence_directory", None))
        self.__setup_tool_config(conf)
        self.__setup_object_store(conf)
        self.__setup_dependency_manager(conf)
        self.__setup_job_metrics(conf)
        self.__setup_managers(conf)
        self.__setup_file_cache(conf)
        self.__setup_bind_to_message_queue(conf)
        self.__recover_jobs()
        self.ensure_cleanup = conf.get("ensure_cleanup", False)

    def shutdown(self, timeout=None):
        for manager in self.managers.values():
            try:
                manager.shutdown(timeout)
            except Exception:
                pass

        if self.__queue_state:
            self.__queue_state.deactivate()
            if self.ensure_cleanup:
                self.__queue_state.join(timeout)

    def __setup_bind_to_message_queue(self, conf):
        message_queue_url = conf.get("message_queue_url", None)
        queue_state = None
        if message_queue_url:
            queue_state = messaging.bind_app(self, message_queue_url, conf)
        self.__queue_state = queue_state

    def __setup_tool_config(self, conf):
        """
        Setups toolbox object and authorization mechanism based
        on supplied toolbox_path.
        """
        tool_config_files = conf.get("tool_config_files", None)
        if not tool_config_files:
            # For compatibility with Galaxy, allow tool_config_file
            # option name.
            tool_config_files = conf.get("tool_config_file", None)
        toolbox = None
        if tool_config_files:
            toolbox = ToolBox(tool_config_files)
        else:
            log.info(NOT_WHITELIST_WARNING)
        self.toolbox = toolbox
        self.authorizer = get_authorizer(toolbox)

    def __setup_staging_directory(self, staging_directory):
        self.staging_directory = os.path.abspath(staging_directory)

    def __setup_managers(self, conf):
        self.managers = build_managers(self, conf)

    def __recover_jobs(self):
        for manager in self.managers.values():
            manager.recover_active_jobs()

    def __setup_private_token(self, private_token):
        self.private_token = private_token
        if private_token:
            log.info("Securing Pulsar web app with private key, please verify you are using HTTPS so key cannot be obtained by monitoring traffic.")

    def __setup_persistence_directory(self, persistence_directory):
        persistence_directory = persistence_directory or DEFAULT_PERSISTENCE_DIRECTORY
        if persistence_directory == "__none__":
            persistence_directory = None
        self.persistence_directory = persistence_directory

    def __setup_file_cache(self, conf):
        file_cache_dir = conf.get('file_cache_dir', None)
        self.file_cache = Cache(file_cache_dir) if file_cache_dir else None

    def __setup_object_store(self, conf):
        if "object_store_config_file" not in conf and "object_store_config" not in conf:
            self.object_store = None
            return

        config_obj_kwds = dict(
            file_path=conf.get("object_store_file_path", None),
            object_store_check_old_style=False,
            job_working_directory=conf.get("object_store_job_working_directory", None),
            new_file_path=conf.get("object_store_new_file_path", tempdir),
            umask=int(conf.get("object_store_umask", "0000")),
            jobs_directory=None,
        )
        config_dict = None
        if conf.get("object_store_config_file"):
            config_obj_kwds["object_store_config_file"] = conf['object_store_config_file']
        else:
            config_dict = conf["object_store_config"]

        object_store_config = Bunch(**config_obj_kwds)
        self.object_store = build_object_store_from_config(object_store_config, config_dict=config_dict)

    def __setup_dependency_manager(self, conf):
        default_tool_dependency_dir = "dependencies"
        if build_dependency_manager is not None:
            self.dependency_manager = build_dependency_manager(app_config_dict=conf, default_tool_dependency_dir=default_tool_dependency_dir)
        else:
            dependencies_dir = conf.get("tool_dependency_dir", default_tool_dependency_dir)
            resolvers_config_file = conf.get("dependency_resolvers_config_file", "dependency_resolvers_conf.xml")
            conda_config = {k: v for k, v in conf.items() if k.startswith("conda_")}
            self.dependency_manager = DependencyManager(dependencies_dir, resolvers_config_file, app_config=conda_config)

    def __setup_job_metrics(self, conf):
        job_metrics = conf.get("job_metrics", None)
        if job_metrics is None:
            job_metrics_config_file = conf.get("job_metrics_config_file", "job_metrics_conf.xml")
            job_metrics = JobMetrics(job_metrics_config_file)
        self.job_metrics = job_metrics

    @property
    def only_manager(self):
        """Convience accessor for tests and contexts with sole manager."""
        assert len(self.managers) == 1, MULTIPLE_MANAGERS_MESSAGE
        return list(self.managers.values())[0]