//go:build integration
// +build integration

package integration_test

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"net/http"
	"testing"
	"time"

	"github.com/everything-on-mysql/cassandra-on-mysql/internal/config"
)

func TestMetricsEndpoint(t *testing.T) {
	requireMySQLConfig(t)

	adminAddr := reserveTCPAddr(t)
	host, port, stop, err := startProxyWithConfig(mysqlCfg, func(conf *config.Config) {
		conf.Server.Admin.Metrics.Enabled = true
		conf.Server.Admin.Metrics.Path = "/metrics"
		conf.Server.Admin.Address = adminAddr
	})
	if err != nil {
		t.Fatalf("start proxy failed: %v", err)
	}
	defer stop()

	session := newSessionForHostPort(t, host, port)
	// Request counters are registered lazily, so seed one request before scraping.
	if _, err := scanRows(session, "SELECT key FROM system.local"); err != nil {
		t.Fatalf("seed metrics query failed: %v", err)
	}

	body := waitForHTTP(t, fmt.Sprintf("http://%s/metrics", adminAddr))
	if !bytes.Contains(body, []byte("proxy_request_total")) {
		t.Fatalf("expected proxy_request_total in metrics output")
	}
}

func TestGlobalConnectionLimit(t *testing.T) {
	requireMySQLConfig(t)

	host, port, stop, err := startProxyWithConfig(mysqlCfg, func(conf *config.Config) {
		conf.Server.MaxConnections = 1
	})
	if err != nil {
		t.Fatalf("start proxy failed: %v", err)
	}
	defer stop()

	addr := fmt.Sprintf("%s:%d", host, port)
	conn1, err := net.Dial("tcp", addr)
	if err != nil {
		t.Fatalf("dial first connection failed: %v", err)
	}
	defer conn1.Close()

	conn2, err := net.Dial("tcp", addr)
	if err != nil {
		t.Fatalf("dial second connection failed: %v", err)
	}
	defer conn2.Close()

	_ = conn2.SetReadDeadline(time.Now().Add(200 * time.Millisecond))
	buf := make([]byte, 1)
	_, err = conn2.Read(buf)
	if err == nil {
		t.Fatalf("expected second connection to be closed")
	}
	if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
		t.Fatalf("expected second connection to close, got timeout")
	}
}

func reserveTCPAddr(t *testing.T) string {
	t.Helper()
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("reserve admin addr failed: %v", err)
	}
	addr := ln.Addr().String()
	_ = ln.Close()
	return addr
}

func waitForHTTP(t *testing.T, url string) []byte {
	t.Helper()
	deadline := time.Now().Add(2 * time.Second)
	client := &http.Client{Timeout: 500 * time.Millisecond}
	for time.Now().Before(deadline) {
		resp, err := client.Get(url)
		if err == nil {
			body, readErr := io.ReadAll(resp.Body)
			_ = resp.Body.Close()
			if readErr == nil && resp.StatusCode == http.StatusOK {
				return body
			}
		}
		time.Sleep(50 * time.Millisecond)
	}
	t.Fatalf("failed to reach %s", url)
	return nil
}
