package logging

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

	"github.com/stretchr/testify/require"
)

func TestManagedGoBackgroundPanicFlushesBeforeProcessExit(t *testing.T) {
	if os.Getenv("AGENTTEAM_LOGGING_PANIC_CHILD") == "1" {
		cfg := fileLogConfig(os.Getenv("AGENTTEAM_LOGGING_PANIC_FILE"))
		cfg.FlushInterval = time.Hour
		pipeline, err := Open(cfg, nil)
		if err != nil {
			panic(err)
		}
		pipeline.Logger().Info("buffered before panic")
		Go(pipeline.Logger(), func() { panic("background boom") })
		select {}
	}

	path := filepath.Join(t.TempDir(), "panic.log")
	command := exec.Command(os.Args[0], "-test.run=^TestManagedGoBackgroundPanicFlushesBeforeProcessExit$")
	command.Env = append(os.Environ(),
		"AGENTTEAM_LOGGING_PANIC_CHILD=1",
		"AGENTTEAM_LOGGING_PANIC_FILE="+path,
	)
	command.Stdout = io.Discard
	command.Stderr = io.Discard
	err := command.Run()
	var exitError *exec.ExitError
	require.ErrorAs(t, err, &exitError)

	contents, err := os.ReadFile(path)
	require.NoError(t, err)
	logOutput := string(contents)
	require.Contains(t, logOutput, `"msg":"buffered before panic"`)
	require.Contains(t, logOutput, `"msg":"panic recovered"`)
	require.Contains(t, logOutput, `"panic_type":"string"`)
}

func TestHTTPErrorLogFlushesRecoveredHandlerPanic(t *testing.T) {
	path := filepath.Join(t.TempDir(), "http-panic.log")
	cfg := fileLogConfig(path)
	cfg.FlushInterval = time.Hour
	pipeline, err := Open(cfg, nil)
	require.NoError(t, err)
	t.Cleanup(func() { require.NoError(t, pipeline.Close()) })

	server := httptest.NewUnstartedServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		panic("request secret must not be logged")
	}))
	server.Config.ErrorLog = HTTPErrorLog(pipeline.Logger())
	server.Start()
	t.Cleanup(server.Close)
	_, _ = server.Client().Get(server.URL)

	require.Eventually(t, func() bool {
		contents, readErr := os.ReadFile(path)
		return readErr == nil && strings.Contains(string(contents), `"msg":"http handler panic recovered"`)
	}, time.Second, 10*time.Millisecond)
	contents, err := os.ReadFile(path)
	require.NoError(t, err)
	require.Contains(t, string(contents), `"stacktrace":"goroutine`)
	require.NotContains(t, string(contents), "request secret must not be logged")
}

func TestHTTPErrorLogFlushesServerErrorWithoutRawDetail(t *testing.T) {
	path := filepath.Join(t.TempDir(), "http-error.log")
	cfg := fileLogConfig(path)
	cfg.FlushInterval = time.Hour
	pipeline, err := Open(cfg, nil)
	require.NoError(t, err)
	t.Cleanup(func() { require.NoError(t, pipeline.Close()) })

	HTTPErrorLog(pipeline.Logger()).Print("TLS handshake error containing request secret")

	contents, err := os.ReadFile(path)
	require.NoError(t, err)
	require.Contains(t, string(contents), `"msg":"http server error"`)
	require.NotContains(t, string(contents), "request secret")
}

func TestHTTPErrorLogPreservesHTTP2PanicStack(t *testing.T) {
	path := filepath.Join(t.TempDir(), "http2-panic.log")
	cfg := fileLogConfig(path)
	cfg.FlushInterval = time.Hour
	pipeline, err := Open(cfg, nil)
	require.NoError(t, err)
	t.Cleanup(func() { require.NoError(t, pipeline.Close()) })

	HTTPErrorLog(pipeline.Logger()).Print("http2: panic serving 127.0.0.1: request secret\ngoroutine 42 [running]:\nhandler.stack")

	contents, err := os.ReadFile(path)
	require.NoError(t, err)
	require.Contains(t, string(contents), `"msg":"http handler panic recovered"`)
	require.Contains(t, string(contents), `"stacktrace":"goroutine 42 [running]:\nhandler.stack"`)
	require.NotContains(t, string(contents), "request secret")
}
