package workflow

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

// APIDefinition represents a third-party API endpoint configuration
type APIDefinition struct {
	ID      string            `json:"id"`      // Unique API identifier
	Method  string            `json:"method"`  // HTTP method (GET, POST, PUT, PATCH, DELETE)
	URL     string            `json:"url"`     // API endpoint URL (may contain {pathParam} placeholders)
	Auth    string            `json:"auth"`    // Authentication credential reference (e.g., "SYSVAR.apiKey")
	Headers map[string]string `json:"headers"` // Static request headers
	Desc    string            `json:"desc"`    // Human-readable description
}

// Registry defines external resources and global boundaries for workflow execution
type Registry struct {
	Services   []string                       `json:"services"`   // Service signatures (VL format)
	APIs       []APIDefinition                `json:"apis"`       // Third-party API endpoint definitions
	Components []string                       `json:"components"` // Component IDs
	Params     []string                       `json:"params"`     // Input parameters (VL format, read-only during execution)
	Vars       []string                       `json:"vars"`       // Global variables (VL format)
	Files      FilesRegistry                  `json:"files"`      // File access boundaries
	Docs       map[string]string              `json:"docs"`       // Semantic document references (docId -> description)
	Schemas    map[string]map[string]interface{} `json:"schemas"`    // JSON Schema definitions for reuse (v3.9+, schemaId -> schema object)
}

// FilesRegistry defines file input/output boundaries
type FilesRegistry struct {
	Inputs    []string `json:"inputs"`    // Read-only input files (path patterns)
	Artifacts []string `json:"artifacts"` // Temporary writable files (path patterns)
}

// ServiceSignature represents a parsed service signature
type ServiceSignature struct {
	Name       string         // Service name
	Parameters []ParameterDef // Input parameters
	Returns    []ParameterDef // Output fields
}

// ParameterDef represents a parameter definition
type ParameterDef struct {
	Name string // Parameter name
	Type string // Parameter type (STRING, INT, BOOL, OBJECT, ARRAY, FILE_REF, etc.)
}

// VariableDeclaration represents a parsed variable declaration
type VariableDeclaration struct {
	Name string // Variable name (with $ prefix)
	Type string // Variable type
}

// ParamDeclaration represents a parsed parameter declaration
type ParamDeclaration struct {
	Name    string  // Parameter name (without $ prefix)
	Type    string  // Parameter type
	Default *string // Optional default value literal (spec §3.2, e.g. "3" for INT=3)
}

// ParseServiceSignature parses a VL-format service signature
// Format: "ServiceName(param1(TYPE1), param2(TYPE2)) RETURN result1(TYPE1), result2(TYPE2)"
func ParseServiceSignature(signature string) (*ServiceSignature, error) {
	// Example: "PlannerService(prd(STRING), rulesFile(FILE_REF)) RETURN plan(OBJECT)"

	// Match service name and parameters
	re := regexp.MustCompile(`^(\w+)\((.*?)\)\s+RETURN\s+(.*)$`)
	matches := re.FindStringSubmatch(strings.TrimSpace(signature))

	if len(matches) != 4 {
		return nil, fmt.Errorf("invalid service signature format: %s", signature)
	}

	serviceName := matches[1]
	paramsStr := matches[2]
	returnsStr := matches[3]

	sig := &ServiceSignature{
		Name: serviceName,
	}

	// Parse parameters
	if paramsStr != "" {
		params, err := parseParameters(paramsStr)
		if err != nil {
			return nil, fmt.Errorf("failed to parse parameters: %w", err)
		}
		sig.Parameters = params
	}

	// Parse return values
	if returnsStr != "" {
		returns, err := parseParameters(returnsStr)
		if err != nil {
			return nil, fmt.Errorf("failed to parse return values: %w", err)
		}
		sig.Returns = returns
	}

	return sig, nil
}

