package claim

import (
	"context"
	"fmt"
	"strings"
	"testing"

	"fiko-ai/internal/llm"
)

// lineCount isteğin içindeki "Firma N" satırlarını sayar.
func firmaLines(req llm.Request) []string {
	var out []string
	for _, l := range strings.Split(req.Messages[len(req.Messages)-1].Content, "\n") {
		if strings.HasPrefix(l, "Firma ") {
			out = append(out, l)
		}
	}
	return out
}

func firmaJSON(lines []string) string {
	var items []string
	for _, l := range lines {
		items = append(items, fmt.Sprintf(`{"text":%q,"kind":"firma","key":%q}`, l, l))
	}
	return `{"remember":[` + strings.Join(items, ",") + `]}`
}

// Model çıktısı jeton sınırında KESİLİRSE parça sessizce 0 aday vermez: ikiye bölünüp yeniden denenir
// ve bütün firmalar kurtarılır. (Gerçek bulgu: 140 firmanın yalnızca 30'u kaydoluyordu.)
func TestExtract_KesilenParcaBolunupYenidenDenenir(t *testing.T) {
	var msg []string
	for i := 1; i <= 20; i++ {
		msg = append(msg, fmt.Sprintf("Firma %02d", i))
	}
	f := &fakeLLM{fn: func(_ int, req llm.Request) (llm.Result, error) {
		lines := firmaLines(req)
		if len(lines) > 6 { // büyük istekte çıktı kesilir
			full := firmaJSON(lines)
			return llm.Result{Content: full[:len(full)/2], DoneReason: "length"}, nil
		}
		return llm.Result{Content: firmaJSON(lines), DoneReason: "stop"}, nil
	}}
	ex, err := NewExtractor(f, nil).Extract(context.Background(), "m", llm.Options{}, strings.Join(msg, "\n"))
	if err != nil {
		t.Fatal(err)
	}
	if len(ex.Candidates) != 20 || ex.FailedChunks != 0 {
		t.Fatalf("20 aday ve 0 başarısız parça beklenirdi: aday=%d başarısız=%d", len(ex.Candidates), ex.FailedChunks)
	}
}

// Bölünemeyen tek satır hâlâ kesiliyorsa: kurtarılabilen tam kayıtlar alınır ama parça EKSİK sayılır.
func TestExtract_BolunemeyenKesikParcaEksikSayilir(t *testing.T) {
	f := &fakeLLM{fn: func(_ int, req llm.Request) (llm.Result, error) {
		full := `{"remember":[{"text":"Firma 1 müşteri","kind":"firma","key":"Firma 1"},{"text":"Firma 2 müşt`
		return llm.Result{Content: full, DoneReason: "length"}, nil
	}}
	ex, err := NewExtractor(f, nil).Extract(context.Background(), "m", llm.Options{}, "Firma 1 ve Firma 2")
	if err != nil {
		t.Fatal(err)
	}
	if len(ex.Candidates) != 1 || ex.FailedChunks != 1 {
		t.Fatalf("1 kurtarılmış aday ve 1 eksik parça beklenirdi: aday=%d başarısız=%d", len(ex.Candidates), ex.FailedChunks)
	}
}

// Düz sohbet cevabı (JSON yok) "kayıt yok"tur; eksik parça sayılmaz.
func TestExtract_DuzMetinCevabiEksikSayilmaz(t *testing.T) {
	f := &fakeLLM{fn: func(int, llm.Request) (llm.Result, error) {
		return llm.Result{Content: "Merhaba! Kaydedilecek bir şey yok.", DoneReason: "stop"}, nil
	}}
	ex, err := NewExtractor(f, nil).Extract(context.Background(), "m", llm.Options{}, "Merhaba")
	if err != nil || len(ex.Candidates) != 0 || ex.FailedChunks != 0 {
		t.Fatalf("beklenen: boş ve hatasız; err=%v %+v", err, ex)
	}
}

func TestSalvageCandidates(t *testing.T) {
	raw := "```json\n{\"remember\":[{\"text\":\"a\",\"kind\":\"firma\",\"key\":\"A\"},{\"text\":\"b\",\"kind\":\"firma\",\"key\":\"B\"},{\"text\":\"c"
	got := salvageCandidates(raw)
	if len(got) != 2 || got[1].Key != "B" {
		t.Fatalf("2 tam nesne kurtarılmalı: %+v", got)
	}
}
