package igw_test

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter/igw"
)

func TestSendPackageHappyPath(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/api/package/TEST3/file" {
			t.Errorf("неожиданный путь %q", r.URL.Path)
		}
		w.WriteHeader(http.StatusOK)
		_ = json.NewEncoder(w).Encode(map[string]string{"package_id": "pkg-123"})
	}))
	defer srv.Close()

	c := igw.NewClient(srv.URL, igw.WithRetry(0, time.Millisecond))
	id, err := c.SendPackage(context.Background(), "TEST3", "#M2MTR", []byte("<xml/>"))
	if err != nil {
		t.Fatal(err)
	}
	if id != "pkg-123" {
		t.Errorf("package_id = %q, ожидалось %q", id, "pkg-123")
	}
}

func TestSendPackageRetryOn500(t *testing.T) {
	calls := 0
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		calls++
		if calls < 2 {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.WriteHeader(http.StatusOK)
		_ = json.NewEncoder(w).Encode(map[string]string{"package_id": "pkg-retry"})
	}))
	defer srv.Close()

	c := igw.NewClient(srv.URL, igw.WithRetry(3, time.Millisecond))
	id, err := c.SendPackage(context.Background(), "TEST3", "#M2MTR", []byte("x"))
	if err != nil {
		t.Fatal(err)
	}
	if id != "pkg-retry" {
		t.Errorf("ожидалось pkg-retry, получено %q", id)
	}
	if calls < 2 {
		t.Errorf("ожидалось хотя бы 2 попытки, получено %d", calls)
	}
}

func TestSendPackage4xxNoRetry(t *testing.T) {
	calls := 0
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		calls++
		w.WriteHeader(http.StatusBadRequest)
		_, _ = w.Write([]byte(`{"error":"bad"}`))
	}))
	defer srv.Close()

	c := igw.NewClient(srv.URL, igw.WithRetry(3, time.Millisecond))
	_, err := c.SendPackage(context.Background(), "TEST3", "#M2MTR", []byte("x"))
	if err == nil {
		t.Fatal("ожидалась ошибка на 400")
	}
	if calls != 1 {
		t.Errorf("4xx не должен ретраиться, попыток = %d", calls)
	}
}

func TestGetStatus(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/api/package/status/pkg-1" {
			t.Errorf("неожиданный путь %q", r.URL.Path)
		}
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(`{"package_id":"pkg-1","state":"delivered","updated_at":"2026-03-02T14:30:00Z"}`))
	}))
	defer srv.Close()
	c := igw.NewClient(srv.URL, igw.WithRetry(0, time.Millisecond))
	st, err := c.GetStatus(context.Background(), "pkg-1")
	if err != nil {
		t.Fatal(err)
	}
	if st.State != "delivered" {
		t.Errorf("state = %q, ожидалось delivered", st.State)
	}
}

func TestListIncoming(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !contains(r.URL.RawQuery, "channel=TEST3") {
			t.Errorf("в query нет channel: %s", r.URL.RawQuery)
		}
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(`{"items":[{"package_id":"p1","package_type":"#M2MTD","channel":"TEST3","received_at":"2026-03-02T14:00:00Z","body":""}]}`))
	}))
	defer srv.Close()
	c := igw.NewClient(srv.URL, igw.WithRetry(0, time.Millisecond))
	pkgs, err := c.ListIncoming(context.Background(), "TEST3", time.Now().Add(-time.Hour), "#M2MTD")
	if err != nil {
		t.Fatal(err)
	}
	if len(pkgs) != 1 || pkgs[0].PackageType != "#M2MTD" {
		t.Errorf("неожиданный результат: %+v", pkgs)
	}
	body, err := pkgs[0].DecodeBody()
	if err != nil {
		t.Errorf("DecodeBody: %v", err)
	}
	if body != nil {
		t.Errorf("ожидалось пустое тело")
	}
}

func contains(s, substr string) bool {
	return len(s) >= len(substr) && (indexOf(s, substr) >= 0)
}

func indexOf(s, substr string) int {
	for i := 0; i+len(substr) <= len(s); i++ {
		if s[i:i+len(substr)] == substr {
			return i
		}
	}
	return -1
}
