package agent

import (
	"context"
	"testing"

	"fiko-ai/internal/conversation"
	"fiko-ai/internal/llm"
	"fiko-ai/internal/protocol"
)

// Onay kartının KALICI kaydı: form bir turda açılır, mesajla birlikte saklanır ve cevap
// kartı kapatır. Sayfa yenilenince / sunucu yeniden başlayınca kart kaybolmamalı.

func formOutput() map[string]any {
	return map[string]any{"confirm": map[string]any{
		"action": "delete", "title": "Kayit silinsin mi?", "destructive": true,
		"confirmLabel": "Sil", "cancelLabel": "Vazgec",
		"payload": map[string]any{"collection": "customers", "ref": "cus_1"},
		"fields":  []any{map[string]any{"key": "name", "label": "Ad", "value": "Aril"}},
	}}
}

// runFormTurn bir form açan tur çalıştırır ve (ajan, konuşma, mesajlar) döndürür.
func runFormTurn(t *testing.T, calls int) (*Agent, string, []conversation.Message) {
	t.Helper()
	specs := echoSpec()
	specs[0].Name = "biz.delete"
	turn := make([]llm.ToolCall, 0, calls)
	for i := 0; i < calls; i++ {
		turn = append(turn, llm.ToolCall{Name: "biz.delete", Arguments: map[string]any{"ref": string(rune('a' + i))}})
	}
	f := &toolLLM{turns: [][]llm.ToolCall{turn}, final: "Onay bekliyorum."}
	runner := &fakeRunner{out: formOutput()}
	a, conv, rec := setupTools(t, f, runner, fakeCatalog{specs}, Config{})

	sendText(a, rec, conv, "kaydi sil")

	msgs, err := a.svc.Messages(context.Background(), "u", conv, 50)
	if err != nil {
		t.Fatal(err)
	}
	return a, conv, msgs
}

func lastAssistant(t *testing.T, msgs []conversation.Message) conversation.Message {
	t.Helper()
	for i := len(msgs) - 1; i >= 0; i-- {
		if msgs[i].Role == conversation.RoleAssistant {
			return msgs[i]
		}
	}
	t.Fatal("asistan mesaji yok")
	return conversation.Message{}
}

func TestTurn_PersistsConfirmCard(t *testing.T) {
	_, _, msgs := runFormTurn(t, 1)
	m := lastAssistant(t, msgs)
	if len(m.ToolConfirms) != 1 {
		t.Fatalf("1 kalici kart bekleniyordu: %+v", m.ToolConfirms)
	}
	c := m.ToolConfirms[0]
	if c.State != conversation.ConfirmPending || c.ConfirmID == "" || c.Tool != "biz.delete" || !c.Destructive {
		t.Errorf("kart eksik/yanlis: %+v", c)
	}
	if len(c.Fields) != 1 || c.Fields[0].Key != "name" {
		t.Errorf("alanlar kaydedilmedi: %+v", c.Fields)
	}
	if len(m.ToolRuns) != 1 || m.ToolRuns[0].CallID != c.CallID {
		t.Errorf("kart, formu doguran tool kaydina baglanmali: runs=%+v card.callId=%q", m.ToolRuns, c.CallID)
	}
}

func TestTurn_CancelClosesPersistedCard(t *testing.T) {
	a, conv, msgs := runFormTurn(t, 1)
	id := lastAssistant(t, msgs).ToolConfirms[0].ConfirmID

	got := a.ResolveConfirm(context.Background(), conv, "u",
		protocol.ToolConfirmAnswerPayload{ConfirmID: id, Confirmed: false, Cancelled: true})
	if got.Confirmed || got.Expired {
		t.Fatalf("iptal: confirmed=false expired=false beklenirdi: %+v", got)
	}

	msgs, _ = a.svc.Messages(context.Background(), "u", conv, 50)
	c := lastAssistant(t, msgs).ToolConfirms[0]
	if c.State != conversation.ConfirmCancelled || c.ResolvedAt == nil {
		t.Errorf("kart cancelled olmali: %+v", c)
	}
}

func TestTurn_RestartedServerMarksCardExpired(t *testing.T) {
	a, conv, msgs := runFormTurn(t, 1)
	id := lastAssistant(t, msgs).ToolConfirms[0].ConfirmID

	// Sunucu yeniden basladi: bekleyen form bellekten gitti, kalici kart duruyor.
	a.confirm = NewPendingConfirm()
	got := a.ResolveConfirm(context.Background(), conv, "u",
		protocol.ToolConfirmAnswerPayload{ConfirmID: id, Confirmed: true})
	if !got.Expired || got.Confirmed {
		t.Fatalf("expired=true confirmed=false beklenirdi: %+v", got)
	}

	msgs, _ = a.svc.Messages(context.Background(), "u", conv, 50)
	if st := lastAssistant(t, msgs).ToolConfirms[0].State; st != conversation.ConfirmExpired {
		t.Errorf("kart expired olmali, gelen %q", st)
	}
}

func TestTurn_MultipleFormsMultipleCards(t *testing.T) {
	_, _, msgs := runFormTurn(t, 3)
	cards := lastAssistant(t, msgs).ToolConfirms
	if len(cards) != 3 {
		t.Fatalf("3 kart bekleniyordu: %d", len(cards))
	}
	seen := map[string]bool{}
	for _, c := range cards {
		if seen[c.ConfirmID] {
			t.Errorf("tekrarlanan confirmId: %s", c.ConfirmID)
		}
		seen[c.ConfirmID] = true
	}
}

// Erken cevap: kullanici formu, tur mesaji kaydetmeden once cevapladi. Sonuc notta bekler ve
// mesaj kaydedilince karta islenir.
func TestApplyEarlyResolutions_ClosesCardAfterSave(t *testing.T) {
	svc := conversation.NewService(conversation.NewMemoryRepo())
	c, err := svc.Create(context.Background(), "u")
	if err != nil {
		t.Fatal(err)
	}
	a := &Agent{svc: svc, confirm: NewPendingConfirm(), active: make(map[string]*run)}
	cards := []conversation.ToolConfirm{{ConfirmID: "cfm_e", Tool: "biz.delete", State: conversation.ConfirmPending}}

	a.persistResolution(c.ID, "u", "cfm_e", confirmResolution{summary: "Silindi.", confirmed: true}) // mesaj yok → not kalir
	if _, err := svc.AddAssistantMessage(context.Background(), c.ID, conversation.AssistantMessage{
		ID: "m1", Content: "x", ToolConfirms: cards,
	}); err != nil {
		t.Fatal(err)
	}
	a.applyEarlyResolutions(context.Background(), Input{UserID: "u", ConversationID: c.ID}, cards)

	msgs, _ := svc.Messages(context.Background(), "u", c.ID, 10)
	got := msgs[len(msgs)-1].ToolConfirms[0]
	if got.State != conversation.ConfirmAnswered || got.Summary != "Silindi." {
		t.Errorf("erken cevap karta islenmeli: %+v", got)
	}
	if _, ok := a.confirm.TakeResolved("cfm_e"); ok {
		t.Error("islenen erken not temizlenmeli")
	}
}
