package tool

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

func TestPermissionParsing(t *testing.T) {
	p, err := ParsePermission(" WRITE ")
	if err != nil || p != PermWrite {
		t.Fatalf("ParsePermission: %v %v", p, err)
	}
	if _, err := ParsePermission("sudo"); err == nil {
		t.Error("bilinmeyen izin reddedilmeliydi")
	}
	all, err := PermissionsOf([]string{"delete", "read", "READ"})
	if err != nil {
		t.Fatal(err)
	}
	if all != PermRead|PermDelete {
		t.Errorf("PermissionsOf: %v", all)
	}
	if got := all.Names(); len(got) != 2 || got[0] != "read" || got[1] != "delete" {
		t.Errorf("kanonik sıra bozuk: %v", got)
	}
	if !PermAll.Has(PermRead | PermSystem) {
		t.Error("PermAll tümünü kapsamalı")
	}
	if PermExecute.Has(PermRead) {
		t.Error("execute, read içermemeli")
	}
}

func TestPolicyAuthorize(t *testing.T) {
	mk := func(perms []string, risk Risk) *Tool {
		return &Tool{Name: "x.y", Permissions: perms, Risk: risk}
	}

	// Varsayılan politika read+write+execute verir (delete/system YOK).
	//
	// Canlı hata (04.10.2026): yalnızca execute verildiği için şirket tool'larının
	// çoğu modele hiç sunulmuyordu. Okuma/yazma artık varsayılan; geri alınamaz
	// işlemler (delete) ve sistem işlemleri (system) KAPALI kalır.
	def := DefaultPolicy()
	if err := def.Authorize(mk(nil, RiskLow)); err != nil {
		t.Errorf("execute varsayılan olarak verilmeli: %v", err)
	}
	if err := def.Authorize(mk([]string{"read"}, RiskLow)); err != nil {
		t.Errorf("read varsayılan olarak verilmeli: %v", err)
	}
	if err := def.Authorize(mk([]string{"write"}, RiskLow)); err != nil {
		t.Errorf("write varsayılan olarak verilmeli: %v", err)
	}
	// GERİ ALINAMAZ işlem yine de kapalı: bu, onay akışının ön kapısıdır.
	err := def.Authorize(mk([]string{"delete"}, RiskLow))
	if !errors.Is(err, ErrPermissionDenied) {
		t.Fatalf("delete varsayılan olarak REDDEDİLMELİYDİ: %v", err)
	}
	var pe *PermissionError
	if !errors.As(err, &pe) || pe.Missing != PermDelete {
		t.Errorf("eksik izin bildirilmeliydi: %+v", pe)
	}
	// system de kapalı.
	if err := def.Authorize(mk([]string{"system"}, RiskLow)); !errors.Is(err, ErrPermissionDenied) {
		t.Errorf("system varsayılan olarak reddedilmeliydi: %v", err)
	}

	// execute hiç bildirilmese de gerekir (Required her zaman ekler).
	if err := (Policy{Grants: PermRead}).Authorize(mk(nil, RiskLow)); !errors.Is(err, ErrPermissionDenied) {
		t.Errorf("execute izni olmadan reddedilmeliydi: %v", err)
	}

	// Risk kapıları: izinler yeterli olsa bile high/critical ek onay ister.
	full := Policy{Grants: PermAll}
	if err := full.Authorize(mk([]string{"delete"}, RiskHigh)); !errors.Is(err, ErrPermissionDenied) {
		t.Errorf("high risk onaysız çalışmamalı: %v", err)
	}
	if err := full.Authorize(mk([]string{"system"}, RiskCritical)); !errors.Is(err, ErrPermissionDenied) {
		t.Errorf("critical risk onaysız çalışmamalı: %v", err)
	}
	if err := (Policy{Grants: PermAll, AllowHighRisk: true}).Authorize(mk([]string{"delete"}, RiskHigh)); err != nil {
		t.Errorf("high onayıyla çalışmalı: %v", err)
	}
	// allowHighRisk, critical izni AÇMAZ: ayrı kapıdır.
	if err := (Policy{Grants: PermAll, AllowHighRisk: true}).Authorize(mk([]string{"system"}, RiskCritical)); !errors.Is(err, ErrPermissionDenied) {
		t.Error("high onayı critical'e yetmemeli")
	}
	if err := (Policy{Grants: PermAll, AllowCritical: true}).Authorize(mk([]string{"system"}, RiskCritical)); err != nil {
		t.Errorf("critical onayıyla çalışmalı: %v", err)
	}
	if err := def.Authorize(nil); !errors.Is(err, ErrNotFound) {
		t.Errorf("nil tool: %v, ErrNotFound bekleniyordu", err)
	}
}

