package kinds

import (
	"reflect"
	"strings"
	"testing"
	"time"
)

// 2026-10-06 Salı.
var testNow = time.Date(2026, 10, 6, 14, 30, 0, 0, time.UTC)

func TestParseDate(t *testing.T) {
	cases := []struct {
		in   string
		want string
		ok   bool
	}{
		{"2026-10-09", "2026-10-09", true},
		{"05.10.2026", "2026-10-05", true},
		{"5/10/26", "2026-10-05", true},
		{"5.10", "2026-10-05", true},
		{"bugün", "2026-10-06", true},
		{"Yarın", "2026-10-07", true},
		{"dün", "2026-10-05", true},
		{"cuma", "2026-10-09", true},
		{"Cuma'ya kadar", "2026-10-09", true},
		{"cumaya", "2026-10-09", true},
		{"salı", "2026-10-06", true}, // bugün dahil
		{"pazartesi", "2026-10-12", true},
		{"pazar", "2026-10-11", true},
		{"cumartesi", "2026-10-10", true},
		{"ay sonu", "2026-10-31", true},
		{"ayın sonuna kadar", "2026-10-31", true},
		{"15 Ekim", "2026-10-15", true},
		{"3 Kasım 2027", "2027-11-03", true},
		{"31.02.2026", "", false},
		{"32.10.2026", "", false},
		{"bilmem", "", false},
		{"", "", false},
	}
	for _, c := range cases {
		got, ok := ParseDate(c.in, testNow)
		if ok != c.ok || got != c.want {
			t.Errorf("ParseDate(%q) = %q,%v; beklenen %q,%v", c.in, got, ok, c.want, c.ok)
		}
	}
	// now sıfırken yalnızca mutlak tarihler çözülür.
	if got, ok := ParseDate("2026-10-09", time.Time{}); !ok || got != "2026-10-09" {
		t.Errorf("mutlak tarih now olmadan da çözülmeli: %q %v", got, ok)
	}
	if _, ok := ParseDate("cuma", time.Time{}); ok {
		t.Error("göreli ifade now olmadan çözülmemeli")
	}
}

func TestParseClock(t *testing.T) {
	for in, want := range map[string]string{"8:10": "08:10", "08.10": "08:10", "17:45": "17:45", "saat 07:05": "07:05"} {
		if got, ok := ParseClock(in); !ok || got != want {
			t.Errorf("ParseClock(%q) = %q,%v; beklenen %q", in, got, ok, want)
		}
	}
	for _, in := range []string{"25:00", "12:60", "sabah", "", "8"} {
		if _, ok := ParseClock(in); ok {
			t.Errorf("ParseClock(%q) geçersiz olmalı", in)
		}
	}
}

func TestDefaultRegistry(t *testing.T) {
	r := Default()
	if got, want := r.PrivateKinds(), []string{"pdks", "personel"}; !reflect.DeepEqual(got, want) {
		t.Errorf("özel türler = %v, beklenen %v", got, want)
	}
	for _, k := range []string{"personel", "pdks"} {
		if !r.IsPrivate(k) {
			t.Errorf("%s özel olmalı", k)
		}
	}
	for _, k := range []string{"firma", "proje", "gorev", "genel", "kisi", "urun", "bilinmeyen_tur"} {
		if r.IsPrivate(k) {
			t.Errorf("%s ekip görünürlüğünde olmalı", k)
		}
	}
	for _, k := range []string{"firma", "kisi", "urun", "proje", "personel"} {
		if !r.IsList(k) {
			t.Errorf("%s listelik olmalı", k)
		}
	}
	for _, k := range []string{"gorev", "pdks", "genel", "yok"} {
		if r.IsList(k) {
			t.Errorf("%s listelik olmamalı", k)
		}
	}
	if p := r.Prompt(); p == "" || !containsAll(p, "gorev:", "son_tarih (tarih)", "pdks:", "personel*", "giris (saat)") {
		t.Errorf("prompt eksik: %q", p)
	}
}

func containsAll(s string, subs ...string) bool {
	for _, x := range subs {
		if !strings.Contains(s, x) {
			return false
		}
	}
	return true
}

