package tools

import (
	"context"
	"fmt"
	"strings"

	"github.com/vektah/gqlparser/v2/ast"
)

// CallToolResult represents the result of a tool call
type CallToolResult struct {
	Content []ContentBlock `json:"content"`
	IsError bool           `json:"isError,omitempty"`
}

// ContentBlock represents a content block in a tool result
type ContentBlock struct {
	Type string `json:"type"`
	Text string `json:"text"`
}

// Introspect returns GraphQL schema information
func Introspect(ctx context.Context, schema *ast.Schema, args map[string]interface{}) (CallToolResult, error) {
	typeName, _ := args["typeName"].(string)

	var result strings.Builder

	if typeName != "" {
		// Introspect specific type
		result.WriteString(introspectType(schema, typeName))
	} else {
		// Full schema overview
		result.WriteString("# ARP GraphQL Schema Overview\n\n")

		// Query type
		result.WriteString("## Query Type\n")
		result.WriteString(introspectType(schema, "Query"))
		result.WriteString("\n")

		// Mutation type
		result.WriteString("## Mutation Type\n")
		result.WriteString(introspectType(schema, "Mutation"))
		result.WriteString("\n")

		// Subscription type
		result.WriteString("## Subscription Type\n")
		result.WriteString(introspectType(schema, "Subscription"))
		result.WriteString("\n")

		// Object types
		result.WriteString("## Object Types\n")
		for _, t := range schema.Types {
			if t.Kind == ast.Object && !strings.HasPrefix(t.Name, "__") && t.Name != "Query" && t.Name != "Mutation" && t.Name != "Subscription" {
				result.WriteString(fmt.Sprintf("### %s\n", t.Name))
				result.WriteString(introspectType(schema, t.Name))
				result.WriteString("\n")
			}
		}

		// Input types
		result.WriteString("## Input Types\n")
		for _, t := range schema.Types {
			if t.Kind == ast.InputObject && !strings.HasPrefix(t.Name, "__") {
				result.WriteString(fmt.Sprintf("### %s\n", t.Name))
				result.WriteString(introspectInputType(t))
				result.WriteString("\n")
			}
		}
	}

	return CallToolResult{
		Content: []ContentBlock{
			{Type: "text", Text: result.String()},
		},
	}, nil
}

func introspectType(schema *ast.Schema, typeName string) string {
	t := schema.Types[typeName]
	if t == nil {
		return fmt.Sprintf("Type '%s' not found\n", typeName)
	}

	var result strings.Builder

	switch t.Kind {
	case ast.Object:
		result.WriteString(fmt.Sprintf("Type: %s (Object)\n", t.Name))
		if t.Description != "" {
			result.WriteString(fmt.Sprintf("Description: %s\n", t.Description))
		}
		result.WriteString("\nFields:\n")
		for _, field := range t.Fields {
			result.WriteString(formatField(field))
		}
	case ast.Interface:
		result.WriteString(fmt.Sprintf("Type: %s (Interface)\n", t.Name))
		if t.Description != "" {
			result.WriteString(fmt.Sprintf("Description: %s\n", t.Description))
		}
		result.WriteString("\nFields:\n")
		for _, field := range t.Fields {
			result.WriteString(formatField(field))
		}
	case ast.Union:
		result.WriteString(fmt.Sprintf("Type: %s (Union)\n", t.Name))
		result.WriteString("Members:\n")
		for _, member := range t.Types {
			result.WriteString(fmt.Sprintf("  - %s\n", member))
		}
	case ast.Enum:
		result.WriteString(fmt.Sprintf("Type: %s (Enum)\n", t.Name))
		result.WriteString("Values:\n")
		for _, val := range t.EnumValues {
			result.WriteString(fmt.Sprintf("  - %s\n", val.Name))
		}
	default:
		result.WriteString(fmt.Sprintf("Type: %s (%s)\n", t.Name, t.Kind))
	}

	return result.String()
}

func introspectInputType(t *ast.Definition) string {
	var result strings.Builder

	result.WriteString(fmt.Sprintf("Type: %s (Input)\n", t.Name))
	if t.Description != "" {
		result.WriteString(fmt.Sprintf("Description: %s\n", t.Description))
	}
	result.WriteString("\nFields:\n")
	for _, field := range t.Fields {
		result.WriteString(formatInputField(field))
	}

	return result.String()
}

func formatField(field *ast.FieldDefinition) string {
	var result strings.Builder

	// Field name and type
	result.WriteString(fmt.Sprintf("  %s", field.Name))
	if len(field.Arguments) > 0 {
		result.WriteString("(")
		for i, arg := range field.Arguments {
			if i > 0 {
				result.WriteString(", ")
			}
			result.WriteString(fmt.Sprintf("%s: %s", arg.Name, formatType(arg.Type)))
		}
		result.WriteString(")")
	}
	result.WriteString(fmt.Sprintf(": %s", formatType(field.Type)))

	// Description
	if field.Description != "" {
		result.WriteString(fmt.Sprintf(" - %s", field.Description))
	}

	// Deprecation
	if field.Directives != nil {
		for _, d := range field.Directives {
			if d.Name == "deprecated" {
				result.WriteString(" [DEPRECATED]")
				if reason := d.Arguments.ForName("reason"); reason != nil {
					result.WriteString(fmt.Sprintf(" - %s", reason.Value.Raw))
				}
			}
		}
	}

	result.WriteString("\n")
	return result.String()
}

func formatInputField(field *ast.FieldDefinition) string {
	var result strings.Builder

	result.WriteString(fmt.Sprintf("  %s: %s", field.Name, formatType(field.Type)))

	if field.Description != "" {
		result.WriteString(fmt.Sprintf(" - %s", field.Description))
	}

	result.WriteString("\n")
	return result.String()
}

func formatType(t *ast.Type) string {
	if t == nil {
		return "Unknown"
	}

	base := t.Name()
	if t.NamedType != "" {
		base = t.NamedType
	}

	if t.Elem != nil {
		return fmt.Sprintf("[%s]", formatType(t.Elem))
	}

	if t.NonNull {
		return base + "!"
	}

	return base
}
