package llm

import (
	"context"
	"fmt"
	"sync"

	"github.com/google/uuid"
)

// Endpoint 是 Registry 从外部 loader 拿到的端点描述（已解密 api_key）。
type Endpoint struct {
	ID       uuid.UUID
	Provider string
	BaseURL  string
	APIKey   string
}

// EndpointLoader 由 chat repository / endpointService 实现，按 id 拉取端点
// 并完成 AES-GCM 解密。Registry 只看到明文 Endpoint。
type EndpointLoader interface {
	Load(ctx context.Context, id uuid.UUID) (Endpoint, error)
}

// Registry 缓存 endpoint_id → LLMClient。endpoint mutation 后由 service 层调
// Invalidate 强制下次 GetClient 重新构造（带新 base_url / api_key）。
type Registry struct {
	loader EndpointLoader
	mu     sync.Mutex
	cache  map[uuid.UUID]LLMClient
}

// NewRegistry 构造一个空缓存的 Registry。
func NewRegistry(loader EndpointLoader) *Registry {
	return &Registry{loader: loader, cache: map[uuid.UUID]LLMClient{}}
}

// GetClient 返回该 endpoint 对应的 LLMClient。命中缓存直接返回，否则 Load + buildClient
// 后写入缓存。多 goroutine 并发触发 miss 时，仅一个写入生效（double-checked under lock）。
func (r *Registry) GetClient(ctx context.Context, id uuid.UUID) (LLMClient, error) {
	r.mu.Lock()
	if c, ok := r.cache[id]; ok {
		r.mu.Unlock()
		return c, nil
	}
	r.mu.Unlock()

	ep, err := r.loader.Load(ctx, id)
	if err != nil {
		return nil, err
	}
	c, err := buildClient(ep)
	if err != nil {
		return nil, err
	}

	r.mu.Lock()
	defer r.mu.Unlock()
	if existing, ok := r.cache[id]; ok {
		return existing, nil
	}
	r.cache[id] = c
	return c, nil
}

// GetEmbedder 返回该 endpoint 对应的 Embedder（OpenAI / Gemini 实现；Anthropic
// 客户端虽实现接口但 Embed 返回 ErrEmbeddingsUnsupported）。底层复用 GetClient
// 的缓存与构造，再做类型断言。endpoint 的 client 未实现 Embedder 时返回错误。
func (r *Registry) GetEmbedder(ctx context.Context, id uuid.UUID) (Embedder, error) {
	c, err := r.GetClient(ctx, id)
	if err != nil {
		return nil, err
	}
	emb, ok := c.(Embedder)
	if !ok {
		return nil, fmt.Errorf("llm registry: endpoint %s provider %q does not support embeddings", id, c.Provider())
	}
	return emb, nil
}

// Invalidate 删除该 endpoint 的缓存条目。endpoint CRUD 后由 service 层调用。
func (r *Registry) Invalidate(id uuid.UUID) {
	r.mu.Lock()
	defer r.mu.Unlock()
	delete(r.cache, id)
}

func buildClient(ep Endpoint) (LLMClient, error) {
	switch ep.Provider {
	case "anthropic":
		return NewAnthropic(ep.APIKey, ep.BaseURL)
	case "openai":
		return NewOpenAI(ep.APIKey, ep.BaseURL)
	case "gemini":
		return NewGemini(ep.APIKey, ep.BaseURL)
	default:
		return nil, fmt.Errorf("llm registry: unknown provider %q", ep.Provider)
	}
}
