package protocol

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

// BzzzURI represents a parsed bzzz:// URI with semantic addressing
// Grammar: bzzz://[agent]:[role]@[project]:[task]/[path][?query][#fragment]
type BzzzURI struct {
	// Core addressing components
	Agent   string // Agent identifier (e.g., "claude", "any", "*")
	Role    string // Agent role (e.g., "frontend", "backend", "architect")
	Project string // Project context (e.g., "chorus", "bzzz")
	Task    string // Task identifier (e.g., "implement", "review", "test", "*")
	
	// Resource path
	Path string // Resource path (e.g., "/src/main.go", "/docs/api.md")
	
	// Standard URI components
	Query    string // Query parameters
	Fragment string // Fragment identifier
	
	// Original raw URI string
	Raw string
}

// URI grammar constants
const (
	BzzzScheme = "bzzz"
	
	// Special identifiers
	AnyAgent   = "any"
	AnyRole    = "any"
	AnyProject = "any"
	AnyTask    = "any"
	Wildcard   = "*"
)

// Validation patterns
var (
	// Component validation patterns
	agentPattern   = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$|^\*$|^any$`)
	rolePattern    = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$|^\*$|^any$`)
	projectPattern = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$|^\*$|^any$`)
	taskPattern    = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$|^\*$|^any$`)
	pathPattern    = regexp.MustCompile(`^/[a-zA-Z0-9\-_/\.]*$|^$`)
	
	// Full URI pattern for validation
	bzzzURIPattern = regexp.MustCompile(`^bzzz://([a-zA-Z0-9\-_*]|any):([a-zA-Z0-9\-_*]|any)@([a-zA-Z0-9\-_*]|any):([a-zA-Z0-9\-_*]|any)(/[a-zA-Z0-9\-_/\.]*)?(\?[^#]*)?(\#.*)?$`)
)

// ParseBzzzURI parses a bzzz:// URI string into a BzzzURI struct
func ParseBzzzURI(uri string) (*BzzzURI, error) {
	if uri == "" {
		return nil, fmt.Errorf("empty URI")
	}
	
	// Basic scheme validation
	if !strings.HasPrefix(uri, BzzzScheme+"://") {
		return nil, fmt.Errorf("invalid scheme: expected '%s'", BzzzScheme)
	}
	
	// Use Go's standard URL parser for basic parsing
	parsedURL, err := url.Parse(uri)
	if err != nil {
		return nil, fmt.Errorf("failed to parse URI: %w", err)
	}
	
	if parsedURL.Scheme != BzzzScheme {
		return nil, fmt.Errorf("invalid scheme: expected '%s', got '%s'", BzzzScheme, parsedURL.Scheme)
	}
	
	// Parse the authority part (user:pass@host:port becomes agent:role@project:task)
	userInfo := parsedURL.User
	if userInfo == nil {
		return nil, fmt.Errorf("missing agent:role information")
	}
	
	username := userInfo.Username()
	password, hasPassword := userInfo.Password()
	if !hasPassword {
		return nil, fmt.Errorf("missing role information")
	}
	
	agent := username
	role := password
	
	// Parse host:port as project:task
	hostPort := parsedURL.Host
	if hostPort == "" {
		return nil, fmt.Errorf("missing project:task information")
	}
	
	// Split host:port to get project:task
	parts := strings.Split(hostPort, ":")
	if len(parts) != 2 {
		return nil, fmt.Errorf("invalid project:task format: expected 'project:task'")
	}
	
	project := parts[0]
	task := parts[1]
	
	// Create BzzzURI instance
	bzzzURI := &BzzzURI{
		Agent:    agent,
		Role:     role,
		Project:  project,
		Task:     task,
		Path:     parsedURL.Path,
		Query:    parsedURL.RawQuery,
		Fragment: parsedURL.Fragment,
		Raw:      uri,
	}
	
	// Validate components
	if err := bzzzURI.Validate(); err != nil {
		return nil, fmt.Errorf("validation failed: %w", err)
	}
	
	return bzzzURI, nil
}

// Validate validates all components of the BzzzURI
func (u *BzzzURI) Validate() error {
	// Validate agent
	if u.Agent == "" {
		return fmt.Errorf("agent cannot be empty")
	}
	if !agentPattern.MatchString(u.Agent) {
		return fmt.Errorf("invalid agent format: '%s'", u.Agent)
	}
	
	// Validate role
	if u.Role == "" {
		return fmt.Errorf("role cannot be empty")
	}
	if !rolePattern.MatchString(u.Role) {
		return fmt.Errorf("invalid role format: '%s'", u.Role)
	}
	
	// Validate project
	if u.Project == "" {
		return fmt.Errorf("project cannot be empty")
	}
	if !projectPattern.MatchString(u.Project) {
		return fmt.Errorf("invalid project format: '%s'", u.Project)
	}
	
	// Validate task
	if u.Task == "" {
		return fmt.Errorf("task cannot be empty")
	}
	if !taskPattern.MatchString(u.Task) {
		return fmt.Errorf("invalid task format: '%s'", u.Task)
	}
	
	// Validate path (optional)
	if u.Path != "" && !pathPattern.MatchString(u.Path) {
		return fmt.Errorf("invalid path format: '%s'", u.Path)
	}
	
	return nil
}

