package adminapi

import (
	"context"
	"net"
	"net/http"
	"testing"
	"time"

	"galaxy/gateway/internal/app"
	"galaxy/gateway/internal/config"
	"galaxy/gateway/internal/restapi"
	"galaxy/gateway/internal/testutil"

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

func TestMetricsAreReachableOnlyOnAdminListener(t *testing.T) {
	t.Parallel()

	logger, _ := testutil.NewObservedLogger(t)
	telemetryRuntime := testutil.NewTelemetryRuntime(t, logger)

	publicAddr := unusedTCPAddr(t)
	adminAddr := unusedTCPAddr(t)

	publicCfg := config.DefaultPublicHTTPConfig()
	publicCfg.Addr = publicAddr
	adminCfg := config.DefaultAdminHTTPConfig()
	adminCfg.Addr = adminAddr

	restServer := restapi.NewServer(publicCfg, restapi.ServerDependencies{
		Logger:    logger,
		Telemetry: telemetryRuntime,
	})
	adminServer := NewServer(adminCfg, telemetryRuntime.Handler(), logger)

	application := app.New(
		config.Config{
			ShutdownTimeout:   time.Second,
			PublicHTTP:        publicCfg,
			AdminHTTP:         adminCfg,
			AuthenticatedGRPC: config.DefaultAuthenticatedGRPCConfig(),
		},
		restServer,
		adminServer,
	)

	ctx, cancel := context.WithCancel(context.Background())
	resultCh := make(chan error, 1)
	go func() {
		resultCh <- application.Run(ctx)
	}()
	defer func() {
		cancel()
		select {
		case err := <-resultCh:
			require.NoError(t, err)
		case <-time.After(time.Second):
			require.FailNow(t, "application did not stop")
		}
	}()

	waitForHTTPStatus(t, "http://"+publicAddr+"/healthz", http.StatusOK)
	waitForHTTPStatus(t, "http://"+adminAddr+"/metrics", http.StatusOK)

	publicMetricsResp, err := http.Get("http://" + publicAddr + "/metrics")
	require.NoError(t, err)
	defer func() {
		require.NoError(t, publicMetricsResp.Body.Close())
	}()
	assert.Equal(t, http.StatusNotFound, publicMetricsResp.StatusCode)
}

func waitForHTTPStatus(t *testing.T, rawURL string, wantStatus int) {
	t.Helper()

	require.Eventually(t, func() bool {
		resp, err := http.Get(rawURL)
		if err != nil {
			return false
		}
		defer func() {
			_ = resp.Body.Close()
		}()

		return resp.StatusCode == wantStatus
	}, time.Second, 10*time.Millisecond)
}

func unusedTCPAddr(t *testing.T) string {
	t.Helper()

	listener, err := net.Listen("tcp", "127.0.0.1:0")
	require.NoError(t, err)

	addr := listener.Addr().String()
	require.NoError(t, listener.Close())

	return addr
}
