/*
 * SPDX-FileCopyrightText: 2025 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */
#ifndef TANGO_INTERNAL_TELEMETRY_INTERFACEIMPLEMENTATION_H
#define TANGO_INTERNAL_TELEMETRY_INTERFACEIMPLEMENTATION_H

#include <tango/common/telemetry/Configuration.h>
#include <tango/common/telemetry/Interface.h>
#include <tango/common/telemetry/Span.h>

#include <opentelemetry/context/propagation/global_propagator.h>
#include <opentelemetry/exporters/ostream/span_exporter_factory.h>
#include <opentelemetry/exporters/otlp/otlp_grpc_exporter_factory.h>
#include <opentelemetry/exporters/otlp/otlp_http_exporter_factory.h>
#include <opentelemetry/logs/provider.h>
#include <opentelemetry/sdk/resource/resource.h>
#include <opentelemetry/sdk/trace/batch_span_processor_factory.h>
#include <opentelemetry/sdk/trace/batch_span_processor_options.h>
#include <opentelemetry/sdk/trace/processor.h>
#include <opentelemetry/sdk/trace/simple_processor_factory.h>
#include <opentelemetry/sdk/trace/tracer_provider.h>
#include <opentelemetry/sdk/trace/tracer_provider_factory.h>
#include <opentelemetry/trace/propagation/http_trace_context.h>
#include <opentelemetry/trace/provider.h>

namespace Tango::telemetry
{

//-----------------------------------------------------------------------------------------
// Ptr to opentelemetry tracer
//-----------------------------------------------------------------------------------------
using TracerPtr = opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer>;

//---------------------------------------------------------------------------------------------------------------------
// The opaque implementation classes.
//
// Such classes are usually inner classes of their associated public class but, due to a technical constraint we have
// here on one of them, we decide, for all of them, to put their respective forward declaration here.
//---------------------------------------------------------------------------------------------------------------------

//-----------------------------------------------------------------------------------------
// InterfaceImplementation
//-----------------------------------------------------------------------------------------
class InterfaceImplementation final
{
  public:
    //-------------------------------------------------------------------------------------
    // Ctor
    //-------------------------------------------------------------------------------------
    explicit InterfaceImplementation(const Configuration &config) :
        cfg{config}
    {
        init_tracer_provider();
        // init the global propagator
        init_global_propagator();
    }

    void terminate() noexcept;

    void init_tracer_provider();

    opentelemetry::sdk::resource::ResourceAttributes get_resource_attributes();

    void cleanup_tracer_provider() noexcept;

    void init_global_propagator() noexcept;

    TracerPtr get_tracer() const noexcept;

    SpanPtr instantiate_span(opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> otel_span) const noexcept;

    SpanPtr start_span(const std::string &name, const Attributes &attributes, const Span::Kind &kind) noexcept;

    SpanPtr start_span(const std::string &name,
                       const Attributes &attributes,
                       const opentelemetry::trace::StartSpanOptions &options) noexcept;
    SpanPtr get_current_span() const noexcept;

    opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> get_current_opentelemetry_span() noexcept;

    // Avoid lifetime issues with static storage duration objects
    opentelemetry::nostd::shared_ptr<const opentelemetry::context::RuntimeContextStorage> rcsKeep{
        opentelemetry::context::RuntimeContext::GetConstRuntimeContextStorage()};
    opentelemetry::nostd::shared_ptr<const opentelemetry::trace::TraceState> tsKeep{
        opentelemetry::trace::TraceState::GetDefault()};

    // default interface flag
    bool is_default_interface{false};

    // the interface configuration
    Configuration cfg;

    // the opentelemetry tracer/logger provider
    opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> provider;
    opentelemetry::nostd::shared_ptr<opentelemetry::logs::LoggerProvider> logger_provider;

    // the actual opentelemetry tracer attached to this interface
    opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer> tracer;

    // the global propagator initialization flag (singleton)
    static bool global_propagator_initialized;

    // the associated mutex protecting its instantiation against race conditions
    // required for pure clients cause in servers, devices are instantiated sequentially
    static std::mutex global_propagator_initialized_mutex;

    // the default interface - returned when none attached to the current thread
    static Tango::telemetry::InterfacePtr default_telemetry_interface;

    // the associated mutex protecting its instantiation against race conditions
    static std::mutex default_telemetry_interface_mutex;
};

} // namespace Tango::telemetry

#endif // TANGO_COMMON_TELEMETRY_INTERFACE_H
