package azure_test

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/veylant/ia-gateway/internal/provider"
	"github.com/veylant/ia-gateway/internal/provider/azure"
)

// newTestAdapter builds an Adapter using the given mock server URL.
// We override the URL by setting ResourceName to the server hostname so the
// adapter constructs the right endpoint in tests.
func newTestAdapter(t *testing.T, serverURL string) *azure.Adapter {
	t.Helper()
	// Strip scheme from serverURL to get host (e.g. "127.0.0.1:PORT")
	host := strings.TrimPrefix(serverURL, "http://")
	// We can't directly set a custom base URL in Azure adapter, so we use a
	// custom config and inject via the exported ChatURL helper via tests.
	// For tests we create a mock that validates expected request patterns.
	_ = host
	return azure.NewWithBaseURL(serverURL, azure.Config{
		APIKey:       "test-key",
		ResourceName: "test-resource",
		DeploymentID: "gpt-4o",
		APIVersion:   "2024-02-01",
		TimeoutSeconds: 5,
		MaxConns:       10,
	})
}

func minimalRequest() *provider.ChatRequest {
	return &provider.ChatRequest{
		Model:    "gpt-4o",
		Messages: []provider.Message{{Role: "user", Content: "hello"}},
	}
}

// ─── Send ────────────────────────────────────────────────────────────────────

func TestAzureAdapter_Send_Nominal(t *testing.T) {
	expected := provider.ChatResponse{
		ID:     "chatcmpl-azure-test",
		Object: "chat.completion",
		Model:  "gpt-4o",
		Choices: []provider.Choice{{
			Message:      provider.Message{Role: "assistant", Content: "Hello from Azure!"},
			FinishReason: "stop",
		}},
	}

	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Verify Azure-specific header (api-key, NOT Authorization Bearer)
		assert.Equal(t, "test-key", r.Header.Get("api-key"))
		assert.Empty(t, r.Header.Get("Authorization"))
		// Verify URL contains api-version
		assert.Contains(t, r.URL.RawQuery, "api-version=2024-02-01")

		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(expected)
	}))
	defer srv.Close()

	adapter := newTestAdapter(t, srv.URL)
	got, err := adapter.Send(context.Background(), minimalRequest())

	require.NoError(t, err)
	assert.Equal(t, "chatcmpl-azure-test", got.ID)
	assert.Equal(t, "Hello from Azure!", got.Choices[0].Message.Content)
}

func TestAzureAdapter_Send_Upstream401_ReturnsAuthError(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusUnauthorized)
	}))
	defer srv.Close()

	_, err := newTestAdapter(t, srv.URL).Send(context.Background(), minimalRequest())
	require.Error(t, err)
	assert.Contains(t, err.Error(), "API key")
}

func TestAzureAdapter_Send_Upstream429_ReturnsRateLimit(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusTooManyRequests)
	}))
	defer srv.Close()

	_, err := newTestAdapter(t, srv.URL).Send(context.Background(), minimalRequest())
	require.Error(t, err)
	assert.Contains(t, err.Error(), "rate limit")
}

func TestAzureAdapter_Send_Upstream500_ReturnsUpstreamError(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
	}))
	defer srv.Close()

	_, err := newTestAdapter(t, srv.URL).Send(context.Background(), minimalRequest())
	require.Error(t, err)
	assert.Contains(t, err.Error(), "500")
}

// ─── Stream ──────────────────────────────────────────────────────────────────

func TestAzureAdapter_Stream_Nominal(t *testing.T) {
	ssePayload := "data: {\"id\":\"chatcmpl-1\",\"choices\":[{\"delta\":{\"content\":\"Hel\"}}]}\n" +
		"data: {\"id\":\"chatcmpl-1\",\"choices\":[{\"delta\":{\"content\":\"lo\"}}]}\n" +
		"data: [DONE]\n"

	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(ssePayload))
	}))
	defer srv.Close()

	rec := httptest.NewRecorder()
	rec.Header().Set("Content-Type", "text/event-stream")

	req := minimalRequest()
	req.Stream = true
	err := newTestAdapter(t, srv.URL).Stream(context.Background(), req, rec)

	require.NoError(t, err)
	body := rec.Body.String()
	assert.Contains(t, body, "Hel")
	assert.Contains(t, body, "[DONE]")
}

// ─── Validate ────────────────────────────────────────────────────────────────

func TestAzureAdapter_Validate_MissingModel(t *testing.T) {
	a := azure.NewWithBaseURL("http://unused", azure.Config{APIKey: "k"})
	err := a.Validate(&provider.ChatRequest{
		Messages: []provider.Message{{Role: "user", Content: "hi"}},
	})
	require.Error(t, err)
	assert.Contains(t, err.Error(), "model")
}

func TestAzureAdapter_Validate_EmptyMessages(t *testing.T) {
	a := azure.NewWithBaseURL("http://unused", azure.Config{APIKey: "k"})
	err := a.Validate(&provider.ChatRequest{Model: "gpt-4o"})
	require.Error(t, err)
	assert.Contains(t, err.Error(), "messages")
}

// ─── HealthCheck ─────────────────────────────────────────────────────────────

func TestAzureAdapter_HealthCheck_Nominal(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		assert.Equal(t, "test-key", r.Header.Get("api-key"))
		assert.Contains(t, r.URL.RawQuery, "api-version=2024-02-01")
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(`{"data":[]}`))
	}))
	defer srv.Close()

	err := newTestAdapter(t, srv.URL).HealthCheck(context.Background())
	require.NoError(t, err)
}

func TestAzureAdapter_HealthCheck_Failure(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusServiceUnavailable)
	}))
	defer srv.Close()

	err := newTestAdapter(t, srv.URL).HealthCheck(context.Background())
	require.Error(t, err)
	assert.Contains(t, err.Error(), "503")
}
