// Package tool Tool Registry, Tool Executor ve Permission sistemini içerir (PROJE.MD Faz 4).
//
// Ayrım şudur: SKILL "ne yapılabilir" tanımıdır (vektörle aranır, LLM'e önerilir),
// TOOL ise gerçek işi yapan dışarı çağrıdır. Bir skill, executorConfig{"tool": "..."}
// ile bir tool'a bağlanır (bkz. skill.Service.ExecuteSkill).
//
// Katman sorumlulukları:
//   - Registry  : tool tanımlarının CRUD iş kuralları + çalışma zamanı handler bağlama
//   - Executor  : validate → permission → timeout → retry → denetim kaydı zinciri
//   - Policy    : kimin hangi tool'u çalıştırabileceği (varsayılan: SADECE execute)
//
// Güvenlik ilkesi (spec: "Tool'lar doğrudan kontrolsüz çalışmamalı"):
// Varsayılan izin yalnızca EXECUTE'tur; READ/WRITE/DELETE/SYSTEM ve HIGH/CRITICAL risk
// kullanıcı tarafından AÇIKÇA verilmeden hiçbir tool çalışmaz.
package tool

import (
	"context"
	"time"
)

// Status tool'un çalıştırılabilirlik durumudur.
type Status string

const (
	// StatusEnabled kullanılabilir tool.
	StatusEnabled Status = "enabled"
	// StatusDisabled tanımı korunmuş ama çalıştırılmayan tool.
	StatusDisabled Status = "disabled"
)

// Valid bilinen bir durum mu?
func (s Status) Valid() bool { return s == StatusEnabled || s == StatusDisabled }

// Kind tool'un nasıl uygulandığını belirtir.
type Kind string

const (
	// KindBuiltin Go ile yazılmış, çalışma zamanında handler'ı bağlanan tool.
	KindBuiltin Kind = "builtin"
	// KindCustom yalnızca tanımı saklanan tool (handler'ı yoktur, bu fazda YÜRÜTÜLEMEZ).
	KindCustom Kind = "custom"
	// KindHTTP uzak bir HTTP servisini çağıran tool'dır (ör. CATIA otomasyon servisi).
	// Handler çalışma zamanında SSRF korumalı istemciyle ÜRETİLİR (bkz. httptool.go);
	// kullanıcı yalnızca method/url/header tanımı yazar.
	KindHTTP Kind = "http"
)

// Valid bilinen bir tür mü?
func (k Kind) Valid() bool { return k == KindBuiltin || k == KindCustom || k == KindHTTP }

// UI, bir tool'un çıktısının arayüzde HANGİ GÖRÜNÜMLE sunulacağını belirtir.
//
// Neden var? "Her şeyi JSON olarak göster" çalışır ama kullanılmaz: 40 satırlık
// tablo JSON'da okunmaz, dosya içeriği kod bloğunda okunmaz. Tool tanımı, kendi
// doğasından gelen bir arayüzü bilir; arayüz de onu sunar. Yeni bir tool eklenirken
// ayrıca frontend kodu yazılmaz — `ui` alanı yeterlidir.
//
// Kural: `UIAuto` seçilirse sunucu tool adının ön ekine bakar (bkz. builtin ui.go),
// o da bulamazsa `UIJson` düşer. Böylece HİÇBİR tool yanlış/arayüzsüz kalmaz.
type UI string

const (
	// UIAuto arayüzü tool adından türetir (önerilen varsayılan).
	UIAuto UI = "auto"
	// UIJson çıktı olduğu gibi JSON ağacı olarak gösterilir (her zaman çalışır).
	UIJson UI = "json"
	// UITable kayıt listeleri için tablo (sıralanabilir, aranabilir satırlar).
	UITable UI = "table"
	// UIFile dosya içeriği için kod/editör görünümü.
	UIFile UI = "file"
	// UIList sıralı öğe listesi (arama sonuçları, bağlantılar).
	UIList UI = "list"
	// UICode terminal/süreç çıktısı (stdout/stderr ayrı gösterilir).
	UICode UI = "code"
	// UIMetrics sayısal özet (finans, döviz, sayaç).
	UIMetrics UI = "metrics"
	// UIForm tek satırlık anahtar/değer özeti (küçük sonuçlar).
	UIForm UI = "form"
)

