package persona

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

func fixedNow() time.Time { return time.Date(2026, 3, 1, 10, 0, 0, 0, time.UTC) }

func newSvc(t *testing.T) *Service {
	t.Helper()
	svc, err := NewService(NewMemoryRepository(), nil)
	if err != nil {
		t.Fatalf("servis kurulamadı: %v", err)
	}
	svc.nowFn(fixedNow)
	return svc
}

func TestNew_AlanDogrulama(t *testing.T) {
	// Ad ve talimat zorunludur; ikisi de eksikse İKİ hata birden döner
	// (arayüz tek turda hepsini görebilsin diye).
	_, err := New(NewInput{Now: fixedNow()})
	if err == nil {
		t.Fatal("ad ve talimat yoksa hata dönmeliydi")
	}
	if got := len(FieldErrors(err)); got != 2 {
		t.Fatalf("2 alan hatası beklenir, %d geldi: %v", got, FieldErrors(err))
	}
	if !IsValidation(err) {
		t.Fatalf("doğrulama hatası olmalı, %v", err)
	}
}

func TestNew_UzunlukSinirlari(t *testing.T) {
	tooLong := make([]rune, MaxInstructions+1)
	for i := range tooLong {
		tooLong[i] = 'a'
	}
	_, err := New(NewInput{Name: "P", Instructions: string(tooLong), Now: fixedNow()})
	if !IsValidation(err) {
		t.Fatalf("çok uzun talimat reddedilmeliydi, %v", err)
	}
	// Sınırın tam kendisi kabul edilir (üst sınır dahil).
	if _, err := New(NewInput{Name: "P", Instructions: string(tooLong[:MaxInstructions]), Now: fixedNow()}); err != nil {
		t.Fatalf("sınırdaki metin kabul edilmeliydi: %v", err)
	}
}

func TestNew_TekrarliAtamalarTemizlenir(t *testing.T) {
	p, err := New(NewInput{
		Name:            "Proje Yöneticisi",
		Role:            "Bu kişi proje yöneticisidir.",
		Instructions:    "Proje eklerken eksiksiz bilgi al.",
		AssignedUserIDs: []string{"u1", " u1 ", "u2", ""},
		Now:             fixedNow(),
	})
	if err != nil {
		t.Fatalf("oluşturulamadı: %v", err)
	}
	// "u1" iki kez ve boş kimlik geldi; tekilleştirilmeli.
	if len(p.AssignedUserIDs) != 2 {
		t.Fatalf("2 atama beklenir, %d: %v", len(p.AssignedUserIDs), p.AssignedUserIDs)
	}
	if !p.HasUser("u1") || !p.HasUser("u2") || p.HasUser("u3") {
		t.Fatalf("atama sorgusu bozuk: %v", p.AssignedUserIDs)
	}
}

