package execution

import (
	"context"
	"fmt"
)

// LifecycleProvider is an optional interface for providers that need start/stop lifecycle management.
type LifecycleProvider interface {
	Start(ctx context.Context) error
	Stop(ctx context.Context) error
}

type entry struct {
	typeName string
	provider Provider
}

// Registry holds all registered providers keyed by instance key.
type Registry struct {
	entries map[string]entry
	order   []string // insertion order for lifecycle and diagnostics
}

func NewRegistry() *Registry {
	return &Registry{entries: make(map[string]entry)}
}

// Register adds an adapter using a.Name() as both instance key and type name.
// This preserves backward compatibility for callers that register by type.
func (r *Registry) Register(a Provider) {
	r.RegisterKeyed(a.Name(), a.Name(), a)
}

// RegisterKeyed adds an adapter with an explicit instance key and type name.
// If key is empty, typeName is used as the key. Duplicate keys replace the
// existing entry but preserve insertion order.
func (r *Registry) RegisterKeyed(key, typeName string, a Provider) {
	if key == "" {
		key = typeName
	}
	if _, exists := r.entries[key]; !exists {
		r.order = append(r.order, key)
	}
	r.entries[key] = entry{typeName: typeName, provider: a}
}

// Get returns the adapter registered under the exact instance key.
func (r *Registry) Get(key string) (Provider, bool) {
	e, ok := r.entries[key]
	return e.provider, ok
}

// Lookup returns an adapter by exact instance key first. If no exact match is
// found it falls back to type-name lookup: succeeds when exactly one instance
// of that type is registered, and returns an ambiguous error when multiple
// instances share the same type name.
func (r *Registry) Lookup(name string) (Provider, error) {
	if e, ok := r.entries[name]; ok {
		return e.provider, nil
	}
	var matchKeys []string
	for _, k := range r.order {
		if r.entries[k].typeName == name {
			matchKeys = append(matchKeys, k)
		}
	}
	switch len(matchKeys) {
	case 0:
		return nil, fmt.Errorf("adapter %q not found", name)
	case 1:
		return r.entries[matchKeys[0]].provider, nil
	default:
		return nil, fmt.Errorf("adapter %q is ambiguous: matches instance keys %v; use an instance key", name, matchKeys)
	}
}

// All returns all registered adapters in registration order.
func (r *Registry) All() []Provider {
	out := make([]Provider, 0, len(r.order))
	for _, key := range r.order {
		out = append(out, r.entries[key].provider)
	}
	return out
}

// MustGet returns the adapter or panics — useful during bootstrap validation.
func (r *Registry) MustGet(name string) Provider {
	a, err := r.Lookup(name)
	if err != nil {
		panic(fmt.Sprintf("adapter %q not registered", name))
	}
	return a
}

// Start calls Start on all registered LifecycleProviders in registration order.
// On failure, already-started adapters are stopped in reverse order before returning.
func (r *Registry) Start(ctx context.Context) error {
	started := make([]string, 0, len(r.order))
	for _, key := range r.order {
		lc, ok := r.entries[key].provider.(LifecycleProvider)
		if !ok {
			continue
		}
		if err := lc.Start(ctx); err != nil {
			for i := len(started) - 1; i >= 0; i-- {
				if startedLC, ok := r.entries[started[i]].provider.(LifecycleProvider); ok {
					_ = startedLC.Stop(context.Background())
				}
			}
			return fmt.Errorf("adapter %q start: %w", key, err)
		}
		started = append(started, key)
	}
	return nil
}

// Stop calls Stop on all registered LifecycleProviders in reverse registration order.
// All adapters are stopped even if some fail; the first error is returned.
func (r *Registry) Stop(ctx context.Context) error {
	var firstErr error
	for i := len(r.order) - 1; i >= 0; i-- {
		key := r.order[i]
		lc, ok := r.entries[key].provider.(LifecycleProvider)
		if !ok {
			continue
		}
		if err := lc.Stop(ctx); err != nil {
			if firstErr == nil {
				firstErr = fmt.Errorf("adapter %q stop: %w", key, err)
			}
		}
	}
	return firstErr
}

// Keys returns all registered adapter instance keys in insertion order.
func (r *Registry) Keys() []string {
	out := make([]string, len(r.order))
	copy(out, r.order)
	return out
}

// TypeOf returns the type name of the adapter registered under key.
func (r *Registry) TypeOf(key string) (string, bool) {
	e, ok := r.entries[key]
	if !ok {
		return "", false
	}
	return e.typeName, true
}
