package plugins

import (
	"fmt"
	"sync"
	"time"
)

// SkillPlugin represents a custom skill plugin
type SkillPlugin interface {
	// Name returns the plugin name
	Name() string
	// Version returns the plugin version
	Version() string
	// Execute executes the plugin with the given input
	Execute(input map[string]interface{}) (map[string]interface{}, error)
	// Validate validates the plugin configuration
	Validate() error
	// Description returns a human-readable description
	Description() string
}

// PluginMetadata holds metadata about a plugin
type PluginMetadata struct {
	Name        string                 `json:"name"`
	Version     string                 `json:"version"`
	Author      string                 `json:"author"`
	Description string                 `json:"description"`
	URL         string                 `json:"url"`
	Config      map[string]interface{} `json:"config,omitempty"`
	LoadedAt    time.Time              `json:"loaded_at"`
	Enabled     bool                   `json:"enabled"`
}

// PluginRegistry manages custom skill plugins
type PluginRegistry struct {
	mu            sync.RWMutex
	plugins       map[string]SkillPlugin
	metadata      map[string]*PluginMetadata
	executionLog  map[string][]*ExecutionRecord
	stats         *PluginStats
}

// ExecutionRecord tracks plugin execution
type ExecutionRecord struct {
	PluginName string
	Timestamp  time.Time
	Duration   time.Duration
	Input      map[string]interface{}
	Output     map[string]interface{}
	Error      error
	Success    bool
}

// PluginStats tracks plugin statistics
type PluginStats struct {
	TotalExecutions    int
	SuccessfulExecutions int
	FailedExecutions   int
	TotalPlugins       int
	EnabledPlugins     int
	AverageExecutionTime time.Duration
}

// NewPluginRegistry creates a new plugin registry
func NewPluginRegistry() *PluginRegistry {
	return &PluginRegistry{
		plugins:      make(map[string]SkillPlugin),
		metadata:     make(map[string]*PluginMetadata),
		executionLog: make(map[string][]*ExecutionRecord),
		stats: &PluginStats{
			TotalExecutions:     0,
			SuccessfulExecutions: 0,
			FailedExecutions:     0,
			TotalPlugins:         0,
			EnabledPlugins:       0,
		},
	}
}

// Register registers a new plugin
func (pr *PluginRegistry) Register(plugin SkillPlugin, author string, config map[string]interface{}) error {
	if plugin == nil {
		return fmt.Errorf("plugin cannot be nil")
	}

	// Validate plugin
	if err := plugin.Validate(); err != nil {
		return fmt.Errorf("plugin validation failed: %w", err)
	}

	pr.mu.Lock()
	defer pr.mu.Unlock()

	name := plugin.Name()
	if _, exists := pr.plugins[name]; exists {
		return fmt.Errorf("plugin already registered: %s", name)
	}

	pr.plugins[name] = plugin
	pr.metadata[name] = &PluginMetadata{
		Name:        name,
		Version:     plugin.Version(),
		Author:      author,
		Description: plugin.Description(),
		URL:         fmt.Sprintf("plugin://%s", name),
		Config:      config,
		LoadedAt:    time.Now(),
		Enabled:     true,
	}

	pr.stats.TotalPlugins++
	pr.stats.EnabledPlugins++
	pr.executionLog[name] = make([]*ExecutionRecord, 0)

	return nil
}

// Unregister unregisters a plugin
func (pr *PluginRegistry) Unregister(name string) error {
	pr.mu.Lock()
	defer pr.mu.Unlock()

	if _, exists := pr.plugins[name]; !exists {
		return fmt.Errorf("plugin not found: %s", name)
	}

	delete(pr.plugins, name)
	if pr.metadata[name].Enabled {
		pr.stats.EnabledPlugins--
	}
	pr.stats.TotalPlugins--
	delete(pr.metadata, name)

	return nil
}

// Execute executes a plugin by name
func (pr *PluginRegistry) Execute(name string, input map[string]interface{}) (map[string]interface{}, error) {
	pr.mu.RLock()
	plugin, exists := pr.plugins[name]
	metadata, metaExists := pr.metadata[name]
	pr.mu.RUnlock()

	if !exists {
		return nil, fmt.Errorf("plugin not found: %s", name)
	}

	if !metaExists || !metadata.Enabled {
		return nil, fmt.Errorf("plugin is disabled: %s", name)
	}

	start := time.Now()
	output, err := plugin.Execute(input)
	duration := time.Since(start)

	// Record execution
	record := &ExecutionRecord{
		PluginName: name,
		Timestamp:  start,
		Duration:   duration,
		Input:      input,
		Output:     output,
		Error:      err,
		Success:    err == nil,
	}

	pr.mu.Lock()
	pr.executionLog[name] = append(pr.executionLog[name], record)
	pr.stats.TotalExecutions++
	if err == nil {
		pr.stats.SuccessfulExecutions++
	} else {
		pr.stats.FailedExecutions++
	}
	pr.mu.Unlock()

	return output, err
}