// String returns the canonical string representation of the BzzzURI
func (u *BzzzURI) String() string {
	uri := fmt.Sprintf("%s://%s:%s@%s:%s", BzzzScheme, u.Agent, u.Role, u.Project, u.Task)
	
	if u.Path != "" {
		uri += u.Path
	}
	
	if u.Query != "" {
		uri += "?" + u.Query
	}
	
	if u.Fragment != "" {
		uri += "#" + u.Fragment
	}
	
	return uri
}

// Normalize normalizes the URI components for consistent addressing
func (u *BzzzURI) Normalize() {
	// Convert empty wildcards to standard wildcard
	if u.Agent == "" {
		u.Agent = Wildcard
	}
	if u.Role == "" {
		u.Role = Wildcard
	}
	if u.Project == "" {
		u.Project = Wildcard
	}
	if u.Task == "" {
		u.Task = Wildcard
	}
	
	// Normalize to lowercase for consistency
	u.Agent = strings.ToLower(u.Agent)
	u.Role = strings.ToLower(u.Role)
	u.Project = strings.ToLower(u.Project)
	u.Task = strings.ToLower(u.Task)
	
	// Clean path
	if u.Path != "" && !strings.HasPrefix(u.Path, "/") {
		u.Path = "/" + u.Path
	}
}

// IsWildcard checks if a component is a wildcard or "any"
func IsWildcard(component string) bool {
	return component == Wildcard || component == AnyAgent || component == AnyRole || 
		   component == AnyProject || component == AnyTask
}

// Matches checks if this URI matches another URI (with wildcard support)
func (u *BzzzURI) Matches(other *BzzzURI) bool {
	if other == nil {
		return false
	}
	
	// Check each component with wildcard support
	if !componentMatches(u.Agent, other.Agent) {
		return false
	}
	if !componentMatches(u.Role, other.Role) {
		return false
	}
	if !componentMatches(u.Project, other.Project) {
		return false
	}
	if !componentMatches(u.Task, other.Task) {
		return false
	}
	
	// Path matching (exact or wildcard)
	if u.Path != "" && other.Path != "" && u.Path != other.Path {
		return false
	}
	
	return true
}

// componentMatches checks if two components match (with wildcard support)
func componentMatches(a, b string) bool {
	// Exact match
	if a == b {
		return true
	}
	
	// Wildcard matching
	if IsWildcard(a) || IsWildcard(b) {
		return true
	}
	
	return false
}

// GetSelectorPriority returns a priority score for URI matching (higher = more specific)
func (u *BzzzURI) GetSelectorPriority() int {
	priority := 0
	
	// More specific components get higher priority
	if !IsWildcard(u.Agent) {
		priority += 8
	}
	if !IsWildcard(u.Role) {
		priority += 4
	}
	if !IsWildcard(u.Project) {
		priority += 2
	}
	if !IsWildcard(u.Task) {
		priority += 1
	}
	
	// Path specificity adds priority
	if u.Path != "" && u.Path != "/" {
		priority += 1
	}
	
	return priority
}

// ToAddress returns a simplified address representation for P2P routing
func (u *BzzzURI) ToAddress() string {
	return fmt.Sprintf("%s:%s@%s:%s", u.Agent, u.Role, u.Project, u.Task)
}

// ValidateBzzzURIString validates a bzzz:// URI string without parsing
func ValidateBzzzURIString(uri string) error {
	if uri == "" {
		return fmt.Errorf("empty URI")
	}
	
	if !bzzzURIPattern.MatchString(uri) {
		return fmt.Errorf("invalid bzzz:// URI format")
	}
	
	return nil
}

// NewBzzzURI creates a new BzzzURI with the given components
func NewBzzzURI(agent, role, project, task, path string) *BzzzURI {
	uri := &BzzzURI{
		Agent:   agent,
		Role:    role,
		Project: project,
		Task:    task,
		Path:    path,
	}
	uri.Normalize()
	return uri
}

// ParseAddress parses a simplified address format (agent:role@project:task)
func ParseAddress(addr string) (*BzzzURI, error) {
	// Convert simplified address to full URI
	fullURI := BzzzScheme + "://" + addr
	return ParseBzzzURI(fullURI)
}