package claim

import (
	"context"
	"fmt"
	"strings"
	"testing"
	"time"
)

// Faz 9 rapor testleri. "Bugün" sabittir: 2026-10-06 (Türkiye saati).
var repNow = time.Date(2026, 10, 6, 9, 0, 0, 0, trt)

func repTask(id, text string, f map[string]string, st Status) Claim {
	c := readClaim(id, KindGorev, text, st, nil)
	c.Key = "" // görev listelik tür değildir
	c.Fields = f
	return c
}

func repProject(id, ad string, f map[string]string) Claim {
	c := readClaim(id, "proje", ad, StatusActive, nil)
	c.Key = NormalizeKey(ad)
	f["ad"] = ad
	c.Fields = f
	return c
}

func repReader(t *testing.T, cs ...Claim) *Reader {
	t.Helper()
	rd := newReader(readRepo(t, cs...), &readLLM{}, nil)
	rd.now = func() time.Time { return repNow }
	return rd
}

func mustReport(t *testing.T, rd *Reader, req ReportRequest, actor Actor) *ReportResult {
	t.Helper()
	res, err := rd.Report(context.Background(), req, actor)
	if err != nil {
		t.Fatalf("Report(%+v): %v", req, err)
	}
	return res
}

func TestToday_TurkiyeTakvimi(t *testing.T) {
	// 22:30 UTC = ertesi gün 01:30 Türkiye: "bugün" Türkiye takvimine göre.
	if got := Today(time.Date(2026, 10, 5, 22, 30, 0, 0, time.UTC)); got != "2026-10-06" {
		t.Errorf("Today = %s, beklenen 2026-10-06", got)
	}
	if got := Today(time.Date(2026, 10, 6, 20, 59, 0, 0, time.UTC)); got != "2026-10-06" {
		t.Errorf("Today = %s, beklenen 2026-10-06", got)
	}
}

func TestOverdueReport(t *testing.T) {
	rd := repReader(t,
		repTask("t1", "B-12 işleme programı hazırlanacak", map[string]string{"proje": "B-12", "atanan": "Ahmet", "son_tarih": "2026-10-02", "durum": "acik"}, StatusActive),
		repTask("t2", "Kalite kontrol", map[string]string{"atanan": "Ayşe", "son_tarih": "2026-10-05", "durum": "devam"}, StatusActive),
		repTask("t3", "Bugün biten iş", map[string]string{"son_tarih": "2026-10-06", "durum": "acik"}, StatusActive),
		repTask("t4", "Tamamlanmış eski iş", map[string]string{"son_tarih": "2026-09-01", "durum": "tamamlandi"}, StatusActive),
		repTask("t5", "İptal eski iş", map[string]string{"son_tarih": "2026-09-01", "durum": "iptal"}, StatusActive),
		repTask("t6", "Tarihsiz iş", map[string]string{"durum": "acik"}, StatusActive),
		repTask("t7", "Geçersiz sayılan", map[string]string{"son_tarih": "2026-09-01", "durum": "acik"}, StatusInvalid),
		repTask("t8", "Güncellenen eski kayıt", map[string]string{"son_tarih": "2026-09-01", "durum": "acik"}, StatusUpdated),
	)
	res := mustReport(t, rd, ReportRequest{Type: ReportOverdue}, readUye)

	if len(res.Tasks) != 2 || res.Tasks[0].ID != "t1" || res.Tasks[1].ID != "t2" {
		t.Fatalf("gecikmiş görevler = %+v; beklenen t1 sonra t2 (en çok geciken önce)", res.Tasks)
	}
	if res.Tasks[0].DaysLate != 4 || res.Tasks[1].DaysLate != 1 {
		t.Errorf("gecikme günleri = %d, %d; beklenen 4, 1", res.Tasks[0].DaysLate, res.Tasks[1].DaysLate)
	}
	// t1, t2, t3 ve t6 açık; t6 tarihsiz.
	if res.OpenTasks != 4 || res.NoDue != 1 {
		t.Errorf("açık=%d tarihsiz=%d; beklenen 4 ve 1", res.OpenTasks, res.NoDue)
	}
	for _, want := range []string{"4 gün gecikti", "1 gün gecikti", "atanan: Ahmet", "proje: B-12", "Toplam: 2 gecikmiş görev", "açık görev: 4", "son tarihi olmayan: 1"} {
		if !strings.Contains(res.Text, want) {
			t.Errorf("rapor metninde %q yok:\n%s", want, res.Text)
		}
	}
	for _, not := range []string{"Bugün biten", "Tamamlanmış", "İptal", "Geçersiz sayılan", "Güncellenen"} {
		if strings.Contains(res.Text, not) {
			t.Errorf("rapor metninde olmamalı: %q\n%s", not, res.Text)
		}
	}
}