func TestNormalize_Gorev(t *testing.T) {
	s, _ := Default().Get("gorev")
	got, missing := s.Normalize(map[string]string{
		"Atanan": " Ahmet ", "Son Tarih": "cuma'ya kadar", "durum": "Devam ediyor", "uydurma": "x", "proje": "B-12",
	}, testNow)
	want := map[string]string{"atanan": "Ahmet", "son_tarih": "2026-10-09", "durum": "devam", "proje": "B-12"}
	if !reflect.DeepEqual(got, want) || len(missing) != 0 {
		t.Errorf("got=%v missing=%v; beklenen %v", got, missing, want)
	}

	// Atanan zorunlu DEĞİL (bilgi kaybolmasın); durum varsayılanı uygulanır; geçersiz tarih atılır.
	got, missing = s.Normalize(map[string]string{"son_tarih": "bilmem", "proje": "B-12"}, testNow)
	if len(missing) != 0 {
		t.Errorf("atanan zorunlu olmamalı: %v", missing)
	}
	if got["durum"] != "acik" || got["son_tarih"] != "" {
		t.Errorf("varsayılan durum ve geçersiz tarih: %v", got)
	}
}

func TestNormalize_Pdks(t *testing.T) {
	s, _ := Default().Get("pdks")
	got, missing := s.Normalize(map[string]string{"personel": "Ali", "tarih": "bugün", "giris": "8:10", "cikis": "17.45"}, testNow)
	want := map[string]string{"personel": "Ali", "tarih": "2026-10-06", "giris": "08:10", "cikis": "17:45", "kaynak": "elle"}
	if !reflect.DeepEqual(got, want) || len(missing) != 0 {
		t.Errorf("got=%v missing=%v; beklenen %v", got, missing, want)
	}
	// Tarih çözülemezse zorunlu alan eksiktir.
	if _, missing = s.Normalize(map[string]string{"personel": "Ali", "tarih": "geçen sene"}, testNow); !reflect.DeepEqual(missing, []string{"tarih"}) {
		t.Errorf("tarih eksik sayılmalı: %v", missing)
	}
}

func TestCheck(t *testing.T) {
	s, _ := Default().Get("pdks")
	if bad := s.Check(map[string]string{"personel": "Ali", "tarih": "2026-10-06", "giris": "08:10", "kaynak": "elle"}); len(bad) != 0 {
		t.Errorf("kanonik alanlar geçerli olmalı: %v", bad)
	}
	for name, f := range map[string]map[string]string{
		"bilinmeyen alan": {"maas": "100"},
		"kanonik değil":   {"tarih": "cuma"},
		"yanlış saat":     {"giris": "25:00"},
		"yanlış enum":     {"kaynak": "kart"},
	} {
		if bad := s.Check(f); len(bad) != 1 {
			t.Errorf("%s reddedilmeli: %v", name, bad)
		}
	}
}

func TestNewRegistry_Errors(t *testing.T) {
	ok := Schema{Kind: "x", Visibility: Team}
	cases := map[string][]Schema{
		"geçersiz ad":    {{Kind: "X Y", Visibility: Team}},
		"yinelenen tür":  {ok, ok},
		"görünürlük yok": {{Kind: "x"}},
		"yinelenen alan": {{Kind: "x", Visibility: Team, Fields: []Field{{Name: "a", Type: TypeText}, {Name: "a", Type: TypeText}}}},
		"enum boş":       {{Kind: "x", Visibility: Team, Fields: []Field{{Name: "a", Type: TypeEnum}}}},
		"keyfield yok":   {{Kind: "x", Visibility: Team, KeyField: "ad"}},
	}
	for name, ss := range cases {
		if _, err := NewRegistry(ss...); err == nil {
			t.Errorf("%s: hata beklenirdi", name)
		}
	}
	if _, err := NewRegistry(ok); err != nil {
		t.Errorf("geçerli şema reddedildi: %v", err)
	}
}

func TestNormalizeKeyed(t *testing.T) {
	s, _ := Default().Get("firma")
	got, missing := s.NormalizeKeyed(map[string]string{"tur": "Müşteriler"}, "ACME Kalıp A.Ş.", testNow)
	if len(missing) != 0 || got["ad"] != "ACME Kalıp A.Ş." || got["tur"] != "musteri" {
		t.Errorf("ad anahtardan türetilmeli: %v %v", got, missing)
	}
	if _, missing = s.NormalizeKeyed(nil, "", testNow); len(missing) != 1 || missing[0] != "ad" {
		t.Errorf("ad ve anahtar yoksa eksik: %v", missing)
	}
}
