package telescope

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func TestRingOrderAndOverflow(t *testing.T) {
	tel := New(3, nil, nil)
	for i := 0; i < 5; i++ {
		tel.add(Entry{Path: string(rune('a' + i))})
	}
	got := tel.Entries()
	if len(got) != 3 {
		t.Fatalf("want 3 entries, got %d", len(got))
	}
	// جدید-به-قدیم: e, d, c (a و b بازنویسی شده‌اند).
	want := []string{"e", "d", "c"}
	for i, w := range want {
		if got[i].Path != w {
			t.Errorf("entry %d: want %q got %q", i, w, got[i].Path)
		}
	}
	// شناسه‌ها باید افزایشی و یکتا باشند.
	if got[0].ID != 5 || got[2].ID != 3 {
		t.Errorf("ids wrong: %d..%d", got[2].ID, got[0].ID)
	}
}

func TestMiddlewareCapturesPayloadHeadersResponseMasked(t *testing.T) {
	tel := New(10, nil, nil)
	h := tel.Middleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusBadRequest)
		_, _ = w.Write([]byte(`{"token":"xyz","message":"bad"}`))
	}))
	req := httptest.NewRequest(http.MethodPost, "/api/x",
		strings.NewReader(`{"password":"secret","kind":"coin"}`))
	req.Header.Set("Authorization", "Bearer supersecret")
	h.ServeHTTP(httptest.NewRecorder(), req)

	e := tel.Entries()
	if len(e) != 1 || e[0].Status != 400 {
		t.Fatalf("expected one 400 entry, got %+v", e)
	}
	// payload برای هر درخواست ثبت می‌شود و مقادیرِ حساس ماسک می‌شوند.
	if !strings.Contains(e[0].ReqBody, `"password":"***"`) {
		t.Errorf("password not masked: %s", e[0].ReqBody)
	}
	if !strings.Contains(e[0].ReqBody, `"kind":"coin"`) {
		t.Errorf("non-secret field lost: %s", e[0].ReqBody)
	}
	// پاسخ ثبت و توکنِ آن ماسک می‌شود.
	if !strings.Contains(e[0].RespBody, `"token":"***"`) {
		t.Errorf("response token not masked: %s", e[0].RespBody)
	}
	// هدرِ Authorization در نمایش پنهان می‌شود.
	if strings.Contains(e[0].Headers, "supersecret") {
		t.Errorf("authorization header leaked: %s", e[0].Headers)
	}
	if !strings.Contains(e[0].Headers, "Authorization: ***") {
		t.Errorf("authorization header not masked: %s", e[0].Headers)
	}
}

func TestMobileFromLoginBody(t *testing.T) {
	tel := New(10, nil, nil)
	h := tel.Middleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
	}))
	req := httptest.NewRequest(http.MethodPost, "/api/auth/login-otp",
		strings.NewReader(`{"mobile":"09120000000"}`))
	h.ServeHTTP(httptest.NewRecorder(), req)
	e := tel.Entries()
	if len(e) != 1 || e[0].Mobile != "09120000000" {
		t.Errorf("mobile not extracted from login body, got %+v", e)
	}
}

func TestResolveMobileByUserID(t *testing.T) {
	tel := New(10,
		func(*http.Request) int64 { return 42 },
		func(id int64) string {
			if id == 42 {
				return "09121112233"
			}
			return ""
		})
	h := tel.Middleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
	}))
	h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/api/me", nil))
	e := tel.Entries()
	if len(e) != 1 || e[0].Mobile != "09121112233" || e[0].UserID != 42 {
		t.Errorf("mobile not resolved by user id, got %+v", e)
	}
}

func TestMiddlewareSkipsWS(t *testing.T) {
	tel := New(10, nil, nil)
	h := tel.Middleware(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
	}))
	h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/ws", nil))
	if len(tel.Entries()) != 0 {
		t.Error("ws request should be skipped")
	}
}