func TestOverdueReport_GecikmeYoksa(t *testing.T) {
	rd := repReader(t, repTask("t1", "Yarın biter", map[string]string{"son_tarih": "2026-10-07", "durum": "acik"}, StatusActive))
	res := mustReport(t, rd, ReportRequest{Type: ReportOverdue}, readUye)
	if len(res.Tasks) != 0 || !strings.Contains(res.Text, "Gecikmiş görev yok.") || !strings.Contains(res.Text, "açık görev: 1") {
		t.Errorf("gecikme yokken metin = %q", res.Text)
	}
}

func TestOverdueReport_CelisikiliIsaretler(t *testing.T) {
	rd := repReader(t, repTask("t1", "Sevkiyat hazırlığı", map[string]string{"son_tarih": "2026-10-01", "durum": "acik"}, StatusConflict))
	res := mustReport(t, rd, ReportRequest{Type: ReportOverdue}, readUye)
	if len(res.Tasks) != 1 || !res.Tasks[0].Conflict || !strings.Contains(res.Text, "[çelişkili]") {
		t.Errorf("çelişkili görev işaretlenmedi: %+v\n%s", res.Tasks, res.Text)
	}
}

func projectFixture() []Claim {
	return []Claim{
		repProject("p1", "B-12", map[string]string{"sorumlu": "Mehmet", "durum": "devam", "bitis": "2026-10-31", "oncelik": "yuksek", "baslangic": "2026-09-01"}),
		repProject("p2", "B-120", map[string]string{"sorumlu": "Zeynep", "durum": "devam"}),
		repTask("g1", "Kalıp çizimi", map[string]string{"proje": "B-12", "durum": "tamamlandi"}, StatusActive),
		repTask("g2", "Malzeme siparişi", map[string]string{"proje": "b12", "durum": "tamamlandi"}, StatusActive),
		repTask("g3", "İşleme programı", map[string]string{"proje": "B-12", "atanan": "Ahmet", "son_tarih": "2026-10-02", "durum": "devam"}, StatusActive),
		repTask("g4", "Montaj", map[string]string{"proje": "B-12", "son_tarih": "2026-10-20", "durum": "acik"}, StatusActive),
		repTask("g5", "Eski tasarım", map[string]string{"proje": "B-12", "durum": "iptal"}, StatusActive),
		repTask("g6", "Ahmet B-12 için kalite raporu yazsın", map[string]string{"son_tarih": "2026-10-03", "durum": "acik"}, StatusActive), // proje alanı boş, metinde geçiyor
		repTask("g7", "B-120 test düzeneği", map[string]string{"proje": "B-120", "son_tarih": "2026-10-01", "durum": "acik"}, StatusActive),
		repTask("g8", "Genel temizlik", map[string]string{"son_tarih": "2026-10-04", "durum": "acik"}, StatusActive), // projesiz
	}
}

