package api

// auth_test.go kimlik dogrulama ve KIMLIK TAKLIDI davranisini sinar.
//
// CANLI BULGU: X-Fiko-User basligi dogrulanmadan guvenilirdi. Istemci istedigi
// kullaniciyi yazip baskasinin verisine erisebiliyordu ("hacker" kimligiyle
// konusma olusturulabildi). Bu testler regresyonu engeller.

import (
	"context"
	"io"
	"log/slog"
	"net"
	"net/http"
	"net/http/httptest"

	"github.com/valyala/fasthttp/fasthttputil"
	"strings"
	"testing"
	"time"

	"fiko-ai/internal/agent"
	"fiko-ai/internal/api/ws"
	"fiko-ai/internal/conversation"
	"fiko-ai/internal/event"
	"fiko-ai/internal/prompt"
)

// authServer, verilen anahtarla bir sunucu kurar.
func authServer(t *testing.T, authKey string) *Server {
	t.Helper()
	log := slog.New(slog.NewTextHandler(io.Discard, nil))
	svc := conversation.NewService(conversation.NewMemoryRepo())
	bus := event.NewMemoryBus(nil, 16)
	ag := agent.New(svc, nil, prompt.Default(), bus, log, agent.Config{})
	hub := ws.NewHub(ag, log, ws.Options{PingInterval: time.Hour, PongWait: 2 * time.Hour, WriteWait: time.Second})
	s := New(context.Background(), Config{
		Env: "test", Version: "test",
		AuthKey:         authKey,
		DefaultUserID:   "local",
		DefaultPageSize: 2, MaxPageSize: 5,
	}, Deps{Conversations: svc, Hub: hub, Bus: bus}, log)
	t.Cleanup(func() {
		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
		defer cancel()
		_ = s.Shutdown(ctx)
	})
	return s
}

func body(t *testing.T, resp *http.Response) string {
	t.Helper()
	defer resp.Body.Close()
	b, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Fatal(err)
	}
	return string(b)
}

// TestAuthBlocksWhenKeySet: anahtar tanimliyken korumali uc 401 vermeli.
func TestAuthBlocksWhenKeySet(t *testing.T) {
	s := authServer(t, "gizli-anahtar")
	for _, h := range []string{"", "yanlis-anahtar", "Gizli-Anahtar", "gizli-anahtar2"} {
		req := httptest.NewRequest("GET", "/api/conversations", nil)
		if h != "" {
			req.Header.Set(AuthHeader, h)
		}
		resp, err := s.App().Test(req)
		if err != nil {
			t.Fatal(err)
		}
		if resp.StatusCode != 401 {
			t.Errorf("başlık %q ile 401 beklenir, %d geldi", h, resp.StatusCode)
		}
	}
	req := httptest.NewRequest("GET", "/api/conversations", nil)
	req.Header.Set(AuthHeader, "gizli-anahtar")
	resp, err := s.App().Test(req)
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != 200 {
		t.Errorf("doğru anahtarla 200 beklenir, %d geldi", resp.StatusCode)
	}
}

// TestAuthOpenWhenKeyEmpty: anahtar yoksa akis BOZULMAZ (gelistirme varsayilani).
func TestAuthOpenWhenKeyEmpty(t *testing.T) {
	s := authServer(t, "")
	resp, err := s.App().Test(httptest.NewRequest("GET", "/api/conversations", nil))
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != 200 {
		t.Errorf("anahtar yokken akış açık olmalı, %d geldi", resp.StatusCode)
	}
}

// TestHealthAlwaysOpen: health/modeller/system korumasiz kalmali (health probe).
func TestHealthAlwaysOpen(t *testing.T) {
	s := authServer(t, "gizli-anahtar")
	for _, p := range []string{"/api/health", "/api/models", "/api/system"} {
		resp, err := s.App().Test(httptest.NewRequest("GET", p, nil))
		if err != nil {
			t.Fatal(err)
		}
		if resp.StatusCode == 401 {
			t.Errorf("%s korumasız olmalı (health probe)", p)
		}
	}
}

// TestSystemReportsAuthDisabled: anahtar yoksa panel ACIKCA uyarmali.
func TestSystemReportsAuthDisabled(t *testing.T) {
	s := authServer(t, "")
	resp, err := s.App().Test(httptest.NewRequest("GET", "/api/system", nil))
	if err != nil {
		t.Fatal(err)
	}
	b := body(t, resp)
	if !strings.Contains(b, `"auth"`) {
		t.Fatalf("/api/system auth durumu bildirmeli: %s", b)
	}
	if !strings.Contains(b, "KAPALI") {
		t.Errorf("kimlik doğrulama kapalıyken uyarı görünmeli: %s", b)
	}
}

func TestSecureEqual(t *testing.T) {
	if secureEqual("", "x") || secureEqual("x", "") {
		t.Error("boş taraf eşleşmemeli")
	}
	if !secureEqual("abc", "abc") {
		t.Error("aynı değerler eşleşmeli")
	}
	if secureEqual("abc", "abcd") {
		t.Error("farklı uzunluk eşleşmemeli")
	}
}

// TestAuthRealServer, gercek bir HTTP sunucusu uzerinde dogrular (fasthttp
// basligi normalize ediyor olabilir; App().Test() bunu yansitmayabilir).
func TestAuthRealServer(t *testing.T) {
	s := authServer(t, "gizli-anahtar")
	ln := fasthttputil.NewInmemoryListener()
	go func() { _ = s.App().Listener(ln) }()
	defer func() { _ = s.Shutdown(context.Background()) }()

	client := &http.Client{Transport: &http.Transport{DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
		return ln.Dial()
	}}}

	for _, tc := range []struct {
		header string
		want   int
	}{
		{"", 401},
		{"gizli-anahtar", 200},
		// HTTP basligi normalizasyonu: Fiber degerin son/ilk bosluğunu kirpar,
		{"Gizli-Anahtar", 401},
	} {
		req, err := http.NewRequest("GET", "http://x/api/conversations", nil)
		if err != nil {
			t.Fatal(err)
		}
		if tc.header != "" {
			req.Header.Set(AuthHeader, tc.header)
		}
		resp, err := client.Do(req)
		if err != nil {
			t.Fatal(err)
		}
		resp.Body.Close()
		if resp.StatusCode != tc.want {
			t.Errorf("başlık %q: %d beklenir, %d geldi", tc.header, tc.want, resp.StatusCode)
		}
	}
}
