package ucxl

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"
)

// UCXLAddress represents a parsed UCXL address
type UCXLAddress struct {
	Raw       string `json:"raw"`
	Agent     string `json:"agent"`     // Agent ID or "*" for any
	Role      string `json:"role"`      // Agent role or "*" for any  
	Project   string `json:"project"`   // Project identifier or "*" for any
	Task      string `json:"task"`      // Task identifier or "*" for any
	Path      string `json:"path"`      // Resource path (optional)
	Temporal  string `json:"temporal"`  // Temporal navigation (optional)
}

// UCXLAddressRegex matches valid UCXL address format
// Format: ucxl://agent:role@project:task/path*temporal/
var UCXLAddressRegex = regexp.MustCompile(`^ucxl://([^:]+):([^@]+)@([^:]+):([^/]+)(/[^*]*)?(\*[^/]*)?/?$`)

// ParseUCXLAddress parses a UCXL address string into its components
func ParseUCXLAddress(address string) (*UCXLAddress, error) {
	if address == "" {
		return nil, fmt.Errorf("empty UCXL address")
	}

	// Check if it starts with ucxl://
	if !strings.HasPrefix(address, "ucxl://") {
		return nil, fmt.Errorf("invalid UCXL address: must start with 'ucxl://'")
	}

	// Parse using regex
	matches := UCXLAddressRegex.FindStringSubmatch(address)
	if matches == nil {
		return nil, fmt.Errorf("invalid UCXL address format: %s", address)
	}

	ucxlAddr := &UCXLAddress{
		Raw:     address,
		Agent:   matches[1],
		Role:    matches[2],
		Project: matches[3],
		Task:    matches[4],
		Path:    strings.TrimPrefix(matches[5], "/"),
		Temporal: strings.TrimPrefix(matches[6], "*"),
	}

	// Validate components
	if err := validateUCXLComponents(ucxlAddr); err != nil {
		return nil, fmt.Errorf("invalid UCXL address: %w", err)
	}

	return ucxlAddr, nil
}

// validateUCXLComponents validates individual components of a UCXL address
func validateUCXLComponents(addr *UCXLAddress) error {
	// Agent can be any non-empty string or "*"
	if addr.Agent == "" {
		return fmt.Errorf("agent cannot be empty")
	}

	// Role can be any non-empty string or "*"
	if addr.Role == "" {
		return fmt.Errorf("role cannot be empty")
	}

	// Project can be any non-empty string or "*"
	if addr.Project == "" {
		return fmt.Errorf("project cannot be empty")
	}

	// Task can be any non-empty string or "*"
	if addr.Task == "" {
		return fmt.Errorf("task cannot be empty")
	}

	// Path is optional, but if present should be valid
	if addr.Path != "" {
		// URL decode and validate path
		decoded, err := url.PathUnescape(addr.Path)
		if err != nil {
			return fmt.Errorf("invalid path encoding: %w", err)
		}
		addr.Path = decoded
	}

	// Temporal is optional
	if addr.Temporal != "" {
		// Validate temporal navigation syntax
		if !isValidTemporal(addr.Temporal) {
			return fmt.Errorf("invalid temporal navigation syntax: %s", addr.Temporal)
		}
	}

	return nil
}

// isValidTemporal validates temporal navigation syntax
func isValidTemporal(temporal string) bool {
	// Valid temporal patterns:
	// ^/ - latest version
	// ~/  - earliest version
	// @timestamp - specific timestamp
	// ~n/ - n versions back
	// ^n/ - n versions forward
	validPatterns := []string{
		`^\^/?$`,           // ^/ or ^
		`^~/?$`,            // ~/ or ~
		`^@\d+/?$`,         // @timestamp
		`^~\d+/?$`,         // ~n versions back
		`^\^\d+/?$`,        // ^n versions forward
	}

	for _, pattern := range validPatterns {
		matched, _ := regexp.MatchString(pattern, temporal)
		if matched {
			return true
		}
	}
	return false
}

// GenerateUCXLAddress creates a UCXL address from components
func GenerateUCXLAddress(agent, role, project, task, path, temporal string) (string, error) {
	// Validate required components
	if agent == "" || role == "" || project == "" || task == "" {
		return "", fmt.Errorf("agent, role, project, and task are required")
	}

	// Build address
	address := fmt.Sprintf("ucxl://%s:%s@%s:%s", agent, role, project, task)

	// Add path if provided
	if path != "" {
		if !strings.HasPrefix(path, "/") {
			path = "/" + path
		}
		// URL encode the path
		encodedPath := url.PathEscape(path)
		address += encodedPath
	}

	// Add temporal navigation if provided
	if temporal != "" {
		if !strings.HasPrefix(temporal, "*") {
			temporal = "*" + temporal
		}
		address += temporal
	}

	// Always end with /
	if !strings.HasSuffix(address, "/") {
		address += "/"
	}

	// Validate the generated address
	_, err := ParseUCXLAddress(address)
	if err != nil {
		return "", fmt.Errorf("generated invalid UCXL address: %w", err)
	}

	return address, nil
}

// IsValidUCXLAddress checks if a string is a valid UCXL address
func IsValidUCXLAddress(address string) bool {
	_, err := ParseUCXLAddress(address)
	return err == nil
}

// NormalizeUCXLAddress normalizes a UCXL address to standard format
func NormalizeUCXLAddress(address string) (string, error) {
	parsed, err := ParseUCXLAddress(address)
	if err != nil {
		return "", err
	}

	// Regenerate in standard format
	return GenerateUCXLAddress(
		parsed.Agent,
		parsed.Role,
		parsed.Project,
		parsed.Task,
		parsed.Path,
		parsed.Temporal,
	)
}

// MatchesPattern checks if an address matches a pattern (supports wildcards)
func (addr *UCXLAddress) MatchesPattern(pattern *UCXLAddress) bool {
	// Check agent
	if pattern.Agent != "*" && pattern.Agent != addr.Agent {
		return false
	}

	// Check role
	if pattern.Role != "*" && pattern.Role != addr.Role {
		return false
	}

	// Check project
	if pattern.Project != "*" && pattern.Project != addr.Project {
		return false
	}

	// Check task
	if pattern.Task != "*" && pattern.Task != addr.Task {
		return false
	}

	// Path matching (prefix-based if pattern ends with *)
	if pattern.Path != "" {
		if strings.HasSuffix(pattern.Path, "*") {
			prefix := strings.TrimSuffix(pattern.Path, "*")
			if !strings.HasPrefix(addr.Path, prefix) {
				return false
			}
		} else if pattern.Path != addr.Path {
			return false
		}
	}

	return true
}

// ToMap converts the UCXL address to a map for JSON serialization
func (addr *UCXLAddress) ToMap() map[string]string {
	return map[string]string{
		"raw":      addr.Raw,
		"agent":    addr.Agent,
		"role":     addr.Role,
		"project":  addr.Project,
		"task":     addr.Task,
		"path":     addr.Path,
		"temporal": addr.Temporal,
	}
}

// String returns the string representation of the UCXL address
func (addr *UCXLAddress) String() string {
	return addr.Raw
}