// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package telemetry_test

import (
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"golang.org/x/telemetry"
	"golang.org/x/telemetry/counter"
	"golang.org/x/telemetry/counter/countertest"
	"golang.org/x/telemetry/internal/configtest"
	"golang.org/x/telemetry/internal/regtest"
	it "golang.org/x/telemetry/internal/telemetry"
	"golang.org/x/telemetry/internal/testenv"
)

// These environment variables are used to coordinate the fork+exec subprocess
// started by TestStart.
const (
	runStartEnv     = "X_TELEMETRY_TEST_START"
	telemetryDirEnv = "X_TELEMETRY_TEST_START_TELEMETRY_DIR"
	uploadURLEnv    = "X_TELEMETRY_TEST_START_UPLOAD_URL"
)

func TestMain(m *testing.M) {
	// TestStart can't use internal/regtest, because Start itself also uses
	// fork+exec to start a subprocess, which does not interact well with the
	// fork+exec trick used by regtest.RunProg.
	if os.Getenv(runStartEnv) != "" {
		os.Exit(runStart())
	}
	os.Exit(m.Run())
}

func TestStart(t *testing.T) {
	testenv.SkipIfUnsupportedPlatform(t)
	testenv.MustHaveExec(t)

	// This test sets up a telemetry environment, and then runs a test program
	// that increments a counter, and uploads via telemetry.Start.

	t.Setenv(telemetryDirEnv, t.TempDir())

	uploaded := false
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		uploaded = true
		body, err := io.ReadAll(r.Body)
		if err != nil {
			t.Errorf("error reading body: %v", err)
		} else {
			if body := string(body); !strings.Contains(body, "teststart/counter") {
				t.Errorf("upload does not contain \"teststart/counter\":\n%s", body)
			}
		}
	}))
	t.Setenv(uploadURLEnv, server.URL)

	uc := regtest.CreateTestUploadConfig(t, []string{"teststart/counter"}, nil)
	env := configtest.LocalProxyEnv(t, uc, "v1.2.3")
	for _, e := range env {
		kv := strings.SplitN(e, "=", 2)
		t.Setenv(kv[0], kv[1])
	}

	// Run the runStart function below, via a fork+exec trick.
	exe, err := os.Executable()
	if err != nil {
		t.Fatal(err)
	}
	cmd := exec.Command(exe, "** TestStart **")      // this unused arg is just for ps(1)
	cmd.Env = append(os.Environ(), runStartEnv+"=1") // see TestMain
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("program failed unexpectedly (%v)\n%s", err, out)
	}

	if !uploaded {
		t.Fatalf("no upload occurred on %v", os.Getpid())
	}
}

func runStart() int {
	mustGetEnv := func(envvar string) string {
		v := os.Getenv(envvar)
		if v == "" {
			log.Fatalf("missing required environment var %q", envvar)
		}
		return v
	}

	countertest.Open(mustGetEnv(telemetryDirEnv))
	counter.Inc("teststart/counter")
	if err := it.Default.SetModeAsOf("on", time.Now().Add(-8*24*time.Hour)); err != nil {
		log.Fatalf("setting mode: %v", err)
	}

	res := telemetry.Start(telemetry.Config{
		// No need to set TelemetryDir since the Default dir is already set by countertest.Open.
		Upload:          true,
		UploadURL:       mustGetEnv(uploadURLEnv),
		UploadStartTime: time.Now().Add(8 * 24 * time.Hour),
	})
	res.Wait()
	return 0
}
