package ollama

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"sync/atomic"
	"testing"
	"time"

	"fiko-ai/internal/llm"
)

func TestEmbed_Success(t *testing.T) {
	var gotPath, gotModel, gotInput string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotPath = r.URL.Path
		var body struct {
			Model string `json:"model"`
			Input string `json:"input"`
		}
		_ = json.NewDecoder(r.Body).Decode(&body)
		gotModel, gotInput = body.Model, body.Input
		_, _ = w.Write([]byte(`{"model":"nomic-embed-text","embeddings":[[0.25,0.5,-1]]}`))
	}))
	defer srv.Close()

	c := New(srv.URL, "chat-model", time.Second).WithEmbedModel("  nomic-embed-text ")
	vec, err := c.Embed(context.Background(), "30 mm geriye plane koy")
	if err != nil {
		t.Fatal(err)
	}
	if gotPath != "/api/embed" || gotModel != "nomic-embed-text" || gotInput != "30 mm geriye plane koy" {
		t.Fatalf("istek yanlış: path=%q model=%q input=%q", gotPath, gotModel, gotInput)
	}
	if len(vec) != 3 || vec[0] != 0.25 || vec[2] != -1 {
		t.Fatalf("vektör yanlış: %v", vec)
	}
	if c.EmbedModel() != "nomic-embed-text" {
		t.Fatalf("model kırpılmalı: %q", c.EmbedModel())
	}
}

func TestEmbed_ValidationDoesNotHitNetwork(t *testing.T) {
	var hits int32
	srv := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { atomic.AddInt32(&hits, 1) }))
	defer srv.Close()

	if _, err := New(srv.URL, "m", time.Second).Embed(context.Background(), "x"); !errors.Is(err, llm.ErrNoEmbedModel) {
		t.Fatalf("model yokken ErrNoEmbedModel beklenirdi: %v", err)
	}
	c := New(srv.URL, "m", time.Second).WithEmbedModel("e")
	if _, err := c.Embed(context.Background(), "  \n "); !errors.Is(err, llm.ErrEmptyText) {
		t.Fatalf("boş metinde ErrEmptyText beklenirdi: %v", err)
	}
	if atomic.LoadInt32(&hits) != 0 {
		t.Fatal("doğrulama hatalarında ağ çağrısı yapılmamalı")
	}
}

func TestEmbed_ProviderErrors(t *testing.T) {
	cases := []struct {
		name       string
		status     int
		body       string
		wantStatus int
	}{
		{"HTTP hatası", http.StatusNotFound, `{"error":"model \"yok\" not found"}`, 404},
		{"200 içinde error alanı", http.StatusOK, `{"error":"bellek yetersiz"}`, 0},
		{"boş embeddings", http.StatusOK, `{"embeddings":[]}`, 0},
		{"boş vektör", http.StatusOK, `{"embeddings":[[]]}`, 0},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
				w.WriteHeader(tc.status)
				_, _ = w.Write([]byte(tc.body))
			}))
			defer srv.Close()

			_, err := New(srv.URL, "m", time.Second).WithEmbedModel("e").Embed(context.Background(), "merhaba")
			var pe *llm.ProviderError
			if !errors.As(err, &pe) || pe.StatusCode != tc.wantStatus {
				t.Fatalf("ProviderError(status=%d) beklenirdi: %v", tc.wantStatus, err)
			}
		})
	}
}

func TestEmbed_CancelReturnsContextError(t *testing.T) {
	release := make(chan struct{})
	srv := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { <-release }))
	defer srv.Close()
	defer close(release)

	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
	defer cancel()
	_, err := New(srv.URL, "m", 0).WithEmbedModel("e").Embed(ctx, "merhaba")
	if !errors.Is(err, context.DeadlineExceeded) {
		t.Fatalf("DeadlineExceeded beklenirdi: %v", err)
	}
}
