package api

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
)

// Request types for all core API operations

// MCPServer Request Types

// MCPServerCreateRequest represents a request to create a new MCP server definition.
// This request defines the structure and configuration for various types of MCP servers,
// allowing for flexible deployment and management of MCP capabilities.
//
// Supports stdio command MCP servers that execute local command/process and remote
// MCP servers that connect to external endpoints.
//
// Example for stdio command:
//
//	request := MCPServerCreateRequest{
//	    Name: "git-tools",
//	    Type: "stdio",
//	    Command: "npx",
//	    Args: []string{"@modelcontextprotocol/server-git"},
//	    AutoStart: true,
//	    Env: map[string]string{
//	        "GIT_ROOT": "/workspace",
//	    },
//	}
//
// Example for remote server:
//
//	request := MCPServerCreateRequest{
//	    Name: "remote-tools",
//	    Type: "streamable-http",
//	    URL: "https://api.example.com/mcp",
//	    Timeout: 30,
//	    Headers: map[string]string{
//	        "Authorization": "Bearer token",
//	    },
//	}

type MCPServerCreateRequest struct {
	// Name is the unique identifier for the MCP server (required).
	Name string `json:"name" validate:"required"`

	// Type specifies the MCP server type (required).
	// Valid values: "stdio", "streamable-http", "sse"
	Type string `json:"type" validate:"required"`

	// ToolPrefix is prepended to all tool names from this server to avoid conflicts.
	// Optional; if not specified, tools are exposed with their original names.
	ToolPrefix string `json:"toolPrefix,omitempty"`

	// Family declares that this MCP server is an instance of a family of
	// equivalent servers, sharing exposed tool names with siblings.
	Family *MCPServerFamily `json:"family,omitempty"`

	// Description for the MCP server
	Description string `json:"description,omitempty"`

	// AutoStart determines whether this MCP server should be automatically started
	AutoStart bool `json:"autoStart,omitempty"`

	// Command specifies the executable path for stdio type servers.
	// This field is required when Type is "stdio".
	Command string `json:"command,omitempty"`

	// Args specifies the command line arguments for stdio type servers.
	// This field is only available when Type is "stdio".
	Args []string `json:"args,omitempty"`

	// URL is the endpoint where the remote MCP server can be reached
	// This field is required when Type is "streamable-http" or "sse".
	URL string `json:"url,omitempty"`

	// Env contains environment variables to set for the MCP server.
	Env map[string]string `json:"env,omitempty"`

	// Headers contains HTTP headers to send with requests to remote MCP servers.
	// This field is only relevant when Type is "streamable-http" or "sse".
	Headers map[string]string `json:"headers,omitempty"`

	// Timeout specifies the connection timeout for remote operations (in seconds)
	Timeout int `json:"timeout,omitempty"`

	// Auth configures authentication behavior for this MCP server.
	// This is only relevant for remote servers (streamable-http or sse).
	Auth *MCPServerAuth `json:"auth,omitempty"`
}

// MCPServerUpdateRequest represents a request to update an existing MCP server definition.
// All fields except Name can be modified. Changes may require server restart.
type MCPServerUpdateRequest struct {
	// Name of the MCP server to update (required).
	Name string `json:"name" validate:"required"`

	// Type can be changed, but may require significant reconfiguration.
	Type string `json:"type" validate:"required"`

	// ToolPrefix can be updated, affecting tool naming.
	ToolPrefix string `json:"toolPrefix,omitempty"`

	// Family declares that this MCP server is an instance of a family of
	// equivalent servers, sharing exposed tool names with siblings.
	Family *MCPServerFamily `json:"family,omitempty"`

	// Description for the MCP server
	Description string `json:"description,omitempty"`

	// AutoStart determines whether this MCP server should be automatically started
	AutoStart bool `json:"autoStart,omitempty"`

	// Command specifies the executable path for stdio type servers.
	Command string `json:"command,omitempty"`

	// Args specifies the command line arguments for stdio type servers.
	Args []string `json:"args,omitempty"`

	// URL is the endpoint where the remote MCP server can be reached
	URL string `json:"url,omitempty"`

	// Env contains environment variables to set for the MCP server.
	Env map[string]string `json:"env,omitempty"`

	// Headers contains HTTP headers to send with requests to remote MCP servers.
	Headers map[string]string `json:"headers,omitempty"`

	// Timeout specifies the connection timeout for remote operations (in seconds)
	Timeout int `json:"timeout,omitempty"`

	// Auth configures authentication behavior for this MCP server.
	Auth *MCPServerAuth `json:"auth,omitempty"`
}

