package llm

import (
	"context"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

// TestNewClaudeProvider_Timeout pins the request budget on the Anthropic
// client. The SDK otherwise falls back to http.DefaultClient, which has no
// timeout, so a blackholed connection blocks the report stage forever and the
// retry loop never sees an error to retry (issue #67). Every other
// HTTP-based provider in this package already carries a non-zero timeout.
func TestNewClaudeProvider_Timeout(t *testing.T) {
	tests := []struct {
		name string
		cfg  ClaudeProviderConfig
		want time.Duration
	}{
		{
			name: "unset falls back to the package default",
			cfg:  ClaudeProviderConfig{APIKey: "test-key"},
			want: defaultClaudeTimeout,
		},
		{
			name: "explicit timeout is honoured",
			cfg:  ClaudeProviderConfig{APIKey: "test-key", Timeout: 90 * time.Second},
			want: 90 * time.Second,
		},
		{
			name: "non-positive timeout falls back to the default",
			cfg:  ClaudeProviderConfig{APIKey: "test-key", Timeout: -1},
			want: defaultClaudeTimeout,
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			p := NewClaudeProvider(tc.cfg)

			if p.httpClient == nil {
				t.Fatal("httpClient = nil, want a client with a timeout")
			}
			if p.httpClient.Timeout != tc.want {
				t.Errorf("httpClient.Timeout = %v, want %v", p.httpClient.Timeout, tc.want)
			}
		})
	}
}

// TestClaudeProvider_Complete_TimesOutOnStalledConnection reproduces the
// report-stage hang from issue #67: the server accepts the request and then
// goes silent mid-response. Complete must return an error instead of parking
// on the socket forever.
func TestClaudeProvider_Complete_TimesOutOnStalledConnection(t *testing.T) {
	release := make(chan struct{})

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		if f, ok := w.(http.Flusher); ok {
			f.Flush()
		}
		// Blackhole: hold the connection open without finishing the body.
		<-release
	}))
	// Deferred last-in-first-out, so release is closed before server.Close()
	// waits on the handler that is parked above.
	defer server.Close()
	defer close(release)

	t.Setenv("ANTHROPIC_BASE_URL", server.URL)

	p := NewClaudeProvider(ClaudeProviderConfig{
		APIKey:  "test-key",
		Model:   "claude-sonnet-4-6",
		Timeout: 250 * time.Millisecond,
	})

	done := make(chan error, 1)
	go func() {
		_, err := p.Complete(context.Background(), CompletionRequest{
			Messages: []Message{{Role: "user", Content: "Hello"}},
		})
		done <- err
	}()

	select {
	case err := <-done:
		if err == nil {
			t.Fatal("Complete returned no error, want a timeout error")
		}
	case <-time.After(10 * time.Second):
		t.Fatal("Complete blocked on a stalled connection; the request timeout is not being applied")
	}
}