// parseParameters parses parameter list in format: "param1(TYPE1), param2(TYPE2)"
func parseParameters(paramsStr string) ([]ParameterDef, error) {
	var params []ParameterDef

	// Split by comma, but need to handle nested parentheses
	parts := strings.Split(paramsStr, ",")

	for _, part := range parts {
		part = strings.TrimSpace(part)
		if part == "" {
			continue
		}

		// Match paramName(TYPE)
		re := regexp.MustCompile(`^(\w+)\(([^)]+)\)$`)
		matches := re.FindStringSubmatch(part)

		if len(matches) != 3 {
			return nil, fmt.Errorf("invalid parameter format: %s", part)
		}

		params = append(params, ParameterDef{
			Name: matches[1],
			Type: matches[2],
		})
	}

	return params, nil
}

// ParseVariableDeclaration parses a VL-format variable declaration
// Format: "$varName(TYPE)" or "$varName([TYPE])" for arrays
func ParseVariableDeclaration(declaration string) (*VariableDeclaration, error) {
	// Example: "$keyword(STRING)", "$items([OBJECT])", "$result(OBJECT)"

	re := regexp.MustCompile(`^(\$\w+)\((.+)\)$`)
	matches := re.FindStringSubmatch(strings.TrimSpace(declaration))

	if len(matches) != 3 {
		return nil, fmt.Errorf("invalid variable declaration format: %s", declaration)
	}

	return &VariableDeclaration{
		Name: matches[1],
		Type: matches[2],
	}, nil
}

// ParseParamDeclaration parses a VL-format parameter declaration.
// Supported formats:
//   - "paramName(TYPE)"            — no default
//   - "paramName([TYPE])"          — array type, no default
//   - "paramName(TYPE) = value"    — with default value (spec §3.2)
//
// Examples: "userId(STRING)", "filters([OBJECT])", "maxRetries(INT) = 3"
func ParseParamDeclaration(declaration string) (*ParamDeclaration, error) {
	// Optional trailing "= <defaultValue>" after the closing paren.
	re := regexp.MustCompile(`^([a-zA-Z]\w*)\((.+)\)(?:\s*=\s*(.+))?$`)
	matches := re.FindStringSubmatch(strings.TrimSpace(declaration))

	if len(matches) < 3 {
		return nil, fmt.Errorf("invalid parameter declaration format: %s", declaration)
	}

	decl := &ParamDeclaration{
		Name: matches[1],
		Type: matches[2],
	}

	// matches[3] is the optional default value (empty string if not present)
	if len(matches) == 4 && matches[3] != "" {
		defaultVal := strings.TrimSpace(matches[3])
		// Strip surrounding quotes from string literals, e.g. "\"hello\"" → "hello"
		if len(defaultVal) >= 2 && defaultVal[0] == '"' && defaultVal[len(defaultVal)-1] == '"' {
			defaultVal = defaultVal[1 : len(defaultVal)-1]
		}
		decl.Default = &defaultVal
	}

	return decl, nil
}

