package database

import (
	"strconv"
	"testing"
)

// TestSettingDefaults ensures every seeded setting has a compiled default with
// a valid value for its declared type. This guards against typos that would
// silently fall back at runtime.
func TestSettingDefaults(t *testing.T) {
	if len(SettingDefaults) == 0 {
		t.Fatal("SettingDefaults is empty")
	}
	for _, d := range SettingDefaults {
		if d.Key == "" {
			t.Errorf("default has empty key: %+v", d)
			continue
		}
		switch d.Type {
		case SettingTypeInt:
			if _, err := strconv.Atoi(d.Value); err != nil {
				t.Errorf("int setting %s default %q is not an int: %v", d.Key, d.Value, err)
			}
			if d.Min != "" {
				if _, err := strconv.Atoi(d.Min); err != nil {
					t.Errorf("int setting %s min %q is not an int", d.Key, d.Min)
				}
			}
			if d.Max != "" {
				if _, err := strconv.Atoi(d.Max); err != nil {
					t.Errorf("int setting %s max %q is not an int", d.Key, d.Max)
				}
			}
		case SettingTypeBool:
			if _, err := strconv.ParseBool(d.Value); err != nil {
				t.Errorf("bool setting %s default %q is not a bool", d.Key, d.Value)
			}
		case SettingTypeString:
			if d.Value == "" {
				t.Errorf("string setting %s has empty default", d.Key)
			}
		default:
			t.Errorf("setting %s has unknown type %q", d.Key, d.Type)
		}
	}
}

// TestSettingsRegistryGetIntClamping verifies that out-of-range DB values are
// clamped to the declared min/max, and that garbage falls back to the default.
func TestSettingsRegistryGetIntClamping(t *testing.T) {
	r := &SettingsRegistry{values: map[string]string{}, q: nil}

	// Seed with an over-max value; expect clamping to the max (3600).
	r.values["scan_poll_interval_seconds"] = "999999"
	if got := r.ScanPollInterval(); got.Seconds() != 3600 {
		t.Errorf("expected clamp to 3600, got %v", got)
	}

	// Seed with an under-min value; expect clamp to min (1).
	r.values["scan_poll_interval_seconds"] = "0"
	if got := r.ScanPollInterval(); got.Seconds() != 1 {
		t.Errorf("expected clamp to 1, got %v", got)
	}

	// Seed with garbage; expect fallback to default (60).
	r.values["scan_poll_interval_seconds"] = "not-a-number"
	if got := r.ScanPollInterval(); got.Seconds() != 60 {
		t.Errorf("expected fallback default 60, got %v", got)
	}
}

// TestSettingsRegistryGetBoolFallback verifies bool parsing and fallback.
func TestSettingsRegistryGetBoolFallback(t *testing.T) {
	r := &SettingsRegistry{values: map[string]string{}, q: nil}

	r.values["auto_scan_enabled"] = "true"
	if !r.AutoScanEnabled() {
		t.Error("expected true")
	}

	r.values["auto_scan_enabled"] = "garbage"
	// garbage falls back to default ("true")
	if !r.AutoScanEnabled() {
		t.Error("expected fallback to default true")
	}
}

// TestLookupDefaultUnknownKey verifies unknown keys return ok=false.
func TestLookupDefaultUnknownKey(t *testing.T) {
	if _, ok := LookupDefault("does_not_exist"); ok {
		t.Error("expected ok=false for unknown key")
	}
	if _, ok := LookupDefault("session_duration_seconds"); !ok {
		t.Error("expected ok=true for known key")
	}
}
