package integration_test

import (
    "compress/gzip"
    "encoding/hex"
    "integration/tart"
    "io"
    "net/http"
    "net/http/httptest"
    "net/url"
    "testing"

    "github.com/stretchr/testify/require"
    semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
    tracepkg "go.opentelemetry.io/proto/otlp/collector/trace/v1"
    v1 "go.opentelemetry.io/proto/otlp/common/v1"
    "google.golang.org/protobuf/proto"
)

func TestOpenTelemetry(t *testing.T) {
    // Start a mock OpenTelemetry collector server
    var traces []*tracepkg.ExportTraceServiceRequest

    server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
        var trace tracepkg.ExportTraceServiceRequest

        reader := request.Body
        var err error

        if request.Header.Get("Content-Encoding") == "gzip" {
            reader, err = gzip.NewReader(reader)
            require.NoError(t, err)
        }

        requestBytes, err := io.ReadAll(reader)
        require.NoError(t, err)

        switch request.Header.Get("Content-Type") {
        case "application/x-protobuf":
            require.NoError(t, proto.Unmarshal(requestBytes, &trace))
        default:
            require.FailNowf(t, "unsupported content type",
                "we do not support %q yet", request.Header.Get("Content-Type"))
        }

        traces = append(traces, &trace)

        var response tracepkg.ExportTraceServiceResponse

        responseBytes, err := proto.Marshal(&response)
        require.NoError(t, err)

        writer.Header().Set("Content-Type", "application/x-protobuf")
        _, err = writer.Write(responseBytes)
        require.NoError(t, err)
    }))

    // Start a "tart list" command
    serverURL, err := url.Parse(server.URL)
    require.NoError(t, err)

    t.Setenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", serverURL.JoinPath("v1/traces").String())
    t.Setenv("CIRRUS_SENTRY_TAGS", "A=B,C=D")
    t.Setenv("TRACEPARENT", "00-00000000000000000000000000000001-0000000000000001-01")

    _, _, err = tart.Tart(t, "list")
    require.NoError(t, err)

    // Ensure that the mock OpenTelemetry collector received a trace from "tart list"
    require.Len(t, traces, 1)

    resourceSpans := traces[0].GetResourceSpans()
    require.Len(t, resourceSpans, 1)

    // Ensure that service name and version resources are set
    resourceSpan := resourceSpans[0]
    stringAttributes := stringAttributesToMap(resourceSpan.GetResource().GetAttributes())
    require.Equal(t, "tart", stringAttributes[string(semconv.ServiceNameKey)])
    require.Equal(t, "SNAPSHOT", stringAttributes[string(semconv.ServiceVersionKey)])

    scopeSpans := resourceSpan.GetScopeSpans()
    require.Len(t, scopeSpans, 1)

    spans := scopeSpans[0].GetSpans()
    require.Len(t, spans, 1)

    // Ensure that the root span is correctly named
    span := spans[0]
    require.Equal(t, "list", span.Name)

    // Ensure that CIRRUS_SENTRY_TAGS are propagated
    stringAttributes = stringAttributesToMap(span.GetAttributes())
    require.Equal(t, stringAttributes["A"], "B")
    require.Equal(t, stringAttributes["C"], "D")

    // Ensure that W3C Trace Context is propagated
    require.Equal(t, "00000000000000000000000000000001", hex.EncodeToString(span.GetTraceId()))
    require.Equal(t, "0000000000000001", hex.EncodeToString(span.GetParentSpanId()))
}

func stringAttributesToMap(attributes []*v1.KeyValue) map[string]string {
    result := map[string]string{}

    for _, attribute := range attributes {
        result[attribute.GetKey()] = attribute.GetValue().GetStringValue()
    }

    return result
}