// AllUI, sunucuda kabul edilen görünüm türlerinin tamamıdır (arayüz süzgeci ve doğrulama).
var AllUI = []UI{UIAuto, UIJson, UITable, UIFile, UIList, UICode, UIMetrics, UIForm}

// Valid bilinen bir görünüm mü?
func (u UI) Valid() bool {
	switch u {
	case UIAuto, UIJson, UITable, UIFile, UIList, UICode, UIMetrics, UIForm:
		return true
	}
	return false
}

// Label görünümün insan dilindeki adıdır (arayüz seçici kutularında görünür).
func (u UI) Label() string {
	switch u {
	case UITable:
		return "Tablo"
	case UIFile:
		return "Dosya"
	case UIList:
		return "Liste"
	case UICode:
		return "Kod / terminal"
	case UIMetrics:
		return "Özet (metrik)"
	case UIForm:
		return "Anahtar-değer"
	case UIJson:
		return "JSON"
	default:
		return "Otomatik"
	}
}

// Risk tool'un etki alanıdır: yanlış çalıştırılmasının sonucu ne kadar ağır olur.
type Risk string

const (
	RiskLow      Risk = "low"      // dosya okumak
	RiskMedium   Risk = "medium"   // dosya yazmak
	RiskHigh     Risk = "high"     // dosya silmek, süreç çalıştırmak
	RiskCritical Risk = "critical" // sistem geneli etki
)

// Valid bilinen bir risk seviyesi mi?
func (r Risk) Valid() bool {
	return r == RiskLow || r == RiskMedium || r == RiskHigh || r == RiskCritical
}

// Rank risk sıralamasıdır (LOW=0 .. CRITICAL=3); karşılaştırma için.
func (r Risk) Rank() int {
	switch r {
	case RiskLow:
		return 0
	case RiskMedium:
		return 1
	case RiskHigh:
		return 2
	case RiskCritical:
		return 3
	}
	return 0 // tanımsız seviye en güvenli kabul edilir
}

// AtLeast verilen seviyeye eşit ya da daha yüksek mi?
func (r Risk) AtLeast(min Risk) bool { return r.Rank() >= min.Rank() }

// Sınırlar: bozuk ya da aşırı büyük tanımlar hem yürütmeyi hem arayüzü bozar.
const (
	MaxDescriptionRunes = 2000
	MaxTitleRunes       = 120
	MaxTags             = 20
	MaxTagRunes         = 32
	MaxConfigRunes      = 4000
	MaxPermissions      = 5
	MaxSchemaRunes      = 8000

	// DefaultTimeoutMs tool tanımı süre vermezse kullanılan üst sınır.
	DefaultTimeoutMs = 30_000
	// MaxTimeoutMs bir tool'un isteyebileceği en uzun süre (yapılandırmadan da sınırlanır).
	MaxTimeoutMs = 300_000
	// MaxRetryAttempts tek bir çalıştırmada izin verilen en fazla deneme sayısı.
	MaxRetryAttempts = 4
	// DefaultVersion boş bırakıldığında uygulanır.
	DefaultVersion = "1.0.0"
)

// Retry geçici hatalarda tekrar deneme politikasıdır (spec: her tool "retry" desteklemeli).
// Yalnızca hata Retryable() bildirirse tekrar denenir; kalıcı hatalar çoğaltılmaz.
type Retry struct {
	MaxAttempts int `bson:"maxAttempts,omitempty" json:"maxAttempts,omitempty"`
	BackoffMs   int `bson:"backoffMs,omitempty" json:"backoffMs,omitempty"`
}