func TestService_ActiveYalnizcaAtananlariDoner(t *testing.T) {
	svc := newSvc(t)
	ctx := context.Background()

	p1, err := svc.Create(ctx, NewInput{
		Name: "Proje Yöneticisi", Instructions: "Eksiksiz bilgi al.",
		AssignedUserIDs: []string{"mert"}, Now: fixedNow(),
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := svc.Create(ctx, NewInput{
		Name: "Muhasebe", Instructions: "Raporları netleştir.", AssignedUserIDs: []string{"ayse"}, Now: fixedNow(),
	}); err != nil {
		t.Fatal(err)
	}

	got := svc.Active(ctx, "mert")
	if len(got) != 1 || got[0].ID != p1.ID {
		t.Fatalf("Mert'e yalnız kendi profili dönmeliydi, %v", got)
	}
	if len(svc.Active(ctx, "ayse")) != 1 {
		t.Fatal("Ayşe kendi profilini görmeli")
	}
	if len(svc.Active(ctx, "baska")) != 0 {
		t.Fatal("profili olmayan kullanıcıya profil dönmemeli")
	}
	if svc.Active(ctx, "") != nil {
		t.Fatal("boş kullanıcı kimliğine profil dönmemeli")
	}
}

func TestService_UpdateAtamalariDegistirir(t *testing.T) {
	svc := newSvc(t)
	ctx := context.Background()
	p, err := svc.Create(ctx, NewInput{
		Name: "Profil", Instructions: "Kural.", AssignedUserIDs: []string{"mert"}, Now: fixedNow(),
	})
	if err != nil {
		t.Fatal(err)
	}
	if len(svc.Active(ctx, "mert")) != 1 {
		t.Fatal("önce Mert'e atanmış olmalı")
	}

	// Mert'ten al, Ayşe'ye ver.
	upd, err := svc.Update(ctx, p.ID, UpdateInput{
		Name: "Profil", Instructions: "Kural.", AssignedUserIDs: []string{"ayse"}, Now: fixedNow(),
	})
	if err != nil {
		t.Fatal(err)
	}
	if len(svc.Active(ctx, "mert")) != 0 {
		t.Fatal("atama kaldırıldıysa Mert artık profil görmemeli")
	}
	if len(svc.Active(ctx, "ayse")) != 1 {
		t.Fatal("yeni atama Ayşe'ye geçmeli")
	}
	if upd.ID != p.ID || !upd.CreatedAt.Equal(p.CreatedAt) {
		t.Fatal("kimlik ve oluşturma zamanı değişmemeli")
	}
}

func TestService_DeleteAtamayiKaldirir(t *testing.T) {
	svc := newSvc(t)
	ctx := context.Background()
	p, _ := svc.Create(ctx, NewInput{
		Name: "Silinecek", Instructions: "Kural.", AssignedUserIDs: []string{"mert"}, Now: fixedNow(),
	})
	if err := svc.Delete(ctx, p.ID); err != nil {
		t.Fatal(err)
	}
	if len(svc.Active(ctx, "mert")) != 0 {
		t.Fatal("silinen profil isteme girmemeli")
	}
	if err := svc.Delete(ctx, p.ID); !errors.Is(err, ErrNotFound) {
		t.Fatalf("ikinci silme ErrNotFound vermeli, %v", err)
	}
}

func TestService_DepoHatasiSohbetiDusurmez(t *testing.T) {
	// KURAL: profil bir zenginliktir; depo patlasa bile sohbet profil kuralları
	// olmadan DEVAM eder. Bu yüzden Active hata DÖNMEZ ve nil alıcıda da çalışır.
	//
	// main.go'da NewService hata dönerse personaSvc nil kalır ve ajana BAĞLANMAZ;
	// yine de savunma burada: çağıran yanlışlıkla nil servisi kullanırsa
	// panik yerine sessizce "profil yok" dönmelidir (sohbet akışı bozulmaz).
	broken, err := NewService(nil, nil)
	if err == nil {
		t.Fatal("depo yoksa NewService hata dönmeli")
	}
	if broken.Active(context.Background(), "mert") != nil {
		t.Fatal("nil servis profil dönmemeli (panik değil)")
	}
	svc := newSvc(t)
	if len(svc.Active(context.Background(), "mert")) != 0 {
		t.Fatal("atanmamış kullanıcıya profil dönmemeli")
	}
}

func TestService_GuncellemeDogrulamaHatasiDondurur(t *testing.T) {
	svc := newSvc(t)
	ctx := context.Background()
	p, _ := svc.Create(ctx, NewInput{Name: "P", Instructions: "Kural.", Now: fixedNow()})

	// Talimatı boşaltmak reddedilmeli (profilli profil olmaktan çıkarır).
	if _, err := svc.Update(ctx, p.ID, UpdateInput{Name: "P", Instructions: "  ", Now: fixedNow()}); !IsValidation(err) {
		t.Fatalf("boş talimat reddedilmeliydi, %v", err)
	}
	// Olmayan profil 404'e çevrilecek hata vermeli.
	if _, err := svc.Update(ctx, "yok", UpdateInput{Name: "P", Instructions: "K", Now: fixedNow()}); !errors.Is(err, ErrNotFound) {
		t.Fatalf("ErrNotFound beklenir, %v", err)
	}
}

func TestMemoryRepo_CloneDerinKopya(t *testing.T) {
	r := NewMemoryRepository()
	ctx := context.Background()
	p, _ := New(NewInput{Name: "P", Instructions: "K", AssignedUserIDs: []string{"u1"}, Now: fixedNow()})
	stored, err := r.Create(ctx, p)
	if err != nil {
		t.Fatal(err)
	}
	// Dönen kopyayı değiştir; depodaki kayıt ETKİLENMEMELİ.
	stored.Name = "Değişti"
	stored.AssignedUserIDs[0] = "u2"
	got, _ := r.Get(ctx, p.ID)
	if got.Name != "P" || got.AssignedUserIDs[0] != "u1" {
		t.Fatalf("depo kopyası dışarıdan etkilendi: %+v", got)
	}
}
