// Package builtin kodla gelen tool'ların tanım ve uygulamalarını içerir.
//
// Neden builtin? PROJE.MD "Tool interface oluştur... CATIA özel kod yok" diyor:
// tool sistemi jenerik olmalı. Bu paket sistemin UYGULANABILIR olduğunu gösteren
// küçük ve güvenli bir çekirdek sunar; gerçek CATIA/MCP tool'ları aynı sözleşmeyle
// sonradan eklenir.
//
// Güvenlik: dosya işlemleri TOOL_FS_ROOT altına SANDOZLANIR (kök dışına kaçış reddedilir),
// süreç çalıştırma varsayılan olarak KAPALI ve izin listesindeki tam yollarla sınırlıdır.
// Hiçbir tool kendi başına izin almaz: yetki kararı her zaman Executor'a aittir.
package builtin

import (
	"fmt"
	"log/slog"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"fiko-ai/internal/tool"
)

// Options builtin tool'ların yapılandırmasıdır.
type Options struct {
	// FSRoot dosya tool'larının kök dizini. Altındaki her şey çalıştırılabilir kabul edilir.
	FSRoot string
	// ExecAllowed süreç çalıştırma tool'ının izinli TİP YOLLARI (tam yol; boşsa tool hiç açılmaz).
	ExecAllowed []string
	// MaxReadBytes okunacak en büyük dosya (bayt).
	MaxReadBytes int64
	// MaxWriteBytes yazılacak en büyük içerik (bayt).
	MaxWriteBytes int64
	// ExecTimeout bir süreç komutunun üst sınırı (tool zaman aşımından bağımsız güvenlik sınırı).
	ExecTimeout time.Duration
	// Clock saat kaynağı (testte sabitlenir).
	Clock func() time.Time

	// WebEnabled true ise internet tool'ları (web.fetch, web.search, web.exchange_rate) kaydedilir.
	WebEnabled bool
	// HTTPClient web tool'larının istemcisi. Üretimde SSRF korumalı istemci verilir
	// (tool.NewSafeClient); nil ise web tool'ları hiç kaydedilmez (korumasız istemci YOK).
	HTTPClient *http.Client
	// WebSearchClient web.search'ın İSTEĞİNİ atan istemcisidir.
	//
	// Neden ayrı? web.fetch'in istemcisi loopback'i reddeder (SSRF). Arama sağlayıcısı
	// yerelde çalışan SearXNG ise (http://127.0.0.1:8181) aynı koruma Aramayı da
	// engeller. Burada verilen istemci YALNIZCA yapılandırılmış arama adresine
	// bağlanabilir (tool.NewPinnedClient) — tüm web tool'larına özel ağ erişimi
	// açılmaz. Boşsa web.search, HTTPClient'ı kullanır.
	WebSearchClient *http.Client
	// WebSearchProvider: "duckduckgo" (varsayılan, anahtarsız) | "searxng" | "brave".
	WebSearchProvider string
	// WebSearchURL searxng için örnek adresi (örn. http://localhost:8181).
	WebSearchURL string
	// WebSearchKey brave için API anahtarı. Kaynak koda yazılmaz; ortamdan gelir.
	WebSearchKey string

	// BizEnabled true ise şirket yönetimi tool'ları (biz.*) kaydedilir.
	BizEnabled bool
	// BizPath şirket verisinin JSON dosyası; boşsa bellekte tutulur (yeniden başlatınca kaybolur).
	BizPath string

	// ConfirmMode yazma/silme işlemlerinde kullanıcı onayının ne zaman istendiğidir
	// (always|risky|missing|never). Boşsa güvenli varsayılan (risky) uygulanır.
	//
	// Neden burada? Ajan katmanı her üretimde güncel politikayı okuyup tool'lara geçirir;
	// böylece kullanıcı ayarı anında etkili olur (sunucu yeniden başlatmaya gerek kalmaz).
	ConfirmMode string

	// Logger açılış uyarıları (kapalı araç, senkron hatası) için. Boşsa uyarı YOK.
	// Kütüphane log'unu doğrudan yazmaz; sunucu bağlantı noktasından verir.
	Logger *slog.Logger
}

// log uyarı yazar (Logger bağlanmadıysa sessiz geçilir).
func (o Options) log(msg string, args ...any) {
	if o.Logger != nil {
		o.Logger.Warn(msg, args...)
	}
}

func (o Options) fsRoot() string {
	if strings.TrimSpace(o.FSRoot) == "" {
		return filepath.Join("data", "tools")
	}
	return filepath.Clean(o.FSRoot)
}

func (o Options) maxRead() int64 {
	if o.MaxReadBytes <= 0 {
		return 1 << 20 // 1 MiB
	}
	return o.MaxReadBytes
}

func (o Options) maxWrite() int64 {
	if o.MaxWriteBytes <= 0 {
		return 1 << 20 // 1 MiB
	}
	return o.MaxWriteBytes
}

func (o Options) clock() time.Time {
	if o.Clock == nil {
		return time.Now().UTC()
	}
	return o.Clock()
}

// confirmMode etkin onay politikasını döndürür (boş/geçersizse güvenli varsayılan).
func (o Options) confirmMode() string {
	switch o.ConfirmMode {
	case ConfirmAlways, ConfirmRisky, ConfirmMissing, ConfirmNever:
		return o.ConfirmMode
	default:
		return ConfirmRisky
	}
}

// obj yardımcısı: JSON Schema nesnesi kurar (okunabilirlik için kısa yol).
func obj(pairs ...any) map[string]any {
	m := map[string]any{}
	for i := 0; i+1 < len(pairs); i += 2 {
		m[pairs[i].(string)] = pairs[i+1]
	}
	return m
}

// str alan kısayolu.
func str(desc string) map[string]any { return obj("type", "string", "description", desc) }

// Fail uygulama katmanının hata döndürmek için kısa yoludur.
// Geçici hatalar tool.Temporary ile işaretlenir (Executor o zaman tekrar dener).
func Fail(toolName, format string, args ...any) error {
	return &tool.FailedError{Tool: toolName, Reason: fmt.Sprintf(format, args...)}
}

// inputString girdiden zorunlu metin alanını okur.
func inputString(in map[string]any, key string) (string, bool) {
	v, ok := in[key].(string)
	return v, ok && strings.TrimSpace(v) != ""
}
