//go:build e2e

package testkit

import (
	"bytes"
	"context"
	"fmt"
	"net"
	"net/http"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"
)

type LocalServer struct {
	BaseURL string
	cmd     *exec.Cmd
}

func reservePort(t *testing.T) int {
	t.Helper()
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("reserve local port: %v", err)
	}
	defer ln.Close()
	return ln.Addr().(*net.TCPAddr).Port
}

func waitReady(ctx context.Context, baseURL string) error {
	client := &http.Client{Timeout: ReadyProbeHTTPTimeout()}
	readyURL := strings.TrimRight(baseURL, "/") + "/readyz"
	ticker := time.NewTicker(ReadyProbeInterval())
	defer ticker.Stop()

	for {
		req, _ := http.NewRequestWithContext(ctx, http.MethodGet, readyURL, nil)
		resp, err := client.Do(req)
		if err == nil {
			resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				return nil
			}
		}
		select {
		case <-ctx.Done():
			return fmt.Errorf("wait ready timeout for %s", readyURL)
		case <-ticker.C:
		}
	}
}

func repoRootFromThisFile(t *testing.T) string {
	t.Helper()
	_, thisFile, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatalf("resolve repo root: runtime caller unavailable")
	}
	root := filepath.Clean(filepath.Join(filepath.Dir(thisFile), "..", ".."))
	return root
}

// StartLocalE2EServer starts cmd/agentteam-e2e-server on a random local port.
func StartLocalE2EServer(t *testing.T) LocalServer {
	t.Helper()

	port := reservePort(t)
	baseURL := fmt.Sprintf("http://%s:%d", DefaultLocalServerHost, port)
	cmd := exec.Command("go", "run", "./cmd/agentteam-e2e-server", "--http-address", fmt.Sprintf("%s:%d", DefaultLocalServerHost, port))
	cmd.Dir = repoRootFromThisFile(t)
	var logs bytes.Buffer
	cmd.Stdout = &logs
	cmd.Stderr = &logs

	if err := cmd.Start(); err != nil {
		t.Fatalf("start local e2e server: %v", err)
	}

	t.Cleanup(func() {
		if cmd.Process != nil {
			_ = cmd.Process.Kill()
			_, _ = cmd.Process.Wait()
		}
	})

	ctx, cancel := context.WithTimeout(context.Background(), EmbeddedServerReadyTimeout())
	defer cancel()
	if err := waitReady(ctx, baseURL); err != nil {
		t.Fatalf("local e2e server not ready: %v\nlogs:\n%s", err, logs.String())
	}

	return LocalServer{
		BaseURL: baseURL,
		cmd:     cmd,
	}
}
