package services

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
)

var umindEmbeddingsHTTPClient = &http.Client{Timeout: 30 * time.Second}

// GenerarEmbeddings pide los vectores de una tanda de textos en una sola
// llamada (más barato y rápido que uno por uno durante la ingesta). Usa el
// endpoint de embeddings compatible con OpenAI — Anthropic no ofrece
// embeddings, por eso esta config debe ser una con provider "openai" (u otro
// compatible con ese formato de respuesta).
func GenerarEmbeddings(ai *models.AiConfig, textos []string) ([][]float32, error) {
	if ai == nil {
		return nil, fmt.Errorf("no hay una configuración de IA para generar embeddings")
	}
	if len(textos) == 0 {
		return nil, nil
	}

	baseURL := ai.BaseURL
	if baseURL == "" {
		baseURL = ProviderDefaultURL(ai.Provider)
	}
	baseURL = strings.TrimRight(baseURL, "/")

	model := ai.ModelName
	if model == "" {
		model = "text-embedding-3-small"
	}

	reqBody, _ := json.Marshal(map[string]interface{}{
		"model": model,
		"input": textos,
	})
	req, err := http.NewRequest("POST", baseURL+"/embeddings", bytes.NewReader(reqBody))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+ai.ClaveEnClaro())
	req.Header.Set("Content-Type", "application/json")

	resp, err := umindEmbeddingsHTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("no se pudo conectar con el servicio de embeddings: %w", err)
	}
	defer resp.Body.Close()
	raw, _ := io.ReadAll(io.LimitReader(resp.Body, 4*1024*1024))

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		detalle := strings.TrimSpace(string(raw))
		if len(detalle) > 300 {
			detalle = detalle[:300]
		}
		return nil, fmt.Errorf("el servicio de embeddings respondió %d: %s", resp.StatusCode, detalle)
	}

	var out struct {
		Data []struct {
			Embedding []float32 `json:"embedding"`
			Index     int       `json:"index"`
		} `json:"data"`
	}
	if err := json.Unmarshal(raw, &out); err != nil {
		return nil, fmt.Errorf("respuesta inesperada del servicio de embeddings")
	}
	if len(out.Data) != len(textos) {
		return nil, fmt.Errorf("el servicio de embeddings devolvió %d vectores para %d textos", len(out.Data), len(textos))
	}

	vectores := make([][]float32, len(textos))
	for _, d := range out.Data {
		if d.Index < 0 || d.Index >= len(vectores) {
			continue
		}
		vectores[d.Index] = d.Embedding
	}
	return vectores, nil
}

// GenerarEmbedding es el atajo para un solo texto (ej: la consulta del usuario en RAG).
func GenerarEmbedding(ai *models.AiConfig, texto string) ([]float32, error) {
	vectores, err := GenerarEmbeddings(ai, []string{texto})
	if err != nil {
		return nil, err
	}
	if len(vectores) == 0 {
		return nil, fmt.Errorf("no se generó ningún embedding")
	}
	return vectores[0], nil
}
