package ucxi

import (
	"context"
	"fmt"
	"sync"
	"time"

	"chorus.services/bzzz/pkg/ucxl"
)

// BasicAddressResolver provides a basic implementation of AddressResolver
type BasicAddressResolver struct {
	// In-memory registry for announced content
	registry map[string]*ResolvedContent
	mutex    sync.RWMutex

	// P2P integration hooks (to be implemented later)
	announceHook  func(ctx context.Context, addr *ucxl.Address, content *Content) error
	discoverHook  func(ctx context.Context, pattern *ucxl.Address) ([]*ResolvedContent, error)
	
	// Configuration
	defaultTTL time.Duration
	nodeID     string
}

// NewBasicAddressResolver creates a new basic address resolver
func NewBasicAddressResolver(nodeID string) *BasicAddressResolver {
	return &BasicAddressResolver{
		registry:   make(map[string]*ResolvedContent),
		defaultTTL: 5 * time.Minute,
		nodeID:     nodeID,
	}
}

// SetAnnounceHook sets a hook function for content announcements (for P2P integration)
func (r *BasicAddressResolver) SetAnnounceHook(hook func(ctx context.Context, addr *ucxl.Address, content *Content) error) {
	r.announceHook = hook
}

// SetDiscoverHook sets a hook function for content discovery (for P2P integration)
func (r *BasicAddressResolver) SetDiscoverHook(hook func(ctx context.Context, pattern *ucxl.Address) ([]*ResolvedContent, error)) {
	r.discoverHook = hook
}

// Resolve resolves a UCXL address to content
func (r *BasicAddressResolver) Resolve(ctx context.Context, addr *ucxl.Address) (*ResolvedContent, error) {
	if addr == nil {
		return nil, fmt.Errorf("address cannot be nil")
	}

	key := r.generateRegistryKey(addr)

	r.mutex.RLock()
	resolved, exists := r.registry[key]
	r.mutex.RUnlock()

	if exists {
		// Check if content is still valid (TTL)
		if time.Now().Before(resolved.Resolved.Add(resolved.TTL)) {
			return resolved, nil
		}
		
		// Content expired, remove from registry
		r.mutex.Lock()
		delete(r.registry, key)
		r.mutex.Unlock()
	}

	// Try wildcard matching if exact match not found
	if !exists {
		if match := r.findWildcardMatch(addr); match != nil {
			return match, nil
		}
	}

	// If we have a discover hook, try P2P discovery
	if r.discoverHook != nil {
		results, err := r.discoverHook(ctx, addr)
		if err == nil && len(results) > 0 {
			// Cache the first result and return it
			result := results[0]
			r.cacheResolvedContent(key, result)
			return result, nil
		}
	}

	return nil, fmt.Errorf("address not found: %s", addr.String())
}

// Announce announces content at a UCXL address
func (r *BasicAddressResolver) Announce(ctx context.Context, addr *ucxl.Address, content *Content) error {
	if addr == nil {
		return fmt.Errorf("address cannot be nil")
	}
	if content == nil {
		return fmt.Errorf("content cannot be nil")
	}

	key := r.generateRegistryKey(addr)

	resolved := &ResolvedContent{
		Address:  addr,
		Content:  content,
		Source:   r.nodeID,
		Resolved: time.Now(),
		TTL:      r.defaultTTL,
	}

	// Store in local registry
	r.mutex.Lock()
	r.registry[key] = resolved
	r.mutex.Unlock()

	// Call P2P announce hook if available
	if r.announceHook != nil {
		if err := r.announceHook(ctx, addr, content); err != nil {
			// Log but don't fail - local announcement succeeded
			// In a real implementation, this would be logged properly
			return nil
		}
	}

	return nil
}

// Discover discovers content matching a pattern
func (r *BasicAddressResolver) Discover(ctx context.Context, pattern *ucxl.Address) ([]*ResolvedContent, error) {
	if pattern == nil {
		return nil, fmt.Errorf("pattern cannot be nil")
	}

	var results []*ResolvedContent

	// Search local registry
	r.mutex.RLock()
	for _, resolved := range r.registry {
		// Check if content is still valid (TTL)
		if time.Now().After(resolved.Resolved.Add(resolved.TTL)) {
			continue
		}

		// Check if address matches pattern
		if resolved.Address.Matches(pattern) {
			results = append(results, resolved)
		}
	}
	r.mutex.RUnlock()

	// Try P2P discovery if hook is available
	if r.discoverHook != nil {
		p2pResults, err := r.discoverHook(ctx, pattern)
		if err == nil {
			// Merge P2P results with local results
			// Cache P2P results for future use
			for _, result := range p2pResults {
				key := r.generateRegistryKey(result.Address)
				r.cacheResolvedContent(key, result)
				results = append(results, result)
			}
		}
	}

	return results, nil
}

// findWildcardMatch searches for wildcard matches in the registry
func (r *BasicAddressResolver) findWildcardMatch(target *ucxl.Address) *ResolvedContent {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	for _, resolved := range r.registry {
		// Check if content is still valid (TTL)
		if time.Now().After(resolved.Resolved.Add(resolved.TTL)) {
			continue
		}

		// Check if target matches the registered address pattern
		if target.Matches(resolved.Address) {
			return resolved
		}
	}

	return nil
}

// generateRegistryKey generates a unique key for registry storage
func (r *BasicAddressResolver) generateRegistryKey(addr *ucxl.Address) string {
	return fmt.Sprintf("%s:%s@%s:%s/%s",
		addr.Agent, addr.Role, addr.Project, addr.Task, addr.TemporalSegment.String())
}

// cacheResolvedContent caches resolved content in the local registry
func (r *BasicAddressResolver) cacheResolvedContent(key string, resolved *ResolvedContent) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.registry[key] = resolved
}

// GetRegistryStats returns statistics about the registry
func (r *BasicAddressResolver) GetRegistryStats() map[string]interface{} {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	active := 0
	expired := 0
	now := time.Now()

	for _, resolved := range r.registry {
		if now.Before(resolved.Resolved.Add(resolved.TTL)) {
			active++
		} else {
			expired++
		}
	}

	return map[string]interface{}{
		"total_entries":  len(r.registry),
		"active_entries": active,
		"expired_entries": expired,
		"node_id":        r.nodeID,
	}
}

// CleanupExpired removes expired entries from the registry
func (r *BasicAddressResolver) CleanupExpired() int {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	now := time.Now()
	removed := 0

	for key, resolved := range r.registry {
		if now.After(resolved.Resolved.Add(resolved.TTL)) {
			delete(r.registry, key)
			removed++
		}
	}

	return removed
}

// SetDefaultTTL sets the default TTL for cached content
func (r *BasicAddressResolver) SetDefaultTTL(ttl time.Duration) {
	r.defaultTTL = ttl
}