#!/usr/bin/python3

##
# @file
# @ingroup rtctk_clients_deploymentDaemonClient
#
# @brief Entry point for rtctkDeploymentDaemonClient.
#
# @copyright
#   SPDX-FileCopyrightText: 2024-2026 European Southern Observatory (ESO) @n
#   SPDX-License-Identifier: LGPL-3.0-only

"""
This is the entry point for the RTC Toolkit Deployment Daemon Client
that provides a way to send commands to the Deployment daemon.
"""

import sys
import click
import json
from elt.log import CiiLogManager, CiiLogMessageBuilder  # pylint: disable=E0611,E0401
from rtctk.deployment_daemon.client import (  # pylint: disable=E0611,E0401
    DeploymentDaemonClient,
)
from rtctk.common.cli.exceptions import (  # pylint: disable=E0611,E0401
    format_error_message,
)
from rtctk.common.cli.argtypes import (  # pylint: disable=E0611,E0401
    LogLevel,
    UriParamType,
)
from rtctk.common.cli.config import (  # pylint: disable=E0611,E0401
    CommonConfigurationParameters,
    ConfigurationManager,
)
from rtctk.framework import (  # pylint: disable=E0611,E0401
    Uri,
    DataPointPath,
    RtctkException,
)

__version__ = "6.0.0"

_config_manager = ConfigurationManager(CommonConfigurationParameters, DataPointPath(""))
_defaults = _config_manager.get_config_defaults()
_deployment_daemon_client = DeploymentDaemonClient()


# Prevent Doxygen from trying to produce API documentation for the click CLI handling methods.
# The docstring comments will not be appropriate. Click's formatting tags can clash with Doxygen.
# And these methods anyway do not form part of the API.
## @cond __doxygen_ignore__


@click.group()
@click.version_option(version=__version__, message="%(version)s")
@click.option(
    "--service-discovery-endpoint",
    "--sde",
    type=UriParamType(),
    default=_defaults.service_discovery_endpoint.value,
    help="URI endpoint to the service discovery."
    f" Default: {_defaults.service_discovery_endpoint.value}",
)
@click.option(
    "--deployment-daemon-name",
    "--ddn",
    type=click.STRING,
    help=f"Name of deployment daemon instance. Default: {_deployment_daemon_client.dd_name}.",
)
@click.option(
    "--log-level",
    type=click.Choice(LogLevel),
    default=_defaults.log_level.value,
    help="Sets the global logging level for the tool. This overrides any"
    f" {_defaults.log_level.path} setting found in the configuration."
    f" Default: {_defaults.log_level.value}",
)
@click.option(
    "--log-to-file",
    is_flag=True,
    flag_value=True,
    help=f"Makes the tool log to file. This overrides any {_defaults.log_to_file.path} setting"
    f" found in the configuration. Default: {_defaults.log_to_file.value}",
)
@click.option(
    "--timeout",
    "-t",
    default=20,
    type=click.INT,
    help=f"Connection timeout [sec] Default: {_deployment_daemon_client.conn_to.seconds}",
)
def ddc(service_discovery_endpoint, deployment_daemon_name, log_level, log_to_file, timeout):
    """
    Command line client for interacting with the Deployment Daemon.
    """
    _config_manager.update_config_from_cli(
        service_discovery_endpoint=service_discovery_endpoint,
        log_level=log_level,
        log_to_file=log_to_file,
    )

    if deployment_daemon_name != None:
        _deployment_daemon_client.set_name(deployment_daemon_name)

    _deployment_daemon_client.set_timeouts(timeout)
    _deployment_daemon_client.set_service_discovery_uri(service_discovery_endpoint)


@ddc.command(name="Deploy")
@click.argument("deployment-set", type=click.STRING)
def deploy(deployment_set):
    """
    Invokes Deploy on the Deployment Daemon to deploy deployment set: DEPLOYMENT_SET
    """
    _deployment_daemon_client.cmd("Deploy", json.dumps(deployment_set))


