// Package codeagent provides the code agent registry and common utilities.
package codeagent

import (
	"context"
	"fmt"
	"sync"

	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/port"
)

// Registry implements port.CodeAgentRegistry with thread-safe agent management.
type Registry struct {
	mu       sync.RWMutex
	agents   map[domain.AgentProvider]port.CodeAgent
	defProv  domain.AgentProvider
	hasAgent bool
}

// NewRegistry creates a new empty agent registry.
func NewRegistry() *Registry {
	return &Registry{
		agents: make(map[domain.AgentProvider]port.CodeAgent),
	}
}

// Ensure Registry implements port.CodeAgentRegistry at compile time.
var _ port.CodeAgentRegistry = (*Registry)(nil)

// Register adds an agent implementation for a provider.
func (r *Registry) Register(agent port.CodeAgent) {
	r.mu.Lock()
	defer r.mu.Unlock()

	provider := agent.Provider()
	r.agents[provider] = agent

	// If this is the first agent, make it the default
	if !r.hasAgent {
		r.defProv = provider
		r.hasAgent = true
	}
}

// Get returns the agent for a specific provider.
func (r *Registry) Get(provider domain.AgentProvider) port.CodeAgent {
	r.mu.RLock()
	defer r.mu.RUnlock()

	return r.agents[provider]
}

// Default returns the default agent implementation.
func (r *Registry) Default() port.CodeAgent {
	r.mu.RLock()
	defer r.mu.RUnlock()

	if !r.hasAgent {
		return nil
	}
	return r.agents[r.defProv]
}

// SetDefault sets which provider should be used as the default.
func (r *Registry) SetDefault(provider domain.AgentProvider) error {
	r.mu.Lock()
	defer r.mu.Unlock()

	if _, exists := r.agents[provider]; !exists {
		return fmt.Errorf("agent provider %q is not registered", provider)
	}

	r.defProv = provider
	return nil
}

// Available returns all registered providers.
func (r *Registry) Available() []domain.AgentProvider {
	r.mu.RLock()
	defer r.mu.RUnlock()

	providers := make([]domain.AgentProvider, 0, len(r.agents))
	for p := range r.agents {
		providers = append(providers, p)
	}
	return providers
}

// AvailableAgents returns all registered agents that are currently available.
func (r *Registry) AvailableAgents(ctx context.Context) []port.CodeAgent {
	r.mu.RLock()
	defer r.mu.RUnlock()

	available := make([]port.CodeAgent, 0, len(r.agents))
	for _, agent := range r.agents {
		if agent.Available(ctx) {
			available = append(available, agent)
		}
	}
	return available
}

// DefaultProvider returns the current default provider.
// Returns empty string if no agents are registered.
func (r *Registry) DefaultProvider() domain.AgentProvider {
	r.mu.RLock()
	defer r.mu.RUnlock()

	if !r.hasAgent {
		return ""
	}
	return r.defProv
}

// Count returns the number of registered agents.
func (r *Registry) Count() int {
	r.mu.RLock()
	defer r.mu.RUnlock()

	return len(r.agents)
}
