package gateway

import (
	"errors"
	"io"
	"net/http"
	"strings"
	"testing"
	"time"
)

type roundTripFunc func(*http.Request) (*http.Response, error)

func (f roundTripFunc) RoundTrip(request *http.Request) (*http.Response, error) { return f(request) }

func TestResilientTransportRetriesReplayableRequest(t *testing.T) {
	attempts := 0
	transport := &resilientTransport{base: roundTripFunc(func(*http.Request) (*http.Response, error) {
		attempts++
		if attempts < 3 {
			return &http.Response{StatusCode: http.StatusServiceUnavailable, Body: io.NopCloser(strings.NewReader("busy")), Header: make(http.Header)}, nil
		}
		return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader("ok")), Header: make(http.Header)}, nil
	}), circuit: newCircuitBreaker(), maxRetries: 2}
	request, _ := http.NewRequest(http.MethodGet, "https://provider.example/v1/models", nil)
	response, err := transport.RoundTrip(request)
	if err != nil || response.StatusCode != http.StatusOK || attempts != 3 {
		t.Fatalf("response=%v err=%v attempts=%d", response, err, attempts)
	}
}

func TestResilientTransportDoesNotRetryUnsafePost(t *testing.T) {
	attempts := 0
	transport := &resilientTransport{base: roundTripFunc(func(*http.Request) (*http.Response, error) {
		attempts++
		return nil, errors.New("network failure")
	}), circuit: newCircuitBreaker(), maxRetries: 2}
	request, _ := http.NewRequest(http.MethodPost, "https://provider.example/v1/chat/completions", strings.NewReader("{}"))
	_, _ = transport.RoundTrip(request)
	if attempts != 1 {
		t.Fatalf("unsafe POST attempted %d times", attempts)
	}
}

func TestCircuitOpensAndAllowsSingleProbe(t *testing.T) {
	circuit := newCircuitBreaker()
	circuit.threshold = 2
	circuit.openFor = time.Millisecond
	now := time.Now()
	circuit.failure(now)
	circuit.failure(now)
	if circuit.allow(now) {
		t.Fatal("open circuit allowed request")
	}
	if !circuit.allow(now.Add(2 * time.Millisecond)) {
		t.Fatal("circuit did not allow half-open probe")
	}
	if circuit.allow(now.Add(2 * time.Millisecond)) {
		t.Fatal("circuit allowed concurrent half-open probe")
	}
}
