package config

import (
	"slices"
	"strings"
	"testing"
	"time"
)

// clearEnv testlerin ortam değişkenlerinden etkilenmesini engeller.
// t.Setenv test bittikten sonra eski değeri geri yükler (testler paralel çalışmaz).
func clearEnv(t *testing.T) {
	t.Helper()
	for _, k := range []string{
		"APP_ENV", "HTTP_ADDR", "HTTP_ALLOWED_ORIGINS", "HTTP_SHUTDOWN_TIMEOUT",
		"MONGO_URI", "MONGO_DATABASE", "MONGO_CONNECT_TIMEOUT",
		"OLLAMA_BASE_URL", "OLLAMA_MODEL", "OLLAMA_REQUEST_TIMEOUT",
		"DEFAULT_USER_ID", "CHAT_MAX_HISTORY_MESSAGES", "CHAT_GENERATION_TIMEOUT",
		"CHAT_TIMEZONE", "CHAT_MAX_INPUT_RUNES", "CHAT_DEFAULT_CONTEXT_TOKENS",
		"WS_PING_INTERVAL", "WS_PONG_WAIT", "WS_WRITE_WAIT",
		"WS_MAX_MESSAGE_BYTES", "WS_OUTBOUND_BUFFER",
		"LOG_LEVEL", "LOG_FORMAT",
	} {
		t.Setenv(k, "")
	}
}

// Load gerçek ortamı okur; env'in boş olduğundan emin olunur.
// (Boş değerler getString/getInt için "tanımsız" sayılır ve varsayılana düşülür.)
func TestLoad_Defaults(t *testing.T) {
	clearEnv(t)
	cfg, err := Load()
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if cfg.HTTP.Addr != ":8080" {
		t.Errorf("HTTP.Addr = %q, beklenen :8080", cfg.HTTP.Addr)
	}
	if cfg.Env != "development" {
		t.Errorf("Env = %q", cfg.Env)
	}
	if cfg.Ollama.BaseURL != "http://localhost:11434" {
		t.Errorf("Ollama.BaseURL = %q", cfg.Ollama.BaseURL)
	}
	if cfg.Chat.DefaultUserID != "local" {
		t.Errorf("Chat.DefaultUserID = %q", cfg.Chat.DefaultUserID)
	}
	if cfg.Chat.GenerationTimeout != 5*time.Minute {
		t.Errorf("GenerationTimeout = %v, beklenen 5m", cfg.Chat.GenerationTimeout)
	}
	// 131072 = 128k. Kullanıcı isteğiyle tam model penceresi kullanılır
	// (canlı hata 05.10.2026: 4096'da "AI her mesajda sıfırlanıyor").
	if cfg.Chat.DefaultContextTokens != 131072 {
		t.Errorf("DefaultContextTokens = %d, beklenen 131072", cfg.Chat.DefaultContextTokens)
	}
	if cfg.Ollama.MaxContext != 131072 {
		t.Errorf("Ollama.MaxContext = %d, beklenen 131072", cfg.Ollama.MaxContext)
	}
	if cfg.WS.PingInterval != 20*time.Second || cfg.WS.PongWait != 50*time.Second {
		t.Errorf("WS zamanlamaları = %v / %v", cfg.WS.PingInterval, cfg.WS.PongWait)
	}
	if len(cfg.HTTP.AllowedOrigins) != 2 {
		t.Errorf("AllowedOrigins = %v, beklenen 2 giriş", cfg.HTTP.AllowedOrigins)
	}
}

func TestLoad_Overrides(t *testing.T) {
	clearEnv(t)
	t.Setenv("HTTP_ADDR", ":9090")
	t.Setenv("OLLAMA_MODEL", "qwen2.5:7b")
	t.Setenv("CHAT_GENERATION_TIMEOUT", "90s")
	t.Setenv("CHAT_MAX_HISTORY_MESSAGES", "10")
	t.Setenv("WS_MAX_MESSAGE_BYTES", "2048")
	t.Setenv("LOG_FORMAT", "json")
	// Boş string değer varsayılana düşmeli (yoksa tüm ayarlar bozulur).
	t.Setenv("OLLAMA_BASE_URL", "")

	cfg, err := Load()
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if cfg.HTTP.Addr != ":9090" {
		t.Errorf("Addr = %q", cfg.HTTP.Addr)
	}
	if cfg.Ollama.Model != "qwen2.5:7b" {
		t.Errorf("Model = %q", cfg.Ollama.Model)
	}
	if cfg.Chat.GenerationTimeout != 90*time.Second {
		t.Errorf("GenerationTimeout = %v, beklenen 90s", cfg.Chat.GenerationTimeout)
	}
	if cfg.Chat.MaxHistoryMessages != 10 {
		t.Errorf("MaxHistoryMessages = %d", cfg.Chat.MaxHistoryMessages)
	}
	if cfg.WS.MaxMessageBytes != 2048 {
		t.Errorf("MaxMessageBytes = %d", cfg.WS.MaxMessageBytes)
	}
	if cfg.Log.Format != "json" {
		t.Errorf("Log.Format = %q", cfg.Log.Format)
	}
	if cfg.Ollama.BaseURL != "http://localhost:11434" {
		t.Errorf("boş değer varsayılana düşmedi: %q", cfg.Ollama.BaseURL)
	}
}

