//go:build integration

package integration

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

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

func TestEndToEnd(t *testing.T) {
	apiBase := getenv("E2E_EZAPI_URL", "http://localhost:8080")
	adminToken := getenv("EZ_ADMIN_TOKEN", "admin-token") // Make sure this matches docker-compose

	client := &http.Client{Timeout: 5 * time.Second}

	// 1. Admin creates a Master Key
	masterPayload := map[string]interface{}{
		"name":           "test-master",
		"group":          "default",
		"max_child_keys": 2,
		"global_qps":     10,
	}
	var masterResp struct {
		MasterKey string `json:"master_key"`
	}
	postJSONWithAuth(t, client, apiBase+"/admin/masters", masterPayload, &masterResp, adminToken)
	masterKey := masterResp.MasterKey
	require.NotEmpty(t, masterKey)

	// 2. Master issues a Child Key
	childPayload := map[string]interface{}{
		"group":  "default",
		"scopes": "chat:write",
	}
	var childResp struct {
		KeySecret string `json:"key_secret"`
	}
	postJSONWithAuth(t, client, apiBase+"/v1/tokens", childPayload, &childResp, masterKey)
	childKey := childResp.KeySecret
	require.NotEmpty(t, childKey)

	// 3. (Conceptual) Use Child Key to access balancer - this part can't be fully tested here
	// but we've verified the key generation flow.
	t.Logf("Admin Token: %s", adminToken)
	t.Logf("Master Key: %s", masterKey)
	t.Logf("Child Key: %s", childKey)
}

func postJSONWithAuth[T any](t *testing.T, client *http.Client, url string, body interface{}, out T, token string) T {
	b, err := sonic.Marshal(body)
	require.NoError(t, err)

	req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(b))
	require.NoError(t, err)
	req.Header.Set("Content-Type", "application/json")
	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}

	resp, err := client.Do(req)
	require.NoError(t, err)
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
		data, _ := io.ReadAll(resp.Body)
		t.Fatalf("expected 200 or 201, got %d. Body: %s", resp.StatusCode, string(data))
	}

	if out != nil {
		data, _ := io.ReadAll(resp.Body)
		if len(data) > 0 {
			require.NoError(t, sonic.Unmarshal(data, out))
		}
	}
	return out
}

func getenv(key, def string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return def
}
