package server

import (
	"context"
	"encoding/json"
	"net/http"
	"strings"
	"time"

	"github.com/uptrace/bunrouter"

	"github.com/Warky-Devs/vecna.git/pkg/adapter"
	"github.com/Warky-Devs/vecna.git/pkg/embedclient"
)

// googleDispatch parses the wildcard "model:action" segment and routes to the
// correct Google handler. The colon is a literal method separator in the
// Google embedding API, not a bunrouter parameter prefix.
func (h *handler) googleDispatch(w http.ResponseWriter, req bunrouter.Request) error {
	return h.googleDispatchWithAdapter(w, req, h.adapter, "")
}

func (h *handler) googleDispatchMapped(w http.ResponseWriter, req bunrouter.Request) error {
	em, err := h.resolveExtraMap(req.Param("mapping"))
	if err != nil {
		return writeError(w, http.StatusNotFound, err.Error())
	}
	return h.googleDispatchWithAdapter(w, req, em.Adapter, em.ForwardTarget)
}

func (h *handler) googleDispatchWithAdapter(w http.ResponseWriter, req bunrouter.Request, adp adapter.Adapter, targetOverride string) error {
	modelaction := req.Param("modelaction") // e.g. "text-embedding-foo:embedContent"
	idx := strings.LastIndex(modelaction, ":")
	if idx < 0 {
		return writeError(w, http.StatusNotFound, "invalid Google API path")
	}
	model := modelaction[:idx]
	action := modelaction[idx+1:]

	ctx := context.WithValue(req.Context(), modelKey, model)
	req = req.WithContext(ctx)

	switch action {
	case "embedContent":
		return h.googleEmbedContentWithAdapter(w, req, adp, targetOverride)
	case "batchEmbedContents":
		return h.googleBatchEmbedContentsWithAdapter(w, req, adp, targetOverride)
	default:
		return writeError(w, http.StatusNotFound, "unknown Google API method: "+action)
	}
}

// --- single embedContent ---

type googleEmbedContentRequest struct {
	Content  googleContent `json:"content"`
	TaskType string        `json:"taskType,omitempty"`
}

type googleContent struct {
	Parts []googlePart `json:"parts"`
}

type googlePart struct {
	Text string `json:"text"`
}

type googleEmbedContentResponse struct {
	Embedding googleEmbeddingValues `json:"embedding"`
}

type googleEmbeddingValues struct {
	Values []float32 `json:"values"`
}

func (h *handler) googleEmbedContentWithAdapter(w http.ResponseWriter, req bunrouter.Request, adp adapter.Adapter, targetOverride string) error {
	model, _ := req.Context().Value(modelKey).(string)

	var body googleEmbedContentRequest
	if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
		return writeError(w, http.StatusBadRequest, "invalid request body")
	}

	texts := make([]string, len(body.Content.Parts))
	for i, p := range body.Content.Parts {
		texts[i] = p.Text
	}

	client, targetName, targetURL := h.resolveClientOverride(targetOverride, model)
	trace := TraceFromContext(req.Context())
	trace.ForwardTarget = targetName
	trace.ForwardURL = targetURL

	t0 := time.Now()
	embedResp, err := client.Embed(req.Context(), embedclient.Request{Texts: texts, Model: model})
	trace.ForwardDuration = time.Since(t0)
	if err != nil {
		return writeError(w, http.StatusBadGateway, err.Error())
	}
	trace.ForwardModel = embedResp.Model
	trace.PromptTokens = embedResp.Usage.PromptTokens
	trace.TotalTokens = embedResp.Usage.TotalTokens

	t1 := time.Now()
	var adapted []float32
	if len(embedResp.Embeddings) > 0 {
		adapted, err = adp.Adapt(embedResp.Embeddings[0])
		if err != nil {
			return writeError(w, http.StatusInternalServerError, err.Error())
		}
	}
	trace.TranslateDuration = time.Since(t1)

	writeTraceHeaders(w, trace)

	return writeJSON(w, http.StatusOK, googleEmbedContentResponse{
		Embedding: googleEmbeddingValues{Values: adapted},
	})
}

// --- batch batchEmbedContents ---

type googleBatchRequest struct {
	Requests []googleEmbedContentRequest `json:"requests"`
}

type googleBatchResponse struct {
	Embeddings []googleEmbeddingValues `json:"embeddings"`
}

func (h *handler) googleBatchEmbedContentsWithAdapter(w http.ResponseWriter, req bunrouter.Request, adp adapter.Adapter, targetOverride string) error {
	model, _ := req.Context().Value(modelKey).(string)

	var body googleBatchRequest
	if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
		return writeError(w, http.StatusBadRequest, "invalid request body")
	}

	var texts []string
	for _, r := range body.Requests {
		for _, p := range r.Content.Parts {
			texts = append(texts, p.Text)
		}
	}

	client, targetName, targetURL := h.resolveClientOverride(targetOverride, model)
	trace := TraceFromContext(req.Context())
	trace.ForwardTarget = targetName
	trace.ForwardURL = targetURL

	t0 := time.Now()
	embedResp, err := client.Embed(req.Context(), embedclient.Request{Texts: texts, Model: model})
	trace.ForwardDuration = time.Since(t0)
	if err != nil {
		return writeError(w, http.StatusBadGateway, err.Error())
	}
	trace.ForwardModel = embedResp.Model
	trace.PromptTokens = embedResp.Usage.PromptTokens
	trace.TotalTokens = embedResp.Usage.TotalTokens

	t1 := time.Now()
	result := make([]googleEmbeddingValues, len(embedResp.Embeddings))
	for i, vec := range embedResp.Embeddings {
		adapted, adaptErr := adp.Adapt(vec)
		if adaptErr != nil {
			return writeError(w, http.StatusInternalServerError, adaptErr.Error())
		}
		result[i] = googleEmbeddingValues{Values: adapted}
	}
	trace.TranslateDuration = time.Since(t1)

	writeTraceHeaders(w, trace)

	return writeJSON(w, http.StatusOK, googleBatchResponse{Embeddings: result})
}