// MCPServerValidateRequest represents a request to validate an MCP server definition
// without creating it. Validates configuration consistency and tool availability.
type MCPServerValidateRequest struct {
	// Name for validation (required).
	Name string `json:"name" validate:"required"`

	// Type for validation (required).
	Type string `json:"type" validate:"required"`

	// ToolPrefix for validation.
	ToolPrefix string `json:"toolPrefix,omitempty"`

	// Family for validation.
	Family *MCPServerFamily `json:"family,omitempty"`

	// AutoStart determines whether this MCP server should be automatically started
	AutoStart bool `json:"autoStart,omitempty"`

	// Command specifies the executable path for stdio type servers.
	Command string `json:"command,omitempty"`

	// Args specifies the command line arguments for stdio type servers.
	Args []string `json:"args,omitempty"`

	// URL is the endpoint where the remote MCP server can be reached
	URL string `json:"url,omitempty"`

	// Env contains environment variables to set for the MCP server.
	Env map[string]string `json:"env,omitempty"`

	// Headers contains HTTP headers to send with requests to remote MCP servers.
	Headers map[string]string `json:"headers,omitempty"`

	// Timeout specifies the connection timeout for remote operations (in seconds)
	Timeout int `json:"timeout,omitempty"`

	// Description for validation and documentation.
	Description string `json:"description,omitempty"`

	// Auth configures authentication behavior for this MCP server.
	Auth *MCPServerAuth `json:"auth,omitempty"`
}

// Workflow Request Types

// WorkflowCreateRequest represents a request to create a new workflow definition.
// Workflows define multi-step processes that orchestrate tool calls with
// arg templating and conditional logic.
//
// Example:
//
//	request := WorkflowCreateRequest{
//	    Name: "deploy-service",
//	    Description: "Deploy a service to production",
//	    Args: map[string]ArgDefinition{
//	        "service_name": {
//	            Type:        "string",
//	            Required:    true,
//	            Description: "Name of the service to deploy",
//	        },
//	    },
//	    Steps: []WorkflowStep{
//	        {
//	            ID:   "build",
//	            Tool: "docker_build",
//	            Args: map[string]interface{}{
//	                "name": "{{service_name}}",
//	            },
//	        },
//	    },
//	}
type WorkflowCreateRequest struct {
	// Name is the unique identifier for the workflow (required).
	// Must be unique across all workflows in the system.
	Name string `json:"name" validate:"required"`

	// Version indicates the workflow version for compatibility tracking.
	// Recommended to use semantic versioning.
	Version string `json:"version,omitempty"`

	// Description provides human-readable documentation for the workflow.
	// Should explain the workflow's purpose and expected outcomes.
	Description string `json:"description,omitempty"`

	// Args defines the expected input arguments for workflow execution.
	// Used for arg validation and documentation generation.
	// If not specified, the workflow accepts any args.
	Args map[string]ArgDefinition `json:"args,omitempty"`

	// Steps defines the sequence of operations to perform (required).
	// Each step executes a tool with specified arguments and processing logic.
	// Must contain at least one step for a valid workflow.
	Steps []WorkflowStep `json:"steps" validate:"required"`
}

// WorkflowUpdateRequest represents a request to update an existing workflow definition.
// This allows modification of workflow steps, input args, and metadata.
type WorkflowUpdateRequest struct {
	// Name of the workflow to update (required).
	Name string `json:"name" validate:"required"`

	// Version can be updated to reflect changes.
	Version string `json:"version,omitempty"`

	// Description can be updated to improve documentation.
	Description string `json:"description,omitempty"`

	// Args can be modified to change arg requirements.
	// Changes may affect existing callers of this workflow.
	Args map[string]ArgDefinition `json:"args,omitempty"`

	// Steps can be added, modified, or reordered.
	// Changes affect workflow execution behavior.
	Steps []WorkflowStep `json:"steps,omitempty"`
}