@ddc.command(name="Undeploy")
def undeploy():
    """
    Invokes Undeploy on the Deployment Daemon to undeploy active deployment set.
    """
    _deployment_daemon_client.cmd("Undeploy")


@ddc.command(name="GetActiveDeployment")
def get_active_deployment():
    """
    Invokes GetActiveDeployment on the Deployment Daemon that returns active deployment set.
    """
    _deployment_daemon_client.cmd("GetActiveDeployment")


@ddc.command(name="GetDescription")
def get_description():
    """
    Invokes GetDescription on the Deployment Daemon that returns deployment set's description
    """
    _deployment_daemon_client.cmd("GetDescription")


@ddc.command(name="GetDeploymentSets")
def get_deployment_sets():
    """
    Invokes GetDeploymentSets on the Deployment Daemon that returns a list of deployment sets
    """
    _deployment_daemon_client.cmd("GetDeploymentSets")


@ddc.command(name="GetComponents")
def get_components():
    """
    Invokes GetComponents on the Deployment Daemon that returns a list of all components
    """
    _deployment_daemon_client.cmd("GetComponents")


@ddc.command(name="GetActiveComponents")
def get_active_components():
    """
    Invokes GetActiveComponents on the Deployment Daemon that returns a list of active components
    """
    _deployment_daemon_client.cmd("GetActiveComponents")


@ddc.command(name="StartComponent")
@click.argument("component-name", type=click.STRING)
def start_component(component_name):
    """
    Invokes StartComponent on the Deployment Daemon to start component with name: COMPONENT_NAME
    """
    _deployment_daemon_client.cmd("StartComponent", json.dumps(component_name))


@ddc.command(name="StopComponent")
@click.argument("component-name", type=click.STRING)
def stop_component(component_name):
    """
    Invokes StopComponent on the Deployment Daemon to stop component with name: COMPONENT_NAME
    """
    _deployment_daemon_client.cmd("StopComponent", json.dumps(component_name))


@ddc.command(name="GetServices")
def get_services():
    """
    Invokes GetServices on the Deployment Daemon that returns a list of all services
    """
    _deployment_daemon_client.cmd("GetServices")


@ddc.command(name="GetActiveServices")
def get_active_services():
    """
    Invokes GetActiveServices on the Deployment Daemon that returns a list of active services
    """
    _deployment_daemon_client.cmd("GetActiveServices")


@ddc.command(name="GetState")
def get_state():
    """
    Invokes GetState on the Deployment Daemon
    """
    _deployment_daemon_client.cmd("GetState")


@ddc.command(name="Reset")
def reset():
    """
    Invokes Reset on the Deployment Daemon
    """
    _deployment_daemon_client.cmd("Reset")


@ddc.command(name="Exit")
def exit():
    """
    Invokes Exit on the Deployment Daemon
    """
    _deployment_daemon_client.cmd("Exit")


@ddc.command(name="SetLogLevel")
@click.argument("log-info")
def set_log_level(log_info):
    """
    Sets Log Level of the Deployment Daemon to LOG_INFO

    LOG_INFO is a log level: DEBUG, INFO, ...

    """
    print(log_info)
    _deployment_daemon_client.cmd("SetLogLevel", log_info)


## @endcond


def main():
    """
    Program entry point function where basic logging is configured and exceptions are handled
    by printing and/or logging the error.
    """
    _config_manager
    logger = CiiLogManager.get_logger()

    try:
        ddc()  # pylint: disable=E1120
    except (RuntimeError, RtctkException) as err:
        logger.error(f"{format_error_message(err)}")
        print(f"ERROR: {format_error_message(err)}", file=sys.stderr)
        sys.exit(1)
    except Exception as err:
        # Unexpected exceptions, i.e. anything that is not explicitly a RuntimeError, are assumed to
        # be code bugs, so we log it and rethrow.
        msg = "An unexpected exception occurred"
        logger.fatal(CiiLogMessageBuilder.create_and_build_with_exception(msg, err))
        raise


if __name__ == "__main__":
    main()
