package matcher

import (
	"testing"

	"github.com/valyala/fasthttp"
)

func TestLocationEngine_New(t *testing.T) {
	engine := NewLocationEngine()
	if engine.exactMatchers == nil {
		t.Error("exactMatchers should be initialized")
	}
	if engine.prefixPriorityTree == nil {
		t.Error("prefixPriorityTree should be initialized")
	}
	if engine.prefixTree == nil {
		t.Error("prefixTree should be initialized")
	}
	if engine.regexMatchers == nil {
		t.Error("regexMatchers should be initialized")
	}
	if engine.namedMatchers == nil {
		t.Error("namedMatchers should be initialized")
	}
	if engine.registeredPaths == nil {
		t.Error("registeredPaths should be initialized")
	}
}

func TestLocationEngine_AddExact(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	err := engine.AddExact("/api", handler, false)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	result := engine.Match([]byte("/api"))
	if result == nil {
		t.Fatal("expected match")
	}
	if result.Path != "/api" {
		t.Errorf("expected path '/api', got %s", result.Path)
	}
	if result.LocationType != LocationTypeExact {
		t.Errorf("expected location type '%s', got %s", LocationTypeExact, result.LocationType)
	}
}

func TestLocationEngine_AddExact_AfterInitialized(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	engine.MarkInitialized()
	err := engine.AddExact("/api", handler, false)
	if err == nil {
		t.Error("expected error after initialized")
	}
}

func TestLocationEngine_AddExact_PathConflict(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	engine.AddExact("/api", handler, false)
	err := engine.AddExact("/api", handler, false)
	if err == nil {
		t.Error("expected conflict error")
	}
}

func TestLocationEngine_AddPrefixPriority(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	err := engine.AddPrefixPriority("/static", handler, false)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	result := engine.Match([]byte("/static/css/style.css"))
	if result == nil {
		t.Fatal("expected match")
	}
	if result.Path != "/static" {
		t.Errorf("expected path '/static', got %s", result.Path)
	}
	if result.LocationType != LocationTypePrefixPriority {
		t.Errorf("expected location type '%s', got %s", LocationTypePrefixPriority, result.LocationType)
	}
}

func TestLocationEngine_AddPrefixPriority_AfterInitialized(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	engine.MarkInitialized()
	err := engine.AddPrefixPriority("/static", handler, false)
	if err == nil {
		t.Error("expected error after initialized")
	}
}

func TestLocationEngine_AddPrefix(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	err := engine.AddPrefix("/api", handler, false)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	result := engine.Match([]byte("/api/users"))
	if result == nil {
		t.Fatal("expected match")
	}
	if result.LocationType != LocationTypePrefix {
		t.Errorf("expected location type '%s', got %s", LocationTypePrefix, result.LocationType)
	}
}

func TestLocationEngine_AddPrefix_AfterInitialized(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	engine.MarkInitialized()
	err := engine.AddPrefix("/api", handler, false)
	if err == nil {
		t.Error("expected error after initialized")
	}
}

func TestLocationEngine_AddRegex(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	err := engine.AddRegex(`\.php$`, handler, false, false)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	result := engine.Match([]byte("/index.php"))
	if result == nil {
		t.Fatal("expected match")
	}
	if result.LocationType != LocationTypeRegex {
		t.Errorf("expected location type '%s', got %s", LocationTypeRegex, result.LocationType)
	}
}

func TestLocationEngine_AddRegex_CaseInsensitive(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	err := engine.AddRegex(`(?i)\.php$`, handler, true, false)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	result := engine.Match([]byte("/index.PHP"))
	if result == nil {
		t.Fatal("expected match for case insensitive")
	}
	if result.LocationType != LocationTypeRegexCaseless {
		t.Errorf("expected location type '%s', got %s", LocationTypeRegexCaseless, result.LocationType)
	}
}

func TestLocationEngine_AddRegex_InvalidPattern(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	err := engine.AddRegex(`[invalid`, handler, false, false)
	if err == nil {
		t.Error("expected error for invalid regex")
	}
}

func TestLocationEngine_AddRegex_Captures(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	err := engine.AddRegex(`^/user/(?P<id>[0-9]+)$`, handler, false, false)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	result := engine.Match([]byte("/user/123"))
	if result == nil {
		t.Fatal("expected match")
	}
	if result.Captures == nil {
		t.Fatal("expected captures")
	}
	if result.Captures["id"] != "123" {
		t.Errorf("expected id=123, got %s", result.Captures["id"])
	}
}