// ValidateRegistry validates the registry structure and constraints
func (r *Registry) ValidateRegistry() error {
	// Check for duplicate service names
	serviceNames := make(map[string]bool)
	for _, sig := range r.Services {
		parsed, err := ParseServiceSignature(sig)
		if err != nil {
			return fmt.Errorf("invalid service signature: %w", err)
		}
		if serviceNames[parsed.Name] {
			return fmt.Errorf("duplicate service name: %s", parsed.Name)
		}
		serviceNames[parsed.Name] = true
	}

	// Check for duplicate API IDs
	apiIDs := make(map[string]bool)
	for _, api := range r.APIs {
		if apiIDs[api.ID] {
			return fmt.Errorf("duplicate API ID: %s", api.ID)
		}
		apiIDs[api.ID] = true

		// Validate required fields
		if api.Method == "" {
			return fmt.Errorf("API %s: method is required", api.ID)
		}
		if api.URL == "" {
			return fmt.Errorf("API %s: URL is required", api.ID)
		}

		// Validate HTTP method
		validMethods := map[string]bool{
			"GET": true, "POST": true, "PUT": true,
			"PATCH": true, "DELETE": true,
		}
		if !validMethods[strings.ToUpper(api.Method)] {
			return fmt.Errorf("API %s: invalid HTTP method: %s", api.ID, api.Method)
		}
	}

	// Ensure APIs is initialized
	if r.APIs == nil {
		r.APIs = []APIDefinition{}
	}

	// Check for duplicate component IDs
	componentIDs := make(map[string]bool)
	for _, comp := range r.Components {
		if componentIDs[comp] {
			return fmt.Errorf("duplicate component ID: %s", comp)
		}
		componentIDs[comp] = true
	}

	// Check for duplicate parameter names
	paramNames := make(map[string]bool)
	for _, paramDecl := range r.Params {
		parsed, err := ParseParamDeclaration(paramDecl)
		if err != nil {
			return fmt.Errorf("invalid parameter declaration: %w", err)
		}
		if paramNames[parsed.Name] {
			return fmt.Errorf("duplicate parameter name: %s", parsed.Name)
		}
		paramNames[parsed.Name] = true
	}

	// Check for duplicate variable names
	varNames := make(map[string]bool)
	for _, varDecl := range r.Vars {
		parsed, err := ParseVariableDeclaration(varDecl)
		if err != nil {
			return fmt.Errorf("invalid variable declaration: %w", err)
		}
		if varNames[parsed.Name] {
			return fmt.Errorf("duplicate variable name: %s", parsed.Name)
		}
		varNames[parsed.Name] = true
	}

	// Ensure files section has both inputs and artifacts
	if r.Files.Inputs == nil {
		r.Files.Inputs = []string{}
	}
	if r.Files.Artifacts == nil {
		r.Files.Artifacts = []string{}
	}

	// Ensure docs is initialized
	if r.Docs == nil {
		r.Docs = make(map[string]string)
	}

	// Ensure schemas is initialized
	if r.Schemas == nil {
		r.Schemas = make(map[string]map[string]interface{})
	}

	// Validate schema IDs are not empty
	for schemaID := range r.Schemas {
		if schemaID == "" {
			return fmt.Errorf("schema ID cannot be empty")
		}
	}

	return nil
}

// HasDoc checks if a document ID is registered
func (r *Registry) HasDoc(docID string) bool {
	if r.Docs == nil {
		return false
	}
	_, ok := r.Docs[docID]
	return ok
}

// GetDocDescription retrieves the description for a document ID
func (r *Registry) GetDocDescription(docID string) (string, bool) {
	if r.Docs == nil {
		return "", false
	}
	desc, ok := r.Docs[docID]
	return desc, ok
}

// GetServiceSignature retrieves a parsed service signature by name
func (r *Registry) GetServiceSignature(serviceName string) (*ServiceSignature, error) {
	for _, sig := range r.Services {
		parsed, err := ParseServiceSignature(sig)
		if err != nil {
			return nil, err
		}
		if parsed.Name == serviceName {
			return parsed, nil
		}
	}
	return nil, fmt.Errorf("service not found: %s", serviceName)
}

// HasComponent checks if a component is registered
func (r *Registry) HasComponent(componentID string) bool {
	for _, comp := range r.Components {
		if comp == componentID {
			return true
		}
	}
	return false
}

// GetAPIDefinition retrieves an API definition by ID
func (r *Registry) GetAPIDefinition(apiID string) (*APIDefinition, error) {
	for i := range r.APIs {
		if r.APIs[i].ID == apiID {
			return &r.APIs[i], nil
		}
	}
	return nil, fmt.Errorf("API not found: %s", apiID)
}

// HasAPI checks if an API is registered
func (r *Registry) HasAPI(apiID string) bool {
	for _, api := range r.APIs {
		if api.ID == apiID {
			return true
		}
	}
	return false
}

// GetVariableDeclarations returns all parsed variable declarations
func (r *Registry) GetVariableDeclarations() (map[string]*VariableDeclaration, error) {
	result := make(map[string]*VariableDeclaration)

	for _, varDecl := range r.Vars {
		parsed, err := ParseVariableDeclaration(varDecl)
		if err != nil {
			return nil, err
		}
		result[parsed.Name] = parsed
	}

	return result, nil
}