func TestProjectReport(t *testing.T) {
	rd := repReader(t, projectFixture()...)
	res := mustReport(t, rd, ReportRequest{Type: ReportProject, Project: "b-12"}, readUye)
	if res.NotFound || len(res.Projects) != 1 {
		t.Fatalf("proje bulunamadı ya da çok sonuç: %+v", res)
	}
	p := res.Projects[0]
	if p.Name != "B-12" {
		t.Fatalf("proje = %q, beklenen B-12 (B-120 ile karışmamalı)", p.Name)
	}
	// B-12 görevleri: g1,g2 (tamam), g3,g6 (açık+gecikmiş), g4 (açık), g5 (iptal). B-120 ve projesiz GİRMEZ.
	if p.Total != 5 || p.Done != 2 || p.Open != 3 || p.Cancelled != 1 || p.Overdue != 2 {
		t.Errorf("sayılar total=%d done=%d open=%d iptal=%d gecikmis=%d; beklenen 5,2,3,1,2", p.Total, p.Done, p.Open, p.Cancelled, p.Overdue)
	}
	if p.Progress != 40 { // 2/5
		t.Errorf("ilerleme = %d, beklenen 40", p.Progress)
	}
	if !p.EndKnown || p.DaysToEnd != 25 || p.Late {
		t.Errorf("bitişe kalan = %d (known=%v late=%v), beklenen 25", p.DaysToEnd, p.EndKnown, p.Late)
	}
	for _, want := range []string{"Proje: B-12 (durum: devam ediyor, sorumlu: Mehmet, öncelik: yüksek)", "bitiş: 2026-10-31, 25 gün kaldı",
		"%40 (5 görevden 2 tamamlandı)", "Açık görev: 3, gecikmiş görev: 2", "iptal: 1 (ilerlemeye dahil değil)", "4 gün gecikti", "3 gün gecikti"} {
		if !strings.Contains(res.Text, want) {
			t.Errorf("metinde %q yok:\n%s", want, res.Text)
		}
	}
	if strings.Contains(res.Text, "test düzeneği") || strings.Contains(res.Text, "Genel temizlik") {
		t.Errorf("başka projenin ya da projesiz görev B-12 raporuna girdi:\n%s", res.Text)
	}
	// Gecikmişler (g3: 4 gün, g6: 3 gün) ilk iki sırada.
	if p.Tasks[0].ID != "g3" || p.Tasks[1].ID != "g6" {
		t.Errorf("görev sırası = %s, %s; beklenen g3, g6", p.Tasks[0].ID, p.Tasks[1].ID)
	}
}

func TestProjectReport_ZamanGecmis(t *testing.T) {
	rd := repReader(t,
		repProject("p1", "Kalıp A", map[string]string{"durum": "devam", "bitis": "2026-10-01"}),
		repProject("p2", "Kalıp B", map[string]string{"durum": "tamamlandi", "bitis": "2026-10-01"}),
	)
	late := mustReport(t, rd, ReportRequest{Type: ReportProject, Project: "Kalıp A"}, readUye).Projects[0]
	if !late.Late || late.DaysToEnd != -5 || !strings.Contains(endNote(late), "5 gün gecikti") {
		t.Errorf("geciken proje: late=%v gün=%d not=%q", late.Late, late.DaysToEnd, endNote(late))
	}
	done := mustReport(t, rd, ReportRequest{Type: ReportProject, Project: "Kalıp B"}, readUye).Projects[0]
	if done.Late {
		t.Errorf("tamamlanan proje geciken sayıldı")
	}
	if !strings.Contains(progressNote(late), "henüz görev yok") {
		t.Errorf("görevsiz proje ilerleme notu = %q", progressNote(late))
	}
}

func TestProjectReport_BulunamadiBilinenleriSayar(t *testing.T) {
	rd := repReader(t, projectFixture()...)
	res := mustReport(t, rd, ReportRequest{Type: ReportProject, Project: "Z-99"}, readUye)
	if !res.NotFound || len(res.Projects) != 0 {
		t.Fatalf("olmayan proje bulunmuş sayıldı: %+v", res)
	}
	if !strings.Contains(res.Text, "B-12") || !strings.Contains(res.Text, "B-120") || !strings.Contains(res.Text, "bulunamadı") {
		t.Errorf("metin = %q", res.Text)
	}
}