// Get retrieves a plugin by name
func (pr *PluginRegistry) Get(name string) (SkillPlugin, bool) {
	pr.mu.RLock()
	defer pr.mu.RUnlock()

	plugin, exists := pr.plugins[name]
	return plugin, exists
}

// GetMetadata retrieves plugin metadata
func (pr *PluginRegistry) GetMetadata(name string) (*PluginMetadata, bool) {
	pr.mu.RLock()
	defer pr.mu.RUnlock()

	meta, exists := pr.metadata[name]
	return meta, exists
}

// ListPlugins returns all registered plugins
func (pr *PluginRegistry) ListPlugins() map[string]*PluginMetadata {
	pr.mu.RLock()
	defer pr.mu.RUnlock()

	result := make(map[string]*PluginMetadata)
	for name, meta := range pr.metadata {
		result[name] = meta
	}

	return result
}

// EnablePlugin enables a plugin
func (pr *PluginRegistry) EnablePlugin(name string) error {
	pr.mu.Lock()
	defer pr.mu.Unlock()

	meta, exists := pr.metadata[name]
	if !exists {
		return fmt.Errorf("plugin not found: %s", name)
	}

	if meta.Enabled {
		return fmt.Errorf("plugin already enabled: %s", name)
	}

	meta.Enabled = true
	pr.stats.EnabledPlugins++

	return nil
}

// DisablePlugin disables a plugin
func (pr *PluginRegistry) DisablePlugin(name string) error {
	pr.mu.Lock()
	defer pr.mu.Unlock()

	meta, exists := pr.metadata[name]
	if !exists {
		return fmt.Errorf("plugin not found: %s", name)
	}

	if !meta.Enabled {
		return fmt.Errorf("plugin already disabled: %s", name)
	}

	meta.Enabled = false
	pr.stats.EnabledPlugins--

	return nil
}

// GetExecutionLog returns execution history for a plugin
func (pr *PluginRegistry) GetExecutionLog(name string) []*ExecutionRecord {
	pr.mu.RLock()
	defer pr.mu.RUnlock()

	if log, exists := pr.executionLog[name]; exists {
		result := make([]*ExecutionRecord, len(log))
		copy(result, log)
		return result
	}

	return make([]*ExecutionRecord, 0)
}

// GetStats returns registry statistics
func (pr *PluginRegistry) GetStats() *PluginStats {
	pr.mu.RLock()
	defer pr.mu.RUnlock()

	stats := *pr.stats
	if stats.TotalExecutions > 0 {
		totalDuration := time.Duration(0)
		for _, log := range pr.executionLog {
			for _, record := range log {
				totalDuration += record.Duration
			}
		}
		stats.AverageExecutionTime = totalDuration / time.Duration(stats.TotalExecutions)
	}

	return &stats
}

// ResolvePluginURL resolves a plugin:// URL
func (pr *PluginRegistry) ResolvePluginURL(url string) (SkillPlugin, error) {
	if len(url) < 9 || url[:9] != "plugin://" {
		return nil, fmt.Errorf("invalid plugin URL: %s", url)
	}

	name := url[9:] // Remove "plugin://" prefix
	plugin, exists := pr.Get(name)
	if !exists {
		return nil, fmt.Errorf("plugin not found: %s", name)
	}

	return plugin, nil
}

// Clear clears all plugins
func (pr *PluginRegistry) Clear() {
	pr.mu.Lock()
	defer pr.mu.Unlock()

	pr.plugins = make(map[string]SkillPlugin)
	pr.metadata = make(map[string]*PluginMetadata)
	pr.executionLog = make(map[string][]*ExecutionRecord)
	pr.stats = &PluginStats{}
}

// IsPluginURL checks if a URL is a plugin URL
func IsPluginURL(url string) bool {
	return len(url) > 9 && url[:9] == "plugin://"
}

// ExtractPluginName extracts plugin name from plugin URL
func ExtractPluginName(url string) string {
	if IsPluginURL(url) {
		return url[9:]
	}
	return ""
}
