package embedclient

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

type openAIClient struct {
	baseURL    string
	apiKey     string
	httpClient *http.Client
}

// NewOpenAI returns a Client that speaks the OpenAI embeddings API.
func NewOpenAI(baseURL, apiKey string, httpClient *http.Client) Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	return &openAIClient{baseURL: baseURL, apiKey: apiKey, httpClient: httpClient}
}

type openAIEmbedRequest struct {
	Input []string `json:"input"`
	Model string   `json:"model"`
}

type openAIEmbedResponse struct {
	Object string `json:"object"`
	Data   []struct {
		Object    string    `json:"object"`
		Embedding []float32 `json:"embedding"`
		Index     int       `json:"index"`
	} `json:"data"`
	Model string `json:"model"`
	Usage struct {
		PromptTokens int `json:"prompt_tokens"`
		TotalTokens  int `json:"total_tokens"`
	} `json:"usage"`
}

func (c *openAIClient) Embed(ctx context.Context, req Request) (Response, error) {
	body, err := json.Marshal(openAIEmbedRequest{Input: req.Texts, Model: req.Model})
	if err != nil {
		return Response{}, fmt.Errorf("openai embed marshal: %w", err)
	}

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/v1/embeddings", bytes.NewReader(body))
	if err != nil {
		return Response{}, fmt.Errorf("openai embed request: %w", err)
	}
	httpReq.Header.Set("Content-Type", "application/json")
	if c.apiKey != "" {
		httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)
	}

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return Response{}, fmt.Errorf("openai embed do: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		return Response{}, fmt.Errorf("openai embed: unexpected status %d", resp.StatusCode)
	}

	var oaiResp openAIEmbedResponse
	if err := json.NewDecoder(resp.Body).Decode(&oaiResp); err != nil {
		return Response{}, fmt.Errorf("openai embed decode: %w", err)
	}

	embeddings := make([][]float32, len(oaiResp.Data))
	for _, d := range oaiResp.Data {
		embeddings[d.Index] = d.Embedding
	}

	return Response{
		Embeddings: embeddings,
		Model:      oaiResp.Model,
		Usage: Usage{
			PromptTokens: oaiResp.Usage.PromptTokens,
			TotalTokens:  oaiResp.Usage.TotalTokens,
		},
	}, nil
}