func TestProjectReport_BelirsizAdHepsiniGosterir(t *testing.T) {
	rd := repReader(t,
		repProject("p1", "Kalıp A Seri 1", map[string]string{"durum": "devam"}),
		repProject("p2", "Kalıp A Seri 2", map[string]string{"durum": "devam"}),
	)
	res := mustReport(t, rd, ReportRequest{Type: ReportProject, Project: "Kalıp A"}, readUye)
	if len(res.Projects) != 2 || !strings.Contains(res.Text, "hangisini kastettiğinizi belirtin") {
		t.Errorf("belirsiz ad: %d proje\n%s", len(res.Projects), res.Text)
	}
}

func TestSummaryReport(t *testing.T) {
	rd := repReader(t, projectFixture()...)
	res := mustReport(t, rd, ReportRequest{Type: ReportSummary}, readUye)
	if len(res.Projects) != 2 {
		t.Fatalf("proje sayısı = %d, beklenen 2", len(res.Projects))
	}
	// B-12: 2 gecikmiş, B-120: 1 gecikmiş → en çok geciken önde.
	if res.Projects[0].Name != "B-12" || res.Projects[1].Name != "B-120" {
		t.Errorf("sıra = %s, %s", res.Projects[0].Name, res.Projects[1].Name)
	}
	if len(res.Unassigned) != 1 || res.Unassigned[0].ID != "g8" {
		t.Errorf("projesiz görevler = %+v, beklenen yalnız g8", res.Unassigned)
	}
	for _, want := range []string{"Toplam: 2 proje, 3 gecikmiş görev", "Projeye bağlanamayan görev: 1 (gecikmiş: 1)", "1. B-12:", "2. B-120:"} {
		if !strings.Contains(res.Text, want) {
			t.Errorf("özette %q yok:\n%s", want, res.Text)
		}
	}
}

func TestReport_SayfalamaHepsiniGetirir(t *testing.T) {
	var cs []Claim
	for i := 0; i < 120; i++ { // PageSize 50: üç sayfa
		cs = append(cs, repTask(fmt.Sprintf("t%03d", i), fmt.Sprintf("Görev %d", i), map[string]string{"son_tarih": "2026-10-01", "durum": "acik"}, StatusActive))
	}
	res := mustReport(t, repReader(t, cs...), ReportRequest{Type: ReportOverdue}, readUye)
	if len(res.Tasks) != 120 || !strings.Contains(res.Text, "Toplam: 120 gecikmiş görev") {
		t.Errorf("120 gecikmiş görevden %d döndü", len(res.Tasks))
	}
}

func TestReport_GecersizTurHata(t *testing.T) {
	rd := repReader(t)
	if _, err := rd.Report(context.Background(), ReportRequest{Type: "uydurma"}, readUye); err == nil {
		t.Error("bilinmeyen rapor türü hata vermeli")
	}
}

func TestSameProject(t *testing.T) {
	cases := []struct {
		a, b string
		want bool
	}{
		{"b 12", "b 12", true},
		{"b 12", "b12", true},
		{"b 12", "b 120", false},
		{"b 12", "b 12 kalip", true},
		{"kalip a", "kalip a seri 1", true},
		{"a", "a b c", false}, // çok kısa ad yalnızca birebir eşleşir
		{"", "b 12", false},
	}
	for _, c := range cases {
		if got := sameProject(c.a, c.b); got != c.want {
			t.Errorf("sameProject(%q,%q) = %v, beklenen %v", c.a, c.b, got, c.want)
		}
	}
}