func TestLocationEngine_Match_PriorityOrder(t *testing.T) {
	engine := NewLocationEngine()
	hExact := func(ctx *fasthttp.RequestCtx) {}
	hPrefixPriority := func(ctx *fasthttp.RequestCtx) {}
	hRegex := func(ctx *fasthttp.RequestCtx) {}
	hPrefix := func(ctx *fasthttp.RequestCtx) {}

	// All match "/api/path"
	engine.AddExact("/api/path", hExact, false)
	engine.AddPrefixPriority("/api", hPrefixPriority, false)
	engine.AddRegex(`^/api/`, hRegex, false, false)
	engine.AddPrefix("/api/path", hPrefix, false)

	// Exact should win (priority 1)
	result := engine.Match([]byte("/api/path"))
	if result == nil {
		t.Fatal("expected match")
	}
	if result.LocationType != LocationTypeExact {
		t.Errorf("expected exact match to win, got %s", result.LocationType)
	}
}

func TestLocationEngine_Match_PrefixPriorityBeatsRegex(t *testing.T) {
	engine := NewLocationEngine()
	hPrefixPriority := func(ctx *fasthttp.RequestCtx) {}
	hRegex := func(ctx *fasthttp.RequestCtx) {}

	// No exact match for this path
	engine.AddPrefixPriority("/static", hPrefixPriority, false)
	engine.AddRegex(`\.css$`, hRegex, false, false)

	// ^~ prefix priority should beat regex
	result := engine.Match([]byte("/static/style.css"))
	if result == nil {
		t.Fatal("expected match")
	}
	if result.LocationType != LocationTypePrefixPriority {
		t.Errorf("expected prefix_priority to win over regex, got %s", result.LocationType)
	}
}

func TestLocationEngine_Match_RegexBeatsPrefix(t *testing.T) {
	engine := NewLocationEngine()
	hRegex := func(ctx *fasthttp.RequestCtx) {}
	hPrefix := func(ctx *fasthttp.RequestCtx) {}

	engine.AddRegex(`\.php$`, hRegex, false, false)
	engine.AddPrefix("/", hPrefix, false)

	// Regex should win over plain prefix
	result := engine.Match([]byte("/index.php"))
	if result == nil {
		t.Fatal("expected match")
	}
	if result.LocationType != LocationTypeRegex {
		t.Errorf("expected regex to win over prefix, got %s", result.LocationType)
	}
}

func TestLocationEngine_Match_FallbackToPrefix(t *testing.T) {
	engine := NewLocationEngine()
	hPrefix := func(ctx *fasthttp.RequestCtx) {}

	engine.AddPrefix("/api", hPrefix, false)

	result := engine.Match([]byte("/api/users"))
	if result == nil {
		t.Fatal("expected prefix match")
	}
	if result.LocationType != LocationTypePrefix {
		t.Errorf("expected prefix match, got %s", result.LocationType)
	}
}

func TestLocationEngine_Match_NoMatch(t *testing.T) {
	engine := NewLocationEngine()
	hPrefix := func(ctx *fasthttp.RequestCtx) {}

	engine.AddPrefix("/api", hPrefix, false)

	result := engine.Match([]byte("/other"))
	if result != nil {
		t.Errorf("expected no match, got %+v", result)
	}
}

func TestLocationEngine_Match_EmptyString(t *testing.T) {
	engine := NewLocationEngine()
	hPrefix := func(ctx *fasthttp.RequestCtx) {}

	engine.AddPrefix("/api", hPrefix, false)

	result := engine.Match([]byte(""))
	if result != nil {
		t.Errorf("expected no match for empty string, got %+v", result)
	}
}

func TestLocationEngine_Match_UnicodePath(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	engine.AddPrefixPriority("/文档", handler, false)

	result := engine.Match([]byte("/文档/报告"))
	if result == nil {
		t.Fatal("expected unicode prefix match")
	}
	if result.Path != "/文档" {
		t.Errorf("expected '/文档', got %s", result.Path)
	}
}

func TestLocationEngine_MarkInitialized(t *testing.T) {
	engine := NewLocationEngine()
	handler := func(ctx *fasthttp.RequestCtx) {}

	engine.AddPrefix("/api", handler, false)
	engine.MarkInitialized()

	// All add methods should fail after initialized
	if engine.AddExact("/exact", handler, false) == nil {
		t.Error("AddExact should fail after initialized")
	}
	if engine.AddPrefixPriority("/pp", handler, false) == nil {
		t.Error("AddPrefixPriority should fail after initialized")
	}
	if engine.AddPrefix("/pre", handler, false) == nil {
		t.Error("AddPrefix should fail after initialized")
	}
	if engine.AddRegex(`test`, handler, false, false) == nil {
		t.Error("AddRegex should fail after initialized")
	}
	if engine.AddNamed("test", handler) == nil {
		t.Error("AddNamed should fail after initialized")
	}
}
