import logging
import os
from json import JSONDecodeError

import helper
import zmq
from messagehandlerbuilder import SchedCompMessageHandlerBuilder
from redis import StrictRedis
from schedcomp import (
    CronTabInteractor,
    CronTabService,
    EventDefinition,
    RedisLocker,
)
from timeutil import (
    RealClock,
    RealSleeper,
)
from zeromqmessagehandler import (
    ZeroMqMessageBus,
    ZeroMqMessageHandler,
)

COMP_NAME = "SchedComp"
logger = logging.getLogger(COMP_NAME)


def main():
    config_path = './config.json'
    if "CONFIG_PATH" in os.environ:
        config_path = os.environ["CONFIG_PATH"]
    if os.path.isdir(config_path):
        configs_files = []
        default_config = os.environ.get("DEFAULT_CONFIG_FILE", "config.json")
        complete_default_config_path = os.path.join(config_path, default_config)
        configs_files.append(complete_default_config_path)
        for file in os.listdir(config_path):
            complete_config_path = os.path.join(config_path, file)
            if os.path.isfile(complete_config_path) and \
                    file.endswith(".json") and \
                    complete_config_path != complete_default_config_path:
                configs_files.append(complete_config_path)
    else:
        configs_files = config_path.split(",")

    config = helper.read_json_from_file_as_dictionary(configs_files[0])
    if "cronTab" not in config:
        config["cronTab"] = []
    for crontab_file in configs_files[1:]:
        try:
            file_crontab = helper.read_json_from_file_as_dictionary(crontab_file)
            if not isinstance(file_crontab, list):
                raise Exception("The crontab file must be a list of event definitions: {}".format(crontab_file))
        except JSONDecodeError as ex:
            raise Exception("Error parsing config file {}".format(crontab_file)) from ex
        config["cronTab"].extend(file_crontab)

    log_path = config_path
    if "LOG_PATH" in os.environ:
        log_path = os.environ["LOG_PATH"]
    helper.config_logger(log_path, COMP_NAME, add_error_handler=True)

    logger.debug("Configuration file:\n{}".format(config))

    context = zmq.Context()
    message_bus = ZeroMqMessageBus(COMP_NAME, context)
    clock = RealClock()
    sleeper = RealSleeper()
    interactors = []

    locker = None
    if "redis" in config:
        locker = RedisLocker(
            redis_client=StrictRedis(
                host=config["redis"]["host"],
                port=config["redis"]["port"],
                db=config["redis"]["db"],
                socket_timeout=2,
                socket_connect_timeout=2,
                ssl=config["redis"].get("ssl", False),
            ),
            clock=clock,
        )

    for entry in config["cronTab"]:
        event_definition = EventDefinition(
            entry["subject"],
            entry.get("type"),
            entry.get("data"),
            entry["headers"]
        )

        interactors.append(
            CronTabInteractor(
                message_bus=message_bus,
                expression=entry["expression"],
                event_definition=event_definition,
                clock=clock,
                sleeper=sleeper,
                locker=locker,
            )
        )

    cron_tab_service = CronTabService(interactors)
    cron_tab_service.start()

    message_handler = ZeroMqMessageHandler(context,
                                           COMP_NAME,
                                           message_bus,
                                           SchedCompMessageHandlerBuilder())
    message_handler.handle_messages()


if __name__ == '__main__':
    main()
