package conversation

import (
	"context"
	"errors"
	"testing"
)

// RepositoryContract her Repository uygulamasının sağlaması gereken davranışları sınar.
// Bellek içi depo ve MongoDB deposu bu AYNI testten geçer; yeni bir depo yazılacaksa
// sözleşmeyi değiştirme, uygulamayı değiştir.
//
// Bu dosya üretim kodudur (contract.go): sözleşme farklı paketlerden
// (örn. internal/storage/mongostore) de çağrılabilmelidir; _test.go içinde olsaydı
// dış paketler erişemezdi.
func RepositoryContract(t *testing.T, newRepo func() Repository) {
	ctx := context.Background()

	t.Run("create_get_notfound", func(t *testing.T) {
		r := newRepo()
		if _, err := r.GetConversation(ctx, "yok"); !errors.Is(err, ErrNotFound) {
			t.Fatalf("ErrNotFound bekleniyordu: %v", err)
		}
		if err := r.CreateConversation(ctx, &Conversation{ID: "c1", UserID: "u", Title: "t"}); err != nil {
			t.Fatal(err)
		}
		c, err := r.GetConversation(ctx, "c1")
		if err != nil || c.Title != "t" {
			t.Fatalf("c=%+v err=%v", c, err)
		}
	})

	t.Run("messages_order_and_limit", func(t *testing.T) {
		r := newRepo()
		_ = r.CreateConversation(ctx, &Conversation{ID: "c1", UserID: "u"})
		for _, s := range []string{"1", "2", "3", "4"} {
			if err := r.AppendMessage(ctx, &Message{ID: "m" + s, ConversationID: "c1", Content: s}); err != nil {
				t.Fatal(err)
			}
		}
		all, _ := r.Messages(ctx, "c1", 0)
		if len(all) != 4 || all[0].Content != "1" || all[3].Content != "4" {
			t.Fatalf("sıra hatalı: %+v", all)
		}
		last, _ := r.Messages(ctx, "c1", 2)
		if len(last) != 2 || last[0].Content != "3" || last[1].Content != "4" {
			t.Fatalf("son 2 mesaj eskiden yeniye bekleniyordu: %+v", last)
		}
	})

	t.Run("append_to_missing_conversation", func(t *testing.T) {
		r := newRepo()
		if err := r.AppendMessage(ctx, &Message{ID: "m", ConversationID: "yok"}); !errors.Is(err, ErrNotFound) {
			t.Fatalf("ErrNotFound bekleniyordu: %v", err)
		}
	})

	t.Run("list_sorted_by_update_and_paged", func(t *testing.T) {
		r := newRepo()
		for _, id := range []string{"a", "b", "c"} {
			_ = r.CreateConversation(ctx, &Conversation{ID: id, UserID: "u"})
		}
		_ = r.CreateConversation(ctx, &Conversation{ID: "x", UserID: "baska"})
		_ = r.AppendMessage(ctx, &Message{ID: "m1", ConversationID: "a"}) // a en son güncellenen
		list, _ := r.ListConversations(ctx, "u", 0, 0)
		if len(list) != 3 || list[0].ID != "a" {
			t.Fatalf("a başta olmalı, 3 kayıt: %+v", list)
		}
		page, _ := r.ListConversations(ctx, "u", 1, 1)
		if len(page) != 1 {
			t.Fatalf("sayfalama: %+v", page)
		}
		empty, err := r.ListConversations(ctx, "u", 10, 99)
		if err != nil || len(empty) != 0 {
			t.Fatalf("taşan offset boş dönmeli: %+v %v", empty, err)
		}
	})

	t.Run("tool_confirm_persist_and_resolve_once", func(t *testing.T) {
		r := newRepo()
		_ = r.CreateConversation(ctx, &Conversation{ID: "c1", UserID: "u"})
		err := r.AppendMessage(ctx, &Message{ID: "m1", ConversationID: "c1", Role: RoleAssistant, ToolConfirms: []ToolConfirm{
			{ConfirmID: "cfm_a", Title: "A", State: ConfirmPending, Fields: []ToolConfirmField{{Key: "k", Label: "K"}}},
			{ConfirmID: "cfm_b", Title: "B", State: ConfirmPending},
		}})
		if err != nil {
			t.Fatal(err)
		}
		got, _ := r.Messages(ctx, "c1", 0)
		if len(got[0].ToolConfirms) != 2 || got[0].ToolConfirms[0].Fields[0].Key != "k" {
			t.Fatalf("kartlar kaydedilmedi: %+v", got[0].ToolConfirms)
		}

		// Yalnızca hedef kart kapanır; diğeri bekler.
		if err := r.ResolveToolConfirm(ctx, "c1", "cfm_a", ToolConfirmResolution{
			State: ConfirmAnswered, Summary: "tamam", Values: map[string]string{"k": "v"},
		}); err != nil {
			t.Fatal(err)
		}
		got, _ = r.Messages(ctx, "c1", 0)
		a, b := got[0].ToolConfirms[0], got[0].ToolConfirms[1]
		if a.State != ConfirmAnswered || a.Summary != "tamam" || a.Values["k"] != "v" || a.ResolvedAt == nil {
			t.Fatalf("a kapanmadı: %+v", a)
		}
		if b.State != ConfirmPending {
			t.Fatalf("b etkilenmemeliydi: %+v", b)
		}

		// İkinci çözüm ilkini EZMEZ (çift tıklama / ikinci sekme).
		if err := r.ResolveToolConfirm(ctx, "c1", "cfm_a", ToolConfirmResolution{State: ConfirmExpired}); !errors.Is(err, ErrNotFound) {
			t.Fatalf("kapanmış kart ErrNotFound vermeli: %v", err)
		}
		got, _ = r.Messages(ctx, "c1", 0)
		if got[0].ToolConfirms[0].State != ConfirmAnswered {
			t.Fatal("kapanmış kartın durumu ezildi")
		}
		if err := r.ResolveToolConfirm(ctx, "c1", "yok", ToolConfirmResolution{State: ConfirmExpired}); !errors.Is(err, ErrNotFound) {
			t.Fatalf("bilinmeyen kart ErrNotFound vermeli: %v", err)
		}
		if err := r.ResolveToolConfirm(ctx, "yok", "cfm_b", ToolConfirmResolution{State: ConfirmExpired}); !errors.Is(err, ErrNotFound) {
			t.Fatalf("başka konuşmadaki kart çözülmemeli: %v", err)
		}
	})

	t.Run("delete_removes_messages", func(t *testing.T) {
		r := newRepo()
		_ = r.CreateConversation(ctx, &Conversation{ID: "c1", UserID: "u"})
		_ = r.AppendMessage(ctx, &Message{ID: "m", ConversationID: "c1"})
		if err := r.DeleteConversation(ctx, "c1"); err != nil {
			t.Fatal(err)
		}
		if _, err := r.Messages(ctx, "c1", 0); !errors.Is(err, ErrNotFound) {
			t.Fatalf("silinen konuşma ErrNotFound vermeli: %v", err)
		}
		if err := r.DeleteConversation(ctx, "c1"); !errors.Is(err, ErrNotFound) {
			t.Fatalf("ikinci silme ErrNotFound vermeli: %v", err)
		}
	})
}