func TestGrantStore(t *testing.T) {
	s := NewGrantStore(DefaultPolicy())
	if got := s.PolicyFor(""); got.Grants != PermRead|PermWrite|PermExecute {
		t.Errorf("kimliksiz istek varsayılanla sınırlı olmalı (delete YOK): %v", got)
	}
	// Kimliksiz istek geri alınamaz işlem çalıştıramaz (izin kapısı).
	if s.PolicyFor("").Allows(&Tool{Name: "t", Permissions: []string{"delete"}}) {
		t.Error("kimliksiz istek delete çalıştıramamalı")
	}
	s.SetPolicy("ali", Policy{Grants: PermAll, AllowHighRisk: true})
	if !s.PolicyFor("ali").Allows(&Tool{Name: "t", Permissions: []string{"delete"}, Risk: RiskHigh}) {
		t.Error("ali için high risk açık olmalı")
	}
	if s.PolicyFor("ayse").Grants != PermRead|PermWrite|PermExecute {
		t.Error("başka kullanıcı varsayılandan etkilenmemeli")
	}
	if got := s.Users(); len(got) != 1 || got[0] != "ali" {
		t.Errorf("Users: %v", got)
	}
	// Boş kullanıcı VARSAYILANI değiştirir.
	s.SetPolicy("", Policy{Grants: PermNone})
	if s.Default().Grants != PermNone || s.PolicyFor("ayse").Grants != PermNone {
		t.Error("boş kullanıcı varsayılanı değiştirmeli")
	}
}

func TestRegistryCRUD(t *testing.T) {
	ctx := context.Background()
	reg := testRegistry(t)

	def := Tool{Name: "catia.create_plane", Description: "Plane oluşturur", Permissions: []string{"execute"}}
	created, err := reg.Register(ctx, def)
	if err != nil {
		t.Fatal(err)
	}
	if created.ID == "" || created.Status != StatusEnabled || created.Version != DefaultVersion {
		t.Errorf("sunucu alanları uygulanmamış: %+v", created)
	}
	if _, err := reg.Register(ctx, def); !errors.Is(err, ErrDuplicateName) {
		t.Errorf("aynı ad: %v, ErrDuplicateName bekleniyordu", err)
	}
	if _, err := reg.Register(ctx, Tool{Name: "Hatali Ad"}); err == nil {
		t.Error("geçersiz ad kabul edilmemeliydi")
	}

	// Update: kimlik korunur, builtin kullanıcı gövdesinden gelmez.
	upd := *created
	upd.Name = "catia.create_plane_v2"
	upd.Builtin = true
	got, err := reg.Update(ctx, created.ID, upd)
	if err != nil {
		t.Fatal(err)
	}
	if got.ID != created.ID || got.Builtin {
		t.Errorf("kimlik korunmalı, builtin kullanıcıdan gelmemeli: %+v", got)
	}
	if _, err := reg.GetByName(ctx, "catia.create_plane"); !errors.Is(err, ErrNotFound) {
		t.Error("eski ad hâlâ çözülüyor")
	}

	// Validate: ad çakışması kendi adını saymaz.
	if err := reg.Delete(ctx, created.ID); err != nil {
		t.Fatal(err)
	}
	if errs, err := reg.Validate(ctx, "", Tool{Name: "yeni.ad", Description: "x"}); err != nil || len(errs) != 0 {
		t.Errorf("geçerli tanım: %v %v", errs, err)
	}
	if _, err := reg.Register(ctx, Tool{Name: "yeni.ad", Description: "x"}); err != nil {
		t.Fatal(err)
	}
	errs, err := reg.Validate(ctx, "", Tool{Name: "yeni.ad", Description: "x"})
	if err != nil {
		t.Fatal(err)
	}
	if len(errs) != 1 || errs[0].Field != "name" {
		t.Errorf("ad çakışması bildirilmeliydi: %+v", errs)
	}
	if errs, err := reg.Validate(ctx, mustID(t, reg, "yeni.ad"), Tool{Name: "yeni.ad", Description: "x"}); err != nil || len(errs) != 0 {
		t.Errorf("kendi adı çakışma sayılmamalı: %v %v", errs, err)
	}
}