// WorkflowValidateRequest represents a request to validate a workflow definition
// without creating it. Validates step configuration, tool availability, and arg schemas.
type WorkflowValidateRequest struct {
	// Name for validation (required).
	Name string `json:"name" validate:"required"`

	// Version for validation.
	Version string `json:"version,omitempty"`

	// Description for validation.
	Description string `json:"description,omitempty"`

	// Args for validation.
	Args map[string]ArgDefinition `json:"args,omitempty"`

	// Steps for validation (required). All referenced tools will be checked for availability.
	Steps []WorkflowStep `json:"steps" validate:"required"`
}

// ParseRequest converts a map[string]interface{} to a typed request struct.
// This uses JSON marshaling/unmarshaling for type conversion and validation,
// providing strict arg checking and type safety.
//
// The function validates that no unknown args are present and performs
// basic type validation according to the target struct's field types and tags.
//
// Args:
//   - args: The input arguments to parse and validate
//   - request: Pointer to the target request struct to populate
//
// Returns:
//   - error: Validation error if arguments are invalid or contain unknown fields
//
// Example:
//
//	var req WorkflowCreateRequest
//	args := map[string]interface{}{
//	    "name": "deploy",
//	    "steps": []interface{}{...},
//	}
//	err := ParseRequest(args, &req)
//	if err != nil {
//	    return fmt.Errorf("invalid request: %w", err)
//	}
func ParseRequest[T any](args map[string]interface{}, request *T) error {
	// First validate that no unknown args are present
	if err := validateStrictArgs(args, request); err != nil {
		return err
	}

	// Convert to JSON and back to get proper type conversion
	jsonData, err := json.Marshal(args)
	if err != nil {
		return fmt.Errorf("failed to marshal request arguments: %w", err)
	}

	// Use strict JSON decoder that fails on unknown fields
	decoder := json.NewDecoder(strings.NewReader(string(jsonData)))
	decoder.DisallowUnknownFields()

	if err := decoder.Decode(request); err != nil {
		return fmt.Errorf("failed to parse request: %w", err)
	}

	// Basic validation - check required fields
	if err := validateRequest(request); err != nil {
		return fmt.Errorf("validation failed: %w", err)
	}

	return nil
}

// validateRequest performs basic validation on request structs.
// This is a simple implementation that relies on JSON unmarshaling for type conversion.
// Future versions might integrate with a validation library like github.com/go-playground/validator.
//
// Currently, this function serves as a placeholder for more sophisticated validation
// that could include custom validation rules, cross-field validation, and business logic checks.
func validateRequest(request interface{}) error {
	// Use reflection to check for required fields
	// This is a simplified implementation - in production you might want to use
	// a validation library like github.com/go-playground/validator

	// For now, we rely on the JSON unmarshaling to handle type conversion
	// and the calling code to check for required fields
	return nil
}

// validateStrictArgs ensures no unknown args are present by comparing
// against the JSON tags of the target struct. This provides automatic validation
// based on the struct definition without requiring manual field lists.
//
// Args:
//   - args: The arguments to validate
//   - request: The target struct to validate against
//
// Returns:
//   - error: Error listing unknown args if any are found
func validateStrictArgs(args map[string]interface{}, request interface{}) error {
	// Get the struct type
	structType := reflect.TypeOf(request).Elem()

	// Build a map of allowed field names based on JSON tags
	allowedFields := make(map[string]bool)
	for i := 0; i < structType.NumField(); i++ {
		field := structType.Field(i)
		jsonTag := field.Tag.Get("json")

		if jsonTag != "" && jsonTag != "-" {
			// Parse the JSON tag to get the field name
			fieldName := strings.Split(jsonTag, ",")[0]
			if fieldName != "" {
				allowedFields[fieldName] = true
			}
		} else {
			// If no JSON tag, use the field name
			allowedFields[field.Name] = true
		}
	}

	// Check for unknown args
	var unknownParams []string
	for paramName := range args {
		if !allowedFields[paramName] {
			unknownParams = append(unknownParams, paramName)
		}
	}

	if len(unknownParams) > 0 {
		var allowedNames []string
		for name := range allowedFields {
			allowedNames = append(allowedNames, name)
		}
		return fmt.Errorf("unknown args: %v. Allowed args: %v", unknownParams, allowedNames)
	}

	return nil
}