// Geçersiz (sayı/süre olmayan) değerler varsayılana düşer, ÇÖKMEZ.
func TestLoad_InvalidValueFallsBackToDefault(t *testing.T) {
	clearEnv(t)
	t.Setenv("CHAT_MAX_HISTORY_MESSAGES", "çok")
	t.Setenv("CHAT_GENERATION_TIMEOUT", "bir saat")
	t.Setenv("WS_PING_INTERVAL", "hızlı")

	cfg, err := Load()
	if err != nil {
		t.Fatalf("geçersiz değer çökmeye yol açmamalı: %v", err)
	}
	if cfg.Chat.MaxHistoryMessages != 60 {
		t.Errorf("MaxHistoryMessages = %d, beklenen varsayılan 60", cfg.Chat.MaxHistoryMessages)
	}
	if cfg.Chat.GenerationTimeout != 5*time.Minute {
		t.Errorf("GenerationTimeout = %v, beklenen 5m", cfg.Chat.GenerationTimeout)
	}
	if cfg.WS.PingInterval != 20*time.Second {
		t.Errorf("PingInterval = %v, beklenen 20s", cfg.WS.PingInterval)
	}
}

// Validate tutarsız ayarları reddetmeli.
func TestValidate(t *testing.T) {
	base := func() Config {
		clearEnv(t)
		c, _ := Load()
		return c
	}

	if err := base().Validate(); err != nil {
		t.Fatalf("varsayılan yapılandırma geçerli olmalı: %v", err)
	}

	cases := []struct {
		name   string
		mutate func(*Config)
	}{
		{"ollama_url_bozuk", func(c *Config) { c.Ollama.BaseURL = "://gecersiz" }},
		{"mongo_uri_bos", func(c *Config) { c.Mongo.URI = "" }},
		{"mongo_db_bos", func(c *Config) { c.Mongo.Database = "" }},
		{"gecmis_sifir", func(c *Config) { c.Chat.MaxHistoryMessages = 0 }},
		{"pong_wait_kucuk", func(c *Config) { c.WS.PongWait = c.WS.PingInterval }},
		{"baglam_penceresi_kucuk", func(c *Config) { c.Chat.DefaultContextTokens = 100 }},
		{"outbound_buffer_sifir", func(c *Config) { c.WS.OutboundBuffer = 0 }},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			c := base()
			tc.mutate(&c)
			if err := c.Validate(); err == nil {
				t.Fatalf("geçersiz yapılandırma kabul edilmemeliydi")
			}
		})
	}
}

// AllowedOrigins virgülle ayrılmış listedir; boşluklar temizlenmeli.
// Varsayılan izin kümesi silme aracını da açmalıdır.
//
// CANLI HATA (04.10.2026): "delete" izni varsayılanda yoktu → biz.delete modele hiç
// sunulmuyordu → kullanıcı "projemi sil" dediğinde "araç yok" deniyordu. Silme yine de
// güvenlidir: araç kullanıcı onay formu olmadan çalışmaz (bkz. builtin/confirm.go).
func TestLoad_DefaultPermissionsIncludeDelete(t *testing.T) {
	clearEnv(t)
	cfg, err := Load()
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	got := strings.Join(cfg.Tools.DefaultPermissions, ",")
	if !strings.Contains(got, "delete") {
		t.Errorf("varsayılan izinler silmeyi içermeli: %q", got)
	}
	for _, want := range []string{"read", "write", "execute", "delete"} {
		if !slices.Contains(cfg.Tools.DefaultPermissions, want) {
			t.Errorf("varsayılan izinlerde %q yok: %v", want, cfg.Tools.DefaultPermissions)
		}
	}
	// system YOK: süreç çalıştırma varsayılan olarak kapalı kalmalı.
	if slices.Contains(cfg.Tools.DefaultPermissions, "system") {
		t.Errorf("varsayılan izinlerde system olmamalı: %v", cfg.Tools.DefaultPermissions)
	}
	// HIGH/CRITICAL kapıları kapalı kalır (sys.exec modele hiç sunulmaz).
	if cfg.Tools.AllowHighRisk || cfg.Tools.AllowCritical {
		t.Errorf("yüksek/kritik risk kapıları kapalı olmalı: high=%v critical=%v",
			cfg.Tools.AllowHighRisk, cfg.Tools.AllowCritical)
	}
}

func TestLoad_AllowedOriginsList(t *testing.T) {
	clearEnv(t)
	t.Setenv("HTTP_ALLOWED_ORIGINS", " http://a.test , ,http://b.test ")

	cfg, err := Load()
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if len(cfg.HTTP.AllowedOrigins) != 2 {
		t.Fatalf("origins = %v, beklenen 2 temiz giriş", cfg.HTTP.AllowedOrigins)
	}
	if cfg.HTTP.AllowedOrigins[0] != "http://a.test" || cfg.HTTP.AllowedOrigins[1] != "http://b.test" {
		t.Errorf("origins = %v", cfg.HTTP.AllowedOrigins)
	}
}

func TestSplitList(t *testing.T) {
	cases := []struct {
		in   string
		want int
	}{
		{"", 0},
		{"   ", 0},
		{",", 0},
		{"a", 1},
		{"a,b", 2},
		{" a , b , ", 2},
		{"a,,b", 2},
	}
	for _, c := range cases {
		if got := len(splitList(c.in)); got != c.want {
			t.Errorf("splitList(%q) = %d, beklenen %d", c.in, got, c.want)
		}
	}
}

// Hata mesajı hangi ayarın bozuk olduğunu söylemeli (kullanıcı düzeltebilsin).
func TestValidate_MessageMentionsSetting(t *testing.T) {
	clearEnv(t)
	c, _ := Load()
	c.WS.PongWait = c.WS.PingInterval

	err := c.Validate()
	if err == nil {
		t.Fatal("hata bekleniyordu")
	}
	if !strings.Contains(err.Error(), "PONG_WAIT") {
		t.Errorf("hata mesajı ayar adını içermeli: %q", err.Error())
	}
}