func TestRegistryBuiltinLifecycle(t *testing.T) {
	ctx := context.Background()
	reg := testRegistry(t)
	def := echoDef("t.builtin")

	first, created, err := reg.EnsureBuiltin(ctx, def, func(context.Context, *Tool, map[string]any) (map[string]any, error) {
		return map[string]any{"v": 1}, nil
	}, true)
	if err != nil || !created {
		t.Fatalf("ilk kayıt: %v created=%v", err, created)
	}
	if !first.Builtin || first.Kind != KindBuiltin {
		t.Errorf("builtin işaretlenmeli: %+v", first)
	}

	// İkinci açılış: kullanıcının düzenlemesi korunmalı, kod açıklaması EZİLMEMELİ.
	edited := *first
	edited.Description = "kullanıcı düzenledi"
	if _, err := reg.Update(ctx, first.ID, edited); err != nil {
		t.Fatal(err)
	}
	def.Description = "yeni kod açıklaması"
	again, created, err := reg.EnsureBuiltin(ctx, def, nil, true)
	if err != nil || created {
		t.Fatalf("ikinci kayıt: %v created=%v", err, created)
	}
	if again.Description != "kullanıcı düzenledi" {
		t.Errorf("builtin tanımı ezildi: %q", again.Description)
	}

	// Silinemez.
	if err := reg.Delete(ctx, first.ID); !errors.Is(err, ErrBuiltin) {
		t.Errorf("builtin silinmemeliydi: %v", err)
	}
	// enabled=false kapatır; kapalıyken tekrar çağırmak durumu DEĞİŞTİRMEZ (kullanıcı kararı).
	if _, _, err := reg.EnsureBuiltin(ctx, def, nil, false); err != nil {
		t.Fatal(err)
	}
	got, err := reg.Get(ctx, first.ID)
	if err != nil {
		t.Fatal(err)
	}
	if got.Status != StatusDisabled {
		t.Errorf("enabled=false kapatmalıydı: %v", got.Status)
	}
	if _, _, err := reg.EnsureBuiltin(ctx, def, nil, true); err != nil {
		t.Fatal(err)
	}
	if got, _ = reg.Get(ctx, first.ID); got.Status != StatusDisabled {
		t.Errorf("kapalı tool otomatik açılmamalı: %v", got.Status)
	}
}

func TestRegistryResolve(t *testing.T) {
	ctx := context.Background()
	reg := testRegistry(t)
	x := boundTool(t, reg, echoDef("t.resolve"), func(context.Context, *Tool, map[string]any) (map[string]any, error) {
		return nil, nil
	})

	if _, h, err := reg.Resolve(ctx, "t.resolve"); err != nil || h == nil {
		t.Errorf("adla çözümleme: %v", err)
	}
	if _, h, err := reg.Resolve(ctx, x.ID); err != nil || h == nil {
		t.Errorf("kimlikle çözümleme: %v", err)
	}
	if _, _, err := reg.Resolve(ctx, "yok"); !errors.Is(err, ErrNotFound) {
		t.Errorf("yok: %v", err)
	}
	// Handler'ı olmayan custom tool yalnızca tanımdır.
	if _, err := reg.Register(ctx, Tool{Name: "t.custom", Description: "sadece tanım"}); err != nil {
		t.Fatal(err)
	}
	if _, _, err := reg.Resolve(ctx, "t.custom"); !errors.Is(err, ErrNotExecutable) {
		t.Errorf("handler'ı olmayan tool: %v, ErrNotExecutable bekleniyordu", err)
	}
	if reg.Executable("t.custom") || !reg.Executable("t.resolve") {
		t.Error("Executable bildirimi yanlış")
	}
}
