package logger

import (
	"strings"
	"testing"
)

func TestParseLogLevel(t *testing.T) {
	tests := []struct {
		input string
		want  LogLevel
	}{
		{"debug", DEBUG},
		{"DEBUG", DEBUG},
		{"info", INFO},
		{"INFO", INFO},
		{"warn", WARN},
		{"WARN", WARN},
		{"warning", WARN},
		{"WARNING", WARN},
		{"error", ERROR},
		{"ERROR", ERROR},
		{"invalid", INFO},
		{"", INFO},
	}
	for _, tt := range tests {
		got := ParseLogLevel(tt.input)
		if got != tt.want {
			t.Errorf("ParseLogLevel(%q) = %v, want %v", tt.input, got, tt.want)
		}
	}
}

func TestLogger_LevelFiltering(t *testing.T) {
	tests := []struct {
		loggerLevel string
		logLevel    LogLevel
		shouldLog   bool
	}{
		{"debug", DEBUG, true},
		{"debug", INFO, true},
		{"info", DEBUG, false},
		{"info", INFO, true},
		{"warn", INFO, false},
		{"warn", WARN, true},
		{"error", WARN, false},
		{"error", ERROR, true},
	}
	for _, tt := range tests {
		l := NewWithLevel("test", tt.loggerLevel)
		got := l.ShouldLog(tt.logLevel)
		if got != tt.shouldLog {
			t.Errorf("level=%s ShouldLog(%v)=%v want %v", tt.loggerLevel, tt.logLevel, got, tt.shouldLog)
		}
	}
}

func TestLogger_WithFields(t *testing.T) {
	l := New("test")
	l2 := l.WithFields(map[string]any{"key": "value", "n": 42})
	if l2 == l {
		t.Error("WithFields should return a new Logger")
	}
	if len(l2.fields) != 2 {
		t.Errorf("expected 2 fields, got %d", len(l2.fields))
	}
}

func TestLogger_SetLevel(t *testing.T) {
	l := New("test")
	if l.minLevel != INFO {
		t.Errorf("default level should be INFO, got %v", l.minLevel)
	}
	l.SetLevel("debug")
	if l.minLevel != DEBUG {
		t.Errorf("level after SetLevel(debug) should be DEBUG, got %v", l.minLevel)
	}
}

func TestComponentLogger_Interface(t *testing.T) {
	cl := NewComponentLogger("debug")

	// Verify it implements the component-based interface by calling all methods
	cl.Debug("component", "debug msg", nil)
	cl.Info("component", "info msg", map[string]string{"key": "val"})
	cl.Warn("component", "warn msg", nil)
	cl.Error("component", "error msg", map[string]string{"err": "test"})
	cl.Log("component", "info", "log msg", nil)
}

func TestComponentLogger_LevelFiltering(t *testing.T) {
	cl := NewComponentLogger("warn")
	// At warn level, debug and info should be filtered
	if cl.Logger.ShouldLog(DEBUG) {
		t.Error("DEBUG should be filtered at warn level")
	}
	if cl.Logger.ShouldLog(INFO) {
		t.Error("INFO should be filtered at warn level")
	}
	if !cl.Logger.ShouldLog(WARN) {
		t.Error("WARN should pass at warn level")
	}
}

func TestLogger_LogLevelString(t *testing.T) {
	tests := []struct {
		level LogLevel
		want  string
	}{
		{DEBUG, "DEBUG"},
		{INFO, "INFO"},
		{WARN, "WARN"},
		{ERROR, "ERROR"},
	}
	for _, tt := range tests {
		if got := tt.level.String(); got != tt.want {
			t.Errorf("%v.String() = %q, want %q", tt.level, got, tt.want)
		}
	}
}

func TestLogger_EmitContainsLevel(t *testing.T) {
	// Use a custom logger that captures output
	var buf strings.Builder
	l := New("myservice")
	l.logger.SetOutput(&buf)
	l.SetLevel("debug")

	l.Info("hello from info")
	if !strings.Contains(buf.String(), "INFO") {
		t.Errorf("expected INFO in output, got: %s", buf.String())
	}

	buf.Reset()
	l.Debug("hello from debug")
	if !strings.Contains(buf.String(), "DEBUG") {
		t.Errorf("expected DEBUG in output, got: %s", buf.String())
	}
}
