using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using StellaOps.Scanner.Worker.Options;

namespace StellaOps.Scanner.Worker.Diagnostics;

public static class TelemetryExtensions
{
    public static void ConfigureScannerWorkerTelemetry(this IHostApplicationBuilder builder, ScannerWorkerOptions options)
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(options);

        var telemetry = options.Telemetry;
        if (!telemetry.EnableTelemetry)
        {
            return;
        }

        var openTelemetry = builder.Services.AddOpenTelemetry();

        openTelemetry.ConfigureResource(resource =>
        {
            var version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown";
            resource.AddService(telemetry.ServiceName, serviceVersion: version, serviceInstanceId: Environment.MachineName);
            resource.AddAttributes(new[]
            {
                new KeyValuePair<string, object>("deployment.environment", builder.Environment.EnvironmentName),
            });

            foreach (var kvp in telemetry.ResourceAttributes)
            {
                if (string.IsNullOrWhiteSpace(kvp.Key) || kvp.Value is null)
                {
                    continue;
                }

                resource.AddAttributes(new[] { new KeyValuePair<string, object>(kvp.Key, kvp.Value) });
            }
        });

        if (telemetry.EnableTracing)
        {
            openTelemetry.WithTracing(tracing =>
            {
                tracing.AddSource(ScannerWorkerInstrumentation.ActivitySourceName);
                ConfigureExporter(tracing, telemetry);
            });
        }

        if (telemetry.EnableMetrics)
        {
            openTelemetry.WithMetrics(metrics =>
            {
                metrics
                    .AddMeter(ScannerWorkerInstrumentation.MeterName)
                    .AddRuntimeInstrumentation()
                    .AddProcessInstrumentation();

                ConfigureExporter(metrics, telemetry);
            });
        }
    }

    private static void ConfigureExporter(TracerProviderBuilder tracing, ScannerWorkerOptions.TelemetryOptions telemetry)
    {
        if (!string.IsNullOrWhiteSpace(telemetry.OtlpEndpoint))
        {
            tracing.AddOtlpExporter(options =>
            {
                options.Endpoint = new Uri(telemetry.OtlpEndpoint);
            });
        }

        if (telemetry.ExportConsole || string.IsNullOrWhiteSpace(telemetry.OtlpEndpoint))
        {
            tracing.AddConsoleExporter();
        }
    }

    private static void ConfigureExporter(MeterProviderBuilder metrics, ScannerWorkerOptions.TelemetryOptions telemetry)
    {
        if (!string.IsNullOrWhiteSpace(telemetry.OtlpEndpoint))
        {
            metrics.AddOtlpExporter(options =>
            {
                options.Endpoint = new Uri(telemetry.OtlpEndpoint);
            });
        }

        if (telemetry.ExportConsole || string.IsNullOrWhiteSpace(telemetry.OtlpEndpoint))
        {
            metrics.AddConsoleExporter();
        }
    }
}