func TestParseIntent_Rapor(t *testing.T) {
	cases := []struct {
		raw  string
		want Intent
	}{
		{`{"type":"rapor","report":"gecikmis_gorev"}`, Intent{Type: IntentReport, Report: ReportOverdue}},
		{`{"type":"Rapor","report":"Geciken görevler"}`, Intent{Type: IntentReport, Report: ReportOverdue}},
		{`{"type":"rapor","report":"proje_durumu","project":"B-12"}`, Intent{Type: IntentReport, Report: ReportProject, Project: "B-12"}},
		{`{"type":"rapor","report":"proje_durumu","project":"  Kalıp   A  "}`, Intent{Type: IntentReport, Report: ReportProject, Project: "Kalıp A"}},
		{`{"type":"rapor","report":"proje_durumu"}`, Intent{Type: IntentReport, Report: ReportSummary}},                // proje adı yok → özet
		{`{"type":"rapor","report":"proje_ozet","project":"B-12"}`, Intent{Type: IntentReport, Report: ReportSummary}}, // özette ad atılır
		{`{"type":"rapor","report":"maaş"}`, Intent{Type: IntentQuestion}},                                             // bilinmeyen rapor → arama
		{`{"type":"rapor"}`, Intent{Type: IntentQuestion}},
	}
	for _, c := range cases {
		got, err := ParseIntent(c.raw)
		if err != nil || got != c.want {
			t.Errorf("ParseIntent(%q) = %+v, %v; beklenen %+v", c.raw, got, err, c.want)
		}
	}
	long := `{"type":"rapor","report":"proje_durumu","project":"` + strings.Repeat("x", 500) + `"}`
	got, _ := ParseIntent(long)
	if n := len([]rune(got.Project)); n > maxProjectAskRunes {
		t.Errorf("proje adı kırpılmadı: %d karakter", n)
	}
}

func TestPrepare_RaporSunucudaUretilir(t *testing.T) {
	emb := &readEmb{}
	llmf := &readLLM{reply: `{"type":"rapor","report":"proje_durumu","project":"B-12"}`}
	rd := newReader(readRepo(t, projectFixture()...), llmf, emb)
	rd.now = func() time.Time { return repNow }

	p, err := rd.Prepare(context.Background(), "B-12 projesi ne durumda?", readUye)
	if err != nil {
		t.Fatalf("Prepare: %v", err)
	}
	if p.Intent.Type != IntentReport || p.Report == nil {
		t.Fatalf("rapor niyeti işlenmedi: %+v", p.Intent)
	}
	if p.Append != p.Report.Text || !strings.Contains(p.Append, "%40 (5 görevden 2 tamamlandı)") {
		t.Errorf("Append sunucu raporu değil:\n%s", p.Append)
	}
	if p.Prompt != reportPrompt {
		t.Errorf("Prompt rapor yönergesi değil: %q", p.Prompt)
	}
	if emb.calls != 0 {
		t.Errorf("rapor sorusunda embedding çağrıldı (%d); vektör araması yapılmamalı", emb.calls)
	}
	if llmf.calls != 1 {
		t.Errorf("LLM çağrısı = %d, beklenen 1 (yalnızca ayırıcı; hesap kodda)", llmf.calls)
	}
}

func TestReport_EkipRaporuOzelTuruSizdirmaz(t *testing.T) {
	// Proje ve görev ekip türleridir: kimliksiz (sıfır) aktör de yetki filtresiyle ÖZEL türleri göremez
	// ama ekip raporu çalışır; özel tür kayıtları rapora hiç girmez.
	priv := readClaim("pd1", "pdks", "Ali bugün geç geldi", StatusActive, nil)
	priv.Key = ""
	priv.SubjectID = "u_ali"
	priv.Fields = map[string]string{"personel": "Ali", "tarih": "2026-10-06", "giris": "09:30"}
	rd := repReader(t, append(projectFixture(), priv)...)
	res := mustReport(t, rd, ReportRequest{Type: ReportSummary}, Actor{})
	if len(res.Projects) != 2 {
		t.Errorf("ekip raporu çalışmadı: %d proje", len(res.Projects))
	}
	if strings.Contains(res.Text, "Ali") {
		t.Errorf("özel tür kaydı rapora sızdı:\n%s", res.Text)
	}
}
