// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

package api

import (
	"fmt"
	stdlog "log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	"github.com/google/uuid"

	"github.com/DataDog/datadog-agent/pkg/trace/config"
	"github.com/DataDog/datadog-agent/pkg/trace/log"
)

const (
	// debuggerIntakeURLTemplate specifies the template for obtaining the intake URL along with the site.
	debuggerIntakeURLTemplate = "https://debugger-intake.%s/api/v2/debugger"
)

// symDBProxyHandler returns an http.Handler proxying requests to the logs intake. If the logs intake url cannot be
// parsed, the returned handler will always return http.StatusInternalServerError with a clarifying message.
func (r *HTTPReceiver) symDBProxyHandler() http.Handler {
	hostTags := fmt.Sprintf("host:%s,default_env:%s,agent_version:%s", r.conf.Hostname, r.conf.DefaultEnv, r.conf.AgentVersion)
	if orch := r.conf.FargateOrchestrator; orch != config.OrchestratorUnknown {
		hostTags = hostTags + ",orchestrator:fargate_" + strings.ToLower(string(orch))
	}
	intake := fmt.Sprintf(debuggerIntakeURLTemplate, r.conf.Site)
	if v := r.conf.SymDBProxy.DDURL; v != "" {
		intake = v
	} else if site := r.conf.Site; site != "" {
		intake = fmt.Sprintf(debuggerIntakeURLTemplate, site)
	}
	target, err := url.Parse(intake)
	if err != nil {
		log.Criticalf("Error parsing symbol database intake URL %q: %v", intake, err)
		return symDBErrorHandler(fmt.Errorf("error parsing symbol database intake URL %q: %v", intake, err))
	}
	apiKey := r.conf.APIKey()
	if k := r.conf.SymDBProxy.APIKey; k != "" {
		apiKey = strings.TrimSpace(k)
	}
	transport := newMeasuringForwardingTransport(
		r.conf.NewHTTPTransport(), target, apiKey, r.conf.SymDBProxy.AdditionalEndpoints, "datadog.trace_agent.debugger.", []string{}, r.statsd)
	return newSymDBProxy(r.conf, transport, hostTags)
}

// symDBErrorHandler always returns http.StatusInternalServerError with a clarifying message.
func symDBErrorHandler(err error) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		msg := fmt.Sprintf("SymDB Proxy is OFF: %v", err)
		http.Error(w, msg, http.StatusInternalServerError)
	})
}

// newSymDBProxy returns a new httputil.ReverseProxy proxying and augmenting requests with headers containing the tags.
func newSymDBProxy(conf *config.AgentConfig, transport http.RoundTripper, hostTags string) *httputil.ReverseProxy {
	cidProvider := NewIDProvider(conf.ContainerProcRoot, conf.ContainerIDFromOriginInfo)
	logger := log.NewThrottled(5, 10*time.Second) // limit to 5 messages every 10 seconds
	return &httputil.ReverseProxy{
		Director:  getSymDBDirector(hostTags, cidProvider, conf.ContainerTags),
		ErrorLog:  stdlog.New(logger, "symdb.Proxy: ", 0),
		Transport: transport,
	}
}

func getSymDBDirector(hostTags string, cidProvider IDProvider, containerTags func(string) ([]string, error)) func(*http.Request) {
	return func(req *http.Request) {
		req.Header.Set("DD-REQUEST-ID", uuid.New().String())
		req.Header.Set("DD-EVP-ORIGIN", "agent-symdb")
		q := req.URL.Query()
		containerID := cidProvider.GetContainerID(req.Context(), req.Header)
		tags := hostTags
		if ctags := getContainerTags(containerTags, containerID); ctags != "" {
			tags = fmt.Sprintf("%s,%s", tags, ctags)
		}
		if htags := req.Header.Get("X-Datadog-Additional-Tags"); htags != "" {
			tags = fmt.Sprintf("%s,%s", tags, htags)
		}
		if qtags := q.Get("ddtags"); qtags != "" {
			tags = fmt.Sprintf("%s,%s", tags, qtags)
		}
		req.Header.Set("X-Datadog-Additional-Tags", tags)
		log.Debugf("Setting header X-Datadog-Additional-Tags=%s for symdb proxy", tags)
	}
}