// GetParamDeclarations returns all parsed parameter declarations
func (r *Registry) GetParamDeclarations() (map[string]*ParamDeclaration, error) {
	result := make(map[string]*ParamDeclaration)

	for _, paramDecl := range r.Params {
		parsed, err := ParseParamDeclaration(paramDecl)
		if err != nil {
			return nil, err
		}
		result[parsed.Name] = parsed
	}

	return result, nil
}

// IsInputPathAllowed checks if a read path is within allowed inputs
func (r *Registry) IsInputPathAllowed(path string) bool {
	return matchesAnyPattern(path, r.Files.Inputs)
}

// IsArtifactPathAllowed checks if a write path is within allowed artifacts
func (r *Registry) IsArtifactPathAllowed(path string) bool {
	return matchesAnyPattern(path, r.Files.Artifacts)
}

// matchesAnyPattern checks if a path matches any of the given patterns
// Supports glob-like patterns with * wildcard
func matchesAnyPattern(path string, patterns []string) bool {
	for _, pattern := range patterns {
		if matchPattern(path, pattern) {
			return true
		}
	}
	return false
}

// matchPattern performs simple glob-style pattern matching
func matchPattern(path, pattern string) bool {
	// Simple implementation: * matches any characters
	// For production use, consider using filepath.Match or a more robust library

	if !strings.Contains(pattern, "*") {
		return path == pattern
	}

	// Convert glob pattern to regex
	regexPattern := strings.ReplaceAll(regexp.QuoteMeta(pattern), `\*`, `.*`)
	matched, err := regexp.MatchString("^"+regexPattern+"$", path)
	if err != nil {
		return false
	}

	return matched
}

// HasSchema checks if a schema ID is registered (v3.9+)
func (r *Registry) HasSchema(schemaID string) bool {
	if r.Schemas == nil {
		return false
	}
	_, ok := r.Schemas[schemaID]
	return ok
}

// GetSchema retrieves a schema by ID (v3.9+)
func (r *Registry) GetSchema(schemaID string) (map[string]interface{}, error) {
	if r.Schemas == nil {
		return nil, fmt.Errorf("no schemas registered")
	}
	schema, ok := r.Schemas[schemaID]
	if !ok {
		return nil, fmt.Errorf("schema not found: %s", schemaID)
	}
	return schema, nil
}

// CoerceParamDefault converts the string default value of a ParamDeclaration to
// the appropriate Go type based on the declared type (spec §3.2).
//
// Supported type coercions:
//   - INT   → int64
//   - FLOAT → float64
//   - BOOL  → bool
//   - all others (STRING, OBJECT, ARRAY, etc.) → string (already stripped of quotes)
//
// Returns nil, nil if the declaration has no default.
func CoerceParamDefault(decl *ParamDeclaration) (interface{}, error) {
	if decl.Default == nil {
		return nil, nil
	}
	raw := *decl.Default
	switch strings.ToUpper(decl.Type) {
	case "INT":
		v, err := strconv.ParseInt(raw, 10, 64)
		if err != nil {
			return nil, fmt.Errorf("param %s: cannot parse default %q as INT: %w", decl.Name, raw, err)
		}
		return v, nil
	case "FLOAT":
		v, err := strconv.ParseFloat(raw, 64)
		if err != nil {
			return nil, fmt.Errorf("param %s: cannot parse default %q as FLOAT: %w", decl.Name, raw, err)
		}
		return v, nil
	case "BOOL":
		v, err := strconv.ParseBool(raw)
		if err != nil {
			return nil, fmt.Errorf("param %s: cannot parse default %q as BOOL: %w", decl.Name, raw, err)
		}
		return v, nil
	default:
		// STRING and all other types: return as-is (quotes already stripped by ParseParamDeclaration)
		return raw, nil
	}
}
