package mcp_service

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

	"evening_detective_server/internal/modules/storytelling"
	"evening_detective_server/internal/services/game_service"

	"github.com/mark3labs/mcp-go/client"
	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

// Интеграционный тест HTTP-«клея»: MCP-сервер монтируется как
// streamable HTTP (эндпоинт /api/mcp) и вызывается через HTTP-клиент mcp-go.
// Проверяются initialize, tools/list (точное множество) и tools/call
// connect — как это будет делать внешний MCP-клиент.
func TestStreamableHTTPEndToEnd(t *testing.T) {
	svc := NewMCPService(&fakeGamePlayer{story: defaultStory(), game: defaultGame()})

	mcpHTTP := server.NewStreamableHTTPServer(svc.Server())
	srv := httptest.NewServer(mcpHTTP)
	t.Cleanup(srv.Close)

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	t.Cleanup(cancel)

	// ВАЖНО: клиент POST-ит строго на переданный baseURL без добавления
	// пути — эндпоинт /api/mcp передаём явно.
	mcpClient, err := client.NewStreamableHttpClient(srv.URL + "/api/mcp")
	if err != nil {
		t.Fatalf("NewStreamableHttpClient() error = %v", err)
	}
	t.Cleanup(func() { _ = mcpClient.Close() })

	initRequest := mcp.InitializeRequest{}
	initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
	initRequest.Params.ClientInfo = mcp.Implementation{Name: "http_it", Version: "1.0.0"}
	if _, err := mcpClient.Initialize(ctx, initRequest); err != nil {
		t.Fatalf("Initialize() error = %v", err)
	}

	toolsResult, err := mcpClient.ListTools(ctx, mcp.ListToolsRequest{})
	if err != nil {
		t.Fatalf("ListTools() error = %v", err)
	}
	names := make([]string, 0, len(toolsResult.Tools))
	for _, tool := range toolsResult.Tools {
		names = append(names, tool.Name)
	}
	assertExactTools(t, names)

	callRequest := mcp.CallToolRequest{}
	callRequest.Params.Name = "connect"
	callRequest.Params.Arguments = map[string]any{
		"url": "/team-story/10?password=team-pass-1",
	}
	result, err := mcpClient.CallTool(ctx, callRequest)
	if err != nil {
		t.Fatalf("CallTool(connect) error = %v", err)
	}
	var text strings.Builder
	for _, content := range result.Content {
		if tc, ok := content.(mcp.TextContent); ok {
			text.WriteString(tc.Text)
		}
	}
	if !strings.Contains(text.String(), "Подключено к команде 10") {
		t.Fatalf("CallTool(connect) = %q; want success message", text.String())
	}
	if !strings.Contains(text.String(), "entrance") {
		t.Fatalf("CallTool(connect) = %q; want story JSON", text.String())
	}
}

// Проверка, что структуры моков сериализуемы (защита от изменения типов
// storytelling/game_service без обновления моков).
func TestMockTypesUsable(t *testing.T) {
	_ = defaultStory()
	_ = defaultGame()
	_ = (*game_service.Game)(nil)
	_ = (*storytelling.Story)(nil)
}
