package aireview

import (
	"encoding/json"
	"net/http"
	"testing"
)

func TestBuildStructuredRequestBodyResponsesStyle(t *testing.T) {
	t.Parallel()

	svc := NewService("https://example.test/v1/responses", "test-key", "test-model")
	body, err := svc.buildStructuredRequestBody("system prompt", "user prompt", "test_schema", reviewSchema())
	if err != nil {
		t.Fatalf("buildStructuredRequestBody returned error: %v", err)
	}

	if got := body["model"]; got != "test-model" {
		t.Fatalf("model = %v, want test-model", got)
	}

	if _, ok := body["input"]; !ok {
		t.Fatalf("responses body missing input field: %#v", body)
	}

	if _, ok := body["text"]; !ok {
		t.Fatalf("responses body missing text field: %#v", body)
	}

	if _, ok := body["messages"]; ok {
		t.Fatalf("responses body should not include messages: %#v", body)
	}
	if _, ok := body["response_format"]; ok {
		t.Fatalf("responses body should not include response_format: %#v", body)
	}
}

func TestBuildStructuredRequestBodyVLLMChatStyle(t *testing.T) {
	t.Parallel()

	svc := NewService("http://100.92.130.19:8000/v1/chat/completions", "test-key", "qwen3.6-27b")
	body, err := svc.buildStructuredRequestBody("system prompt", "user prompt", "assignment_review", reviewSchema())
	if err != nil {
		t.Fatalf("buildStructuredRequestBody returned error: %v", err)
	}

	if got := body["model"]; got != "qwen3.6-27b" {
		t.Fatalf("model = %v, want qwen3.6-27b", got)
	}

	if _, ok := body["messages"]; !ok {
		t.Fatalf("chat body missing messages: %#v", body)
	}

	responseFormat, ok := body["response_format"].(map[string]any)
	if !ok {
		t.Fatalf("response_format missing or wrong type: %#v", body["response_format"])
	}
	if got := responseFormat["type"]; got != "json_schema" {
		t.Fatalf("response_format.type = %v, want json_schema", got)
	}

	kwargs, ok := body["chat_template_kwargs"].(map[string]any)
	if !ok {
		t.Fatalf("expected chat_template_kwargs for vLLM chat style: %#v", body)
	}
	if got := kwargs["enable_thinking"]; got != false {
		t.Fatalf("enable_thinking = %v, want false", got)
	}

	if _, ok := body["input"]; ok {
		t.Fatalf("chat body should not include responses input field: %#v", body)
	}
}

func TestBuildStructuredRequestBodyAzureChatStyleDoesNotDisableThinking(t *testing.T) {
	t.Parallel()

	svc := NewService("https://example.openai.azure.com/openai/deployments/review/chat/completions?api-version=2025-01-01-preview", "test-key", "gpt-4.1-mini")
	body, err := svc.buildStructuredRequestBody("system prompt", "user prompt", "assignment_review", reviewSchema())
	if err != nil {
		t.Fatalf("buildStructuredRequestBody returned error: %v", err)
	}

	if _, ok := body["chat_template_kwargs"]; ok {
		t.Fatalf("azure chat body should not include chat_template_kwargs: %#v", body)
	}
}

func TestApplyAuthHeader(t *testing.T) {
	t.Parallel()

	t.Run("azure uses api-key", func(t *testing.T) {
		t.Parallel()

		svc := NewService("https://example.openai.azure.com/openai/deployments/review/chat/completions?api-version=2025-01-01-preview", "azure-key", "gpt-4.1-mini")
		req, err := http.NewRequest(http.MethodPost, svc.endpoint, nil)
		if err != nil {
			t.Fatalf("http.NewRequest returned error: %v", err)
		}

		svc.applyAuthHeader(req)

		if got := req.Header.Get("api-key"); got != "azure-key" {
			t.Fatalf("api-key header = %q, want azure-key", got)
		}
		if got := req.Header.Get("Authorization"); got != "" {
			t.Fatalf("Authorization header = %q, want empty", got)
		}
	})

	t.Run("non-azure uses bearer auth", func(t *testing.T) {
		t.Parallel()

		svc := NewService("http://100.92.130.19:8000/v1/chat/completions", "vllm-key", "qwen3.6-27b")
		req, err := http.NewRequest(http.MethodPost, svc.endpoint, nil)
		if err != nil {
			t.Fatalf("http.NewRequest returned error: %v", err)
		}

		svc.applyAuthHeader(req)

		if got := req.Header.Get("Authorization"); got != "Bearer vllm-key" {
			t.Fatalf("Authorization header = %q, want %q", got, "Bearer vllm-key")
		}
		if got := req.Header.Get("api-key"); got != "" {
			t.Fatalf("api-key header = %q, want empty", got)
		}
	})
}

func TestExtractChatCompletionText(t *testing.T) {
	t.Parallel()

	t.Run("string content", func(t *testing.T) {
		t.Parallel()

		respBytes := []byte(`{"choices":[{"message":{"content":"{\"status\":\"ok\"}"}}]}`)
		got, err := extractChatCompletionText(respBytes)
		if err != nil {
			t.Fatalf("extractChatCompletionText returned error: %v", err)
		}
		if got != `{"status":"ok"}` {
			t.Fatalf("content = %q, want %q", got, `{"status":"ok"}`)
		}
	})

	t.Run("array content", func(t *testing.T) {
		t.Parallel()

		payload := map[string]any{
			"choices": []any{
				map[string]any{
					"message": map[string]any{
						"content": []any{
							map[string]any{"type": "text", "text": "{\"status\":"},
							map[string]any{"type": "text", "text": "\"ok\"}"},
						},
					},
				},
			},
		}

		respBytes, err := json.Marshal(payload)
		if err != nil {
			t.Fatalf("json.Marshal returned error: %v", err)
		}

		got, err := extractChatCompletionText(respBytes)
		if err != nil {
			t.Fatalf("extractChatCompletionText returned error: %v", err)
		}
		if got != "{\"status\":\n\"ok\"}" {
			t.Fatalf("content = %q, want joined text output", got)
		}
	})
}
