package adapters

import (
	"go.uber.org/zap"
	"google.golang.org/protobuf/types/known/structpb"

	"iop/packages/go/config"
	runtime "iop/packages/go/execution"
	iop "iop/proto/gen/iop"
)

// BuildFromPayload creates a Registry from a NodeConfigPayload received from edge.
func BuildFromPayload(payload *iop.NodeConfigPayload, logger *zap.Logger) (*runtime.Registry, error) {
	set, err := BuildConfigSet(payload, logger)
	if err != nil {
		return nil, err
	}
	return set.Registry, nil
}

func ollamaConfFromProto(m *iop.OllamaAdapterConfig) config.OllamaConf {
	return config.OllamaConf{
		Enabled:          true,
		BaseURL:          m.GetBaseUrl(),
		ContextSize:      int(m.GetContextSize()),
		Capacity:         int(m.GetCapacity()),
		MaxQueue:         int(m.GetMaxQueue()),
		QueueTimeoutMS:   int(m.GetQueueTimeoutMs()),
		RequestTimeoutMS: int(m.GetRequestTimeoutMs()),
	}
}

func vllmConfFromProto(m *iop.VllmAdapterConfig) config.VllmConf {
	return config.VllmConf{
		Enabled:          true,
		Endpoint:         m.GetEndpoint(),
		Capacity:         int(m.GetCapacity()),
		MaxQueue:         int(m.GetMaxQueue()),
		QueueTimeoutMS:   int(m.GetQueueTimeoutMs()),
		RequestTimeoutMS: int(m.GetRequestTimeoutMs()),
	}
}

func openAICompatConfFromProto(m *iop.OpenAICompatAdapterConfig) config.OpenAICompatConf {
	return config.OpenAICompatConf{
		Enabled:          true,
		Provider:         m.GetProvider(),
		Endpoint:         m.GetEndpoint(),
		Headers:          m.GetHeaders(),
		Capacity:         int(m.GetCapacity()),
		MaxQueue:         int(m.GetMaxQueue()),
		QueueTimeoutMS:   int(m.GetQueueTimeoutMs()),
		RequestTimeoutMS: int(m.GetRequestTimeoutMs()),
		RuntimeProfile:   concreteProfileFromProto(m.GetProtocolProfile()),
	}
}

// concreteProfileFromProto converts a wire ConcreteProtocolProfile to the
// runtime config snapshot. Returns nil when the profile is absent so legacy
// payloads without a profile remain source-compatible.
func concreteProfileFromProto(m *iop.ConcreteProtocolProfile) *config.ConcreteProtocolProfile {
	if m == nil {
		return nil
	}
	cp := &config.ConcreteProtocolProfile{
		ID: m.GetId(),
		ProtocolProfileConf: config.ProtocolProfileConf{
			Driver:  config.ProtocolDriver(m.GetDriver()),
			BaseURL: m.GetBaseUrl(),
			Auth: config.ProtocolAuthConf{
				Header: m.GetAuth().GetHeader(),
				Scheme: m.GetAuth().GetScheme(),
			},
			Capabilities: append([]string(nil), m.GetCapabilities()...),
		},
	}
	if len(m.GetOperations()) > 0 {
		cp.Operations = make(map[string]string, len(m.GetOperations()))
		for k, v := range m.GetOperations() {
			cp.Operations[k] = v
		}
	}
	if len(m.GetModelMapping()) > 0 {
		cp.ModelMapping = make(map[string]string, len(m.GetModelMapping()))
		for k, v := range m.GetModelMapping() {
			cp.ModelMapping[k] = v
		}
	}
	if m.GetExtensions() != nil {
		cp.Extensions = m.GetExtensions().AsMap()
	}
	cloned := cp.Clone()
	return &cloned
}

// Legacy structpb fallback paths for payloads from older edge versions
// that populate AdapterConfig.settings instead of the typed oneof.

func ollamaConfFromStruct(s *structpb.Struct) config.OllamaConf {
	cfg := config.OllamaConf{Enabled: true}
	if s == nil {
		return cfg
	}
	m := s.AsMap()
	if v, ok := m["base_url"].(string); ok {
		cfg.BaseURL = v
	}
	if v, ok := intSetting(m, "context_size"); ok {
		cfg.ContextSize = v
	} else if v, ok := intSetting(m, "num_ctx"); ok {
		cfg.ContextSize = v
	}
	if v, ok := intSetting(m, "capacity"); ok {
		cfg.Capacity = v
	}
	if v, ok := intSetting(m, "max_queue"); ok {
		cfg.MaxQueue = v
	}
	if v, ok := intSetting(m, "queue_timeout_ms"); ok {
		cfg.QueueTimeoutMS = v
	}
	if v, ok := intSetting(m, "request_timeout_ms"); ok {
		cfg.RequestTimeoutMS = v
	}
	return cfg
}

func intSetting(m map[string]any, key string) (int, bool) {
	v, ok := m[key]
	if !ok {
		return 0, false
	}
	switch n := v.(type) {
	case int:
		return n, true
	case int32:
		return int(n), true
	case int64:
		return int(n), true
	case float64:
		return int(n), true
	default:
		return 0, false
	}
}

func vllmConfFromStruct(s *structpb.Struct) config.VllmConf {
	cfg := config.VllmConf{Enabled: true}
	if s == nil {
		return cfg
	}
	m := s.AsMap()
	if v, ok := m["endpoint"].(string); ok {
		cfg.Endpoint = v
	}
	if v, ok := intSetting(m, "capacity"); ok {
		cfg.Capacity = v
	}
	if v, ok := intSetting(m, "max_queue"); ok {
		cfg.MaxQueue = v
	}
	if v, ok := intSetting(m, "queue_timeout_ms"); ok {
		cfg.QueueTimeoutMS = v
	}
	if v, ok := intSetting(m, "request_timeout_ms"); ok {
		cfg.RequestTimeoutMS = v
	}
	return cfg
}

func openAICompatConfFromStruct(s *structpb.Struct) config.OpenAICompatConf {
	cfg := config.OpenAICompatConf{Enabled: true}
	if s == nil {
		return cfg
	}
	m := s.AsMap()
	if v, ok := m["provider"].(string); ok {
		cfg.Provider = v
	}
	if v, ok := m["endpoint"].(string); ok {
		cfg.Endpoint = v
	}
	if headers, ok := m["headers"].(map[string]any); ok {
		cfg.Headers = make(map[string]string, len(headers))
		for k, hv := range headers {
			if s, ok := hv.(string); ok {
				cfg.Headers[k] = s
			}
		}
	}
	if v, ok := intSetting(m, "capacity"); ok {
		cfg.Capacity = v
	}
	if v, ok := intSetting(m, "max_queue"); ok {
		cfg.MaxQueue = v
	}
	if v, ok := intSetting(m, "queue_timeout_ms"); ok {
		cfg.QueueTimeoutMS = v
	}
	if v, ok := intSetting(m, "request_timeout_ms"); ok {
		cfg.RequestTimeoutMS = v
	}
	return cfg
}
