package rag

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

// embedClient 是 OpenAI 兼容的 embeddings 客户端（provider 抽象）。
// 开发期指向第三方在线 API 或本地 mock；生产期换自部署/在线 embedding 模型。
type embedClient struct {
	baseURL string
	apiKey  string
	model   string
	hc      *http.Client
}

func newEmbedClient(baseURL, apiKey, model string) *embedClient {
	return &embedClient{
		baseURL: baseURL,
		apiKey:  apiKey,
		model:   model,
		hc:      &http.Client{Timeout: 30 * time.Second},
	}
}

func (e *embedClient) ready() bool { return e != nil && e.baseURL != "" && e.model != "" }

// Embed 把若干文本向量化（OpenAI 兼容 /embeddings）。
func (e *embedClient) Embed(ctx context.Context, texts []string) ([][]float32, error) {
	if !e.ready() {
		return nil, fmt.Errorf("embedding not configured")
	}
	body, _ := json.Marshal(map[string]any{"model": e.model, "input": texts})
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, e.baseURL+"/embeddings", bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	if e.apiKey != "" {
		req.Header.Set("Authorization", "Bearer "+e.apiKey)
	}
	resp, err := e.hc.Do(req)
	if err != nil {
		return nil, fmt.Errorf("embed request: %w", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		buf := new(bytes.Buffer)
		_, _ = buf.ReadFrom(resp.Body)
		return nil, fmt.Errorf("embed http %d: %s", resp.StatusCode, buf.String())
	}
	var out struct {
		Data []struct {
			Embedding []float32 `json:"embedding"`
			Index     int       `json:"index"`
		} `json:"data"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
		return nil, fmt.Errorf("embed decode: %w", err)
	}
	vecs := make([][]float32, len(out.Data))
	for _, d := range out.Data {
		if d.Index >= 0 && d.Index < len(vecs) {
			vecs[d.Index] = d.Embedding
		}
	}
	return vecs, nil
}
