package mcp

import (
	"encoding/json"
	"strings"
	"testing"

	"github.com/google/uuid"
	"github.com/modelcontextprotocol/go-sdk/mcp"
)

func TestAnnotateJSONResult(t *testing.T) {
	// valid JSON array → wrapped with __renderer + data
	result := textResult(`[{"slug": "host:hubris", "type": "host"}]`)
	annotated := annotateJSONResult(result, "entity_card")

	if len(annotated.Content) != 1 {
		t.Fatalf("expected 1 content item, got %d", len(annotated.Content))
	}
	tc, ok := annotated.Content[0].(*mcp.TextContent)
	if !ok {
		t.Fatal("content is not TextContent")
	}

	var wrapper map[string]interface{}
	if err := json.Unmarshal([]byte(tc.Text), &wrapper); err != nil {
		t.Fatalf("result is not valid JSON: %v", err)
	}
	if wrapper["__renderer"] != "entity_card" {
		t.Errorf("__renderer = %q, want entity_card", wrapper["__renderer"])
	}
	data, ok := wrapper["data"].([]interface{})
	if !ok || len(data) != 1 {
		t.Fatal("data is not the original array")
	}
}

func TestAnnotateJSONResultNoop(t *testing.T) {
	// empty content → no-op
	result := &mcp.CallToolResult{Content: []mcp.Content{}}
	annotated := annotateJSONResult(result, "entity_card")
	if len(annotated.Content) != 0 {
		t.Fatal("empty content should be unchanged")
	}

	// non-JSON text → no-op (not wrapped)
	result = textResult("just plain text")
	annotated = annotateJSONResult(result, "entity_card")
	tc, _ := annotated.Content[0].(*mcp.TextContent)
	if strings.Contains(tc.Text, "__renderer") {
		t.Fatal("non-JSON content should not be annotated")
	}

	// textResult with empty string → no-op
	result = textResult("")
	annotated = annotateJSONResult(result, "entity_card")
	tc, _ = annotated.Content[0].(*mcp.TextContent)
	if tc.Text != "" {
		t.Fatal("empty text content should be unchanged")
	}
}

func TestAnnotateJSONResultPreservesMultipleRows(t *testing.T) {
	result := textResult(`[{"slug": "a"}, {"slug": "b"}, {"slug": "c"}]`)
	annotated := annotateJSONResult(result, "lxc_list")

	tc, _ := annotated.Content[0].(*mcp.TextContent)
	var wrapper map[string]interface{}
	json.Unmarshal([]byte(tc.Text), &wrapper)

	data := wrapper["data"].([]interface{})
	if len(data) != 3 {
		t.Fatalf("expected 3 rows in data, got %d", len(data))
	}
}

// TestNewServerRegistersTools verifies every tool registers with a valid
// input schema. The MCP SDK panics at AddTool if a tool omits its object
// input schema, so merely constructing the server exercises that contract —
// this test would have caught the "missing input schema" panic.
func TestNewServerRegistersTools(t *testing.T) {
	defer func() {
		if r := recover(); r != nil {
			t.Fatalf("newServer panicked (tool schema bug?): %v", r)
		}
	}()
	// pool is only used inside tool handlers (invoked per-call), not at
	// registration time, so a nil pool is safe for this construction test.
	s := newServer(nil, uuid.Nil, nil, nil, nil)
	if s == nil {
		t.Fatal("newServer returned nil")
	}
}

func TestObjSchema(t *testing.T) {
	s := objSchema(prop{"foo", "string", "a foo"}, prop{"n", "integer", "a number"})
	if s.Type != "object" {
		t.Errorf("schema type = %q, want object", s.Type)
	}
	if len(s.Properties) != 2 {
		t.Fatalf("got %d properties, want 2", len(s.Properties))
	}
	if s.Properties["foo"].Type != "string" || s.Properties["n"].Type != "integer" {
		t.Errorf("property types wrong: %+v", s.Properties)
	}
	// empty schema still valid (object with no properties)
	if objSchema().Type != "object" {
		t.Error("empty objSchema not an object")
	}
}