// Tool bir dış aracın tanımıdır. Skill'in executorConfig'i yerine burada doğrudan
// izinler, risk ve zaman aşımı tanımlıdır (spec'in Tool alanları).
type Tool struct {
	ID          string `bson:"_id" json:"id"`
	Name        string `bson:"name" json:"name"` // "catia.create_plane": uygulama.eylem
	Title       string `bson:"title,omitempty" json:"title,omitempty"`
	Description string `bson:"description" json:"description"`
	Version     string `bson:"version" json:"version"`
	Status      Status `bson:"status" json:"status"`
	Kind        Kind   `bson:"kind" json:"kind"`

	InputSchema  map[string]any `bson:"inputSchema,omitempty" json:"inputSchema,omitempty"`
	OutputSchema map[string]any `bson:"outputSchema,omitempty" json:"outputSchema,omitempty"`

	// UI çıktının arayüzde nasıl gösterileceğini söyler (bkz. UI türü).
	// Boşsa UIAuto: sunucu ad ön ekinden türetir (builtin/ui.go), o da bulamazsa JSON.
	UI UI `bson:"ui,omitempty" json:"ui,omitempty"`

	// Permissions: read, write, execute, delete, system (spec'in izin kümesi).
	// Her tool çalıştırıldığında ayrıca execute izni gerekir.
	Permissions []string `bson:"permissions" json:"permissions"`
	Risk        Risk     `bson:"risk" json:"risk"`

	TimeoutMs int   `bson:"timeoutMs,omitempty" json:"timeoutMs,omitempty"`
	Retry     Retry `bson:"retry,omitempty" json:"retry,omitempty"`

	// Config tool uygulamasına özel ayarlardır (ör. dosya kökü). Tanımın parçasıdır.
	Config map[string]any `bson:"config,omitempty" json:"config,omitempty"`

	Tags []string `bson:"tags" json:"tags"`
	// Builtin true ise kayıt kullanıcı tarafından silinemez (yalnızca kapatılabilir).
	Builtin bool `bson:"builtin,omitempty" json:"builtin,omitempty"`

	CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
	UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
}

// Clone derin bir kopya döndürür; depo ile çağıran birbirinin verisini değiştiremez
// (skill.Skill.Clone ile aynı gerekçe: bellek içi depo haritayı paylaşırdı).
func (t Tool) Clone() Tool {
	c := t
	c.InputSchema = cloneMap(t.InputSchema)
	c.OutputSchema = cloneMap(t.OutputSchema)
	c.Config = cloneMap(t.Config)
	// Config yazılabilir olmalıdır: executor çalıştırma bağlamını (kullanıcı kimliği,
	// sohbet kimliği) buraya ekler. Kaynak tanımda Config nil ise klon da nil kalırsa
	// sonraki atama panikler; boş harita ile BAŞLATILIR.
	if c.Config == nil {
		c.Config = map[string]any{}
	}
	c.Permissions = cloneSlice(t.Permissions)
	c.Tags = cloneSlice(t.Tags)
	return c
}

// Handler tool'un gerçek uygulamasıdır. Mongo'ya yazılamaz; çalışma zamanında
// Registry.Bind ile bağlanır (bu yüzden Tool yapısında tutulmaz).
//
// Sözleşme: handler girdiyi DEĞİŞTİRMEZ, hata döndürürse Executor bunu
// FailedError'a sarar, iptal/süre aşımına saygılıdır (ctx).
type Handler func(ctx context.Context, t *Tool, in map[string]any) (map[string]any, error)

func cloneSlice[T any](in []T) []T {
	if in == nil {
		return nil
	}
	out := make([]T, len(in))
	copy(out, in)
	return out
}

func cloneMap(m map[string]any) map[string]any {
	if m == nil {
		return nil
	}
	out := make(map[string]any, len(m))
	for k, v := range m {
		out[k] = cloneValue(v)
	}
	return out
}

func cloneValue(v any) any {
	switch t := v.(type) {
	case map[string]any:
		return cloneMap(t)
	case []any:
		out := make([]any, len(t))
		for i, x := range t {
			out[i] = cloneValue(x)
		}
		return out
	default:
		return v
	}
}
