package client

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"go.uber.org/atomic"
	"golang.org/x/net/http2"
)

func TestManagerServerClient(t *testing.T) {
	firstConn := atomic.NewString("")
	blockFirstConn := func(w http.ResponseWriter, r *http.Request) {
		t.Log("remote addr:", r.RemoteAddr)
		for firstConn.Load() == "" {
			firstConn.CompareAndSwap("", r.RemoteAddr)
		}
		if r.RemoteAddr == firstConn.Load() {
			// block util connection close.
			<-r.Context().Done()
		}
		w.WriteHeader(http.StatusOK)
	}

	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	addr := listener.Addr().(*net.TCPAddr).String()
	server := &http.Server{
		Handler: http.HandlerFunc(blockFirstConn),
		TLSConfig: &tls.Config{
			MinVersion: tls.VersionTLS13, // Use TLS 1.3 or higher for security
		},
	}
	_ = http2.ConfigureServer(server, &http2.Server{})
	go func() {
		err := server.ServeTLS(listener, "testdata/cert.pem", "testdata/key.pem")
		if err != nil && err != http.ErrServerClosed {
			t.Errorf("server error: %v", err)
		}
	}()
	defer server.Close()

	client := NewManagerServerClient()
	// set next recreate time to now+5s
	client.Lock()
	client.lastRecreate = time.Now().Add(-recreateManagerClientInterval + 5*time.Second)
	client.Unlock()

	url := fmt.Sprintf("https://%s/", addr)
	resp, err := doRequest(client, url, 3*time.Second)
	require.NotNil(t, err)
	if resp != nil {
		resp.Body.Close()
	}
	resp, err = doRequest(client, url, 3*time.Second)
	require.NotNil(t, err)
	if resp != nil {
		resp.Body.Close()
	}
	// now it should recreate new connection (3s+3s>5s)
	resp, err = doRequest(client, url, 3*time.Second)
	require.Nil(t, err)
	if resp != nil {
		resp.Body.Close()
	}
}

func doRequest(c *ManagerServerClient, url string, timeout time.Duration) (*http.Response, error) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	defer cancel()
	return c.Do(req)
}
