package platform

import (
	"context"
	"encoding/json"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"github.com/txn2/mcp-data-platform/pkg/registry"
	"github.com/txn2/mcp-data-platform/pkg/toolkit"
)

// connectionEntry describes a single toolkit connection.
//
// CatalogID and OperationCount are populated only for kinds where they
// have meaning (today: api). They make the runtime view of a
// connection visible to MCP clients so a stale in-memory binding
// (DB updated, toolkit not reloaded) is observable from list_connections
// rather than only via a downstream tool failing with "no catalog
// configured".
type connectionEntry struct {
	Kind              string `json:"kind"`
	Name              string `json:"name"`
	Connection        string `json:"connection"`
	Description       string `json:"description,omitempty"`
	IsDefault         bool   `json:"is_default,omitempty"`
	DataHubSourceName string `json:"datahub_source_name,omitempty"`
	CatalogID         string `json:"catalog_id,omitempty"`
	OperationCount    int    `json:"operation_count,omitempty"`
}

// listConnectionsOutput is the JSON response for the list_connections tool.
type listConnectionsOutput struct {
	Connections []connectionEntry `json:"connections"`
	Count       int               `json:"count"`
}

// listConnectionsInput is empty since this tool has no parameters.
type listConnectionsInput struct{}

// registerConnectionsTool registers the list_connections tool with the MCP server.
func (p *Platform) registerConnectionsTool() {
	mcp.AddTool(p.mcpServer, &mcp.Tool{
		Name:        toolListConns,
		Title:       "List Connections",
		Description: "List all configured data connections across toolkits (Trino, DataHub, S3, etc.).",
		Annotations: &mcp.ToolAnnotations{ReadOnlyHint: true},
	}, func(ctx context.Context, req *mcp.CallToolRequest, _ listConnectionsInput) (*mcp.CallToolResult, any, error) {
		return p.handleListConnections(ctx, req)
	})
}

// handleListConnections handles the list_connections tool call.
func (p *Platform) handleListConnections(_ context.Context, _ *mcp.CallToolRequest) (*mcp.CallToolResult, any, error) {
	toolkits := p.toolkitRegistry.All()

	entries := make([]connectionEntry, 0, len(toolkits))
	for _, tk := range toolkits {
		if lister, ok := tk.(toolkit.ConnectionLister); ok {
			entries = p.entriesFromLister(entries, tk, lister)
		} else {
			entries = p.entryFromFallback(entries, tk)
		}
	}

	out := listConnectionsOutput{
		Connections: entries,
		Count:       len(entries),
	}

	data, err := json.MarshalIndent(out, "", "  ")
	if err != nil {
		return &mcp.CallToolResult{ //nolint:nilerr // MCP protocol: tool errors are returned in CallToolResult.IsError, not as Go errors
			Content: []mcp.Content{
				&mcp.TextContent{Text: "Error: " + err.Error()},
			},
			IsError: true,
		}, nil, nil
	}

	return &mcp.CallToolResult{
		Content: []mcp.Content{
			&mcp.TextContent{Text: string(data)},
		},
	}, nil, nil
}

// entriesFromLister builds connection entries from a toolkit that implements ConnectionLister.
func (p *Platform) entriesFromLister(entries []connectionEntry, tk registry.Toolkit, lister toolkit.ConnectionLister) []connectionEntry {
	for _, conn := range lister.ListConnections() {
		entry := connectionEntry{
			Kind:           tk.Kind(),
			Name:           conn.Name,
			Connection:     conn.Name,
			Description:    conn.Description,
			IsDefault:      conn.IsDefault,
			CatalogID:      conn.CatalogID,
			OperationCount: conn.OperationCount,
		}
		if src := p.connectionSources.ForConnection(tk.Kind(), conn.Name); src != nil {
			entry.DataHubSourceName = src.DataHubSourceName
		}
		entries = append(entries, entry)
	}
	return entries
}

// entryFromFallback builds a single connection entry for toolkits that do not
// implement ConnectionLister. Non-data toolkits are skipped.
func (p *Platform) entryFromFallback(entries []connectionEntry, tk registry.Toolkit) []connectionEntry {
	kind := tk.Kind()
	if kind != kindTrino && kind != kindDataHub && kind != kindS3 {
		return entries
	}
	entry := connectionEntry{
		Kind:       kind,
		Name:       tk.Name(),
		Connection: tk.Connection(),
	}
	if src := p.connectionSources.ForConnection(kind, tk.Name()); src != nil {
		entry.DataHubSourceName = src.DataHubSourceName
	}
	return append(entries, entry)
}
