/*
 * SPDX-FileCopyrightText: 2024 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */
//------------------------------------------------------------------------------------------------
//         THE FOLLOWING CODE EXTENDS THE 'DeviceImpl' CLASS IN A DEDICATED .CPP FILE
//      WE SIMPLY REUSE THE APPROACH USED FOR THE LOGGING SERVICE FOR CONSISTENCY REASONS
//------------------------------------------------------------------------------------------------

#include <tango/server/DeviceImpl.h>

#include <tango/internal/utils.h>

#include <tango/client/DbDevice.h>
#include <tango/common/telemetry/consts.h>
#include <tango/common/telemetry/InterfaceFactory.h>
#include <tango/common/telemetry/TelemetryAppender.h>
#include <tango/server/Util.h>

namespace Tango
{

//-------------------------------------------------------------------------------------------------
//  method : DeviceImpl::initialize_telemetry_interface
//-------------------------------------------------------------------------------------------------
void DeviceImpl::initialize_telemetry_interface()
{
    auto details = telemetry::Configuration::Server{device_class->get_name(), device_name};
    telemetry::Configuration cfg{device_name, "tango", details};

    // Apply setting from properties
    auto *util = Tango::Util::instance();
    if(util->use_db())
    {
        DbData db_data;
        db_data.emplace_back("telemetry_enable");
        db_data.emplace_back("telemetry_topics");
        db_data.emplace_back("telemetry_types");
        db_data.emplace_back("telemetry_tracing_exporters");
        db_data.emplace_back("telemetry_tracing_endpoints");
        db_data.emplace_back("telemetry_logging_exporters");
        db_data.emplace_back("telemetry_logging_endpoints");

        try
        {
            db_dev->get_property(db_data);
        }
        catch(Tango::DevFailed &)
        {
            std::stringstream sstr;
            sstr << "Database error while trying to retrieve telemetry properties for the device";
            TANGO_THROW_EXCEPTION(API_DatabaseAccess, sstr.str());
        }

        std::string property;
        if(!db_data[0].is_empty())
        {
            // telemetry_enable
            db_data[0] >> property;
            auto val = detail::to_boolean(detail::to_lower(property));
            if(!val.has_value())
            {
                std::stringstream sstr;
                sstr << "Could not parse telemetry_enable property '";
                sstr << property << "'";
                TANGO_THROW_EXCEPTION(API_InvalidArgs, sstr.str());
            }
            cfg.enabled = val.value();
        }
        if(!db_data[1].is_empty())
        {
            db_data[1] >> property;
            cfg.set_topics(property);
        }
        if(!db_data[2].is_empty())
        {
            // telemetry_types
            db_data[2] >> property;
            cfg.enable_telemetry_types(property);
        }
        if(!db_data[3].is_empty())
        {
            // telemetry_tracing_exporters and telemetry_tracing_endpoints
            db_data[3] >> property;
            std::string endpoints;
            if(!db_data[4].is_empty())
            {
                db_data[4] >> endpoints;
            }
            cfg.set_tracing_collectors(property, endpoints, "properties");
        }
        else if(!db_data[4].is_empty())
        {
            db_data[4] >> property;
            std::stringstream sstr;
            sstr << "telemetry_tracing_endpoints property is set to '";
            sstr << property << "' with no corresponding telemetry_tracing_exporters property set";
            TANGO_THROW_EXCEPTION(API_InvalidArgs, sstr.str());
        }
        if(!db_data[5].is_empty())
        {
            // telemetry_logging exporters and telemetry_logging_endpoints
            db_data[5] >> property;
            std::string endpoints;
            if(!db_data[6].is_empty())
            {
                db_data[6] >> endpoints;
            }
            cfg.set_logging_collectors(property, endpoints, "properties");
        }
        else if(!db_data[6].is_empty())
        {
            db_data[6] >> property;
            std::stringstream sstr;
            sstr << "telemetry_logging_endpoints property is set to '";
            sstr << property << "' with no corresponding telemetry_logging_exporters property set";
            TANGO_THROW_EXCEPTION(API_InvalidArgs, sstr.str());
        }
    }
    // this might throw an exception in case there's no valid endpoint defined
    telemetry_interface = Tango::telemetry::InterfaceFactory::create(cfg);

    // attach a telemetry appender to the logger
    log4tango::Appender *appender = telemetry_interface->get_appender();
    get_logger()->add_appender(appender);
}

//-------------------------------------------------------------------------------------------------
//  method : DeviceImpl::cleanup_telemetry_interface
//-------------------------------------------------------------------------------------------------
void DeviceImpl::cleanup_telemetry_interface() noexcept
{
    get_logger()->remove_appender(Tango::telemetry::kTelemetryLogAppenderName);

    telemetry_interface.reset();
}

// enable the telemetry interface (enable tracing).
void DeviceImpl::enable_telemetry() noexcept
{
    telemetry()->enable();
}

// disable the telemetry interface (disable tracing).
void DeviceImpl::disable_telemetry() noexcept
{
    telemetry()->disable();
}

bool DeviceImpl::is_telemetry_enabled() noexcept
{
    return telemetry()->is_enabled();
}

void DeviceImpl::set_telemetry_topics(const std::vector<std::string> &topics)
{
    telemetry()->set_topics(topics);
}

std::vector<std::string> DeviceImpl::get_telemetry_topics() noexcept
{
    return telemetry()->get_topics();
}

void DeviceImpl::set_telemetry_tracing(bool enable_flag) noexcept
{
    telemetry()->set_tracing(enable_flag);
}

bool DeviceImpl::is_telemetry_tracing_enabled() noexcept
{
    return telemetry()->is_tracing_enabled();
}

void DeviceImpl::set_telemetry_tracing_endpoints(telemetry::Configuration::CollectorSet collectors)
{
    auto cfg = telemetry()->get_configuration();
    cfg.set_tracing_collectors(collectors);
    telemetry()->update_tracing_configuration(cfg);
}

telemetry::Configuration::CollectorSet DeviceImpl::get_telemetry_tracing_endpoints()
{
    auto cfg = telemetry()->get_configuration();
    return cfg.get_tracing_collectors();
}

void DeviceImpl::add_telemetry_tracing_endpoint(telemetry::Configuration::Collector collector)
{
    auto cfg = telemetry()->get_configuration();
    cfg.add_tracing_collector(collector);
    telemetry()->update_tracing_configuration(cfg);
}

void DeviceImpl::remove_telemetry_tracing_endpoint(telemetry::Configuration::Collector collector)
{
    auto cfg = telemetry()->get_configuration();
    cfg.remove_tracing_collector(collector);
    telemetry()->update_tracing_configuration(cfg);
}

void DeviceImpl::update_telemetry_logging()
{
    auto *appender = get_logger()->get_appender(telemetry::kTelemetryLogAppenderName);
    auto *telemetryAppender = dynamic_cast<telemetry::TelemetryAppender *>(appender);
    if(telemetryAppender != nullptr)
    {
        telemetryAppender->cleanup_logger_provider();
        telemetryAppender->init_logger_provider();
    }
}

void DeviceImpl::set_telemetry_logging(bool enable_flag)
{
    if(telemetry()->is_logging_enabled() != enable_flag)
    {
        auto cfg = telemetry()->get_configuration();
        cfg.logging_enabled = enable_flag;
        telemetry()->update_configuration(cfg);
        update_telemetry_logging();
    }
}

bool DeviceImpl::is_telemetry_logging_enabled() noexcept
{
    return telemetry()->is_logging_enabled();
}

void DeviceImpl::set_telemetry_logging_endpoints(telemetry::Configuration::CollectorSet collectors)
{
    auto cfg = telemetry()->get_configuration();
    cfg.set_logging_collectors(collectors);
    telemetry()->update_configuration(cfg);
    update_telemetry_logging();
}

telemetry::Configuration::CollectorSet DeviceImpl::get_telemetry_logging_endpoints()
{
    auto cfg = telemetry()->get_configuration();
    return cfg.get_logging_collectors();
}

void DeviceImpl::add_telemetry_logging_endpoint(telemetry::Configuration::Collector collector)
{
    auto cfg = telemetry()->get_configuration();
    cfg.add_logging_collector(collector);
    telemetry()->update_configuration(cfg);
    update_telemetry_logging();
}

void DeviceImpl::remove_telemetry_logging_endpoint(telemetry::Configuration::Collector collector)
{
    auto cfg = telemetry()->get_configuration();
    cfg.remove_logging_collector(collector);
    telemetry()->update_configuration(cfg);
    update_telemetry_logging();
}

} // namespace Tango
