package middleware

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/user-management-system/internal/security"
)

func init() {
	gin.SetMode(gin.TestMode)
}

// newTestEngine 用给定的 IPFilterMiddleware 构建一个最简 Gin 引擎，
// 注册一个 GET /ping 路由，返回 client_ip 值。
func newTestEngine(f *security.IPFilter) *gin.Engine {
	engine := gin.New()
	engine.Use(NewIPFilterMiddleware(f, IPFilterConfig{}).Filter())
	engine.GET("/ping", func(c *gin.Context) {
		ip, _ := c.Get("client_ip")
		c.JSON(http.StatusOK, gin.H{"ip": ip})
	})
	return engine
}

// doRequest 发送 GET /ping，返回响应码和响应 body map。
func doRequest(engine *gin.Engine, remoteAddr, xff, xri string) (int, map[string]interface{}) {
	req := httptest.NewRequest(http.MethodGet, "/ping", nil)
	req.RemoteAddr = remoteAddr
	if xff != "" {
		req.Header.Set("X-Forwarded-For", xff)
	}
	if xri != "" {
		req.Header.Set("X-Real-IP", xri)
	}
	w := httptest.NewRecorder()
	engine.ServeHTTP(w, req)

	var body map[string]interface{}
	_ = json.Unmarshal(w.Body.Bytes(), &body)
	return w.Code, body
}

// ---------- 黑名单拦截 ----------

func TestIPFilter_BlockedIP_Returns403(t *testing.T) {
	f := security.NewIPFilter()
	_ = f.AddToBlacklist("1.2.3.4", "测试封禁", 0)

	engine := newTestEngine(f)
	code, body := doRequest(engine, "1.2.3.4:9999", "", "")

	if code != http.StatusForbidden {
		t.Fatalf("期望 403，实际 %d", code)
	}
	msg, _ := body["message"].(string)
	if msg == "" {
		t.Error("期望 body 中包含 message 字段")
	}
}

func TestIPFilter_NonBlockedIP_Returns200(t *testing.T) {
	f := security.NewIPFilter()
	_ = f.AddToBlacklist("9.9.9.9", "其他 IP", 0)

	engine := newTestEngine(f)
	code, _ := doRequest(engine, "1.2.3.4:9999", "", "")

	if code != http.StatusOK {
		t.Fatalf("期望 200，实际 %d", code)
	}
}

func TestIPFilter_EmptyBlacklist_AllPass(t *testing.T) {
	f := security.NewIPFilter()
	engine := newTestEngine(f)

	for _, ip := range []string{"1.1.1.1:80", "8.8.8.8:443", "203.0.113.5:1234"} {
		code, _ := doRequest(engine, ip, "", "")
		if code != http.StatusOK {
			t.Errorf("IP %s 应通过，实际 %d", ip, code)
		}
	}
}

// ---------- 白名单豁免 ----------

func TestIPFilter_WhitelistOverridesBlacklist(t *testing.T) {
	f := security.NewIPFilter()
	_ = f.AddToBlacklist("5.5.5.5", "封禁测试", 0)
	_ = f.AddToWhitelist("5.5.5.5", "白名单豁免")

	engine := newTestEngine(f)
	// 白名单优先，应通过
	code, _ := doRequest(engine, "5.5.5.5:8080", "", "")
	if code != http.StatusOK {
		t.Fatalf("白名单 IP 应返回 200，实际 %d", code)
	}
}

// ---------- CIDR 黑名单 ----------

func TestIPFilter_CIDRBlacklist(t *testing.T) {
	f := security.NewIPFilter()
	_ = f.AddToBlacklist("10.10.10.0/24", "封禁整段 CIDR", 0)

	engine := newTestEngine(f)

	// 在 CIDR 范围内，应被封
	code, _ := doRequest(engine, "10.10.10.55:1234", "", "")
	if code != http.StatusForbidden {
		t.Fatalf("CIDR 内 IP 应返回 403，实际 %d", code)
	}

	// 不在 CIDR 范围内，应通过
	code2, _ := doRequest(engine, "10.10.11.1:1234", "", "")
	if code2 != http.StatusOK {
		t.Fatalf("CIDR 外 IP 应返回 200，实际 %d", code2)
	}
}

// ---------- 过期规则 ----------

func TestIPFilter_ExpiredRule_Passes(t *testing.T) {
	f := security.NewIPFilter()
	// 封禁 1 纳秒，几乎立即过期
	_ = f.AddToBlacklist("7.7.7.7", "即将过期", time.Nanosecond)
	time.Sleep(2 * time.Millisecond)

	engine := newTestEngine(f)
	code, _ := doRequest(engine, "7.7.7.7:80", "", "")
	if code != http.StatusOK {
		t.Fatalf("过期规则不应拦截，期望 200，实际 %d", code)
	}
}

// ---------- client_ip 注入 ----------

func TestIPFilter_ClientIPSetInContext(t *testing.T) {
	f := security.NewIPFilter()
	engine := newTestEngine(f)

	code, body := doRequest(engine, "203.0.113.1:9000", "", "")
	if code != http.StatusOK {
		t.Fatalf("期望 200，实际 %d", code)
	}
	ip, _ := body["ip"].(string)
	if ip != "203.0.113.1" {
		t.Errorf("期望 client_ip=203.0.113.1，实际 %q", ip)
	}
}

// ---------- realIP 提取逻辑 ----------

// TestRealIP_XForwardedFor_PublicIP 公网 X-Forwarded-For 取第一个非内网 IP
func TestRealIP_XForwardedFor_PublicIP(t *testing.T) {
	f := security.NewIPFilter()
	engine := newTestEngine(f)

	// X-Forwarded-For: 203.0.113.10, 192.168.1.1（代理内网）
	code, body := doRequest(engine, "192.168.1.1:80", "203.0.113.10, 192.168.1.1", "")
	if code != http.StatusOK {
		t.Fatalf("期望 200，实际 %d", code)
	}
	ip, _ := body["ip"].(string)
	if ip != "203.0.113.10" {
		t.Errorf("期望从 X-Forwarded-For 取公网 IP，实际 %q", ip)
	}
}

// TestRealIP_XForwardedFor_AllPrivate 全内网则取第一个
func TestRealIP_XForwardedFor_AllPrivate(t *testing.T) {
	f := security.NewIPFilter()
	engine := newTestEngine(f)

	code, body := doRequest(engine, "10.0.0.2:80", "192.168.0.5, 10.0.0.1", "")
	if code != http.StatusOK {
		t.Fatalf("期望 200，实际 %d", code)
	}
	ip, _ := body["ip"].(string)
	if ip != "192.168.0.5" {
		t.Errorf("全内网时应取第一个，实际 %q", ip)
	}
}

// TestRealIP_XRealIP_Fallback X-Forwarded-For 缺失时使用 X-Real-IP
func TestRealIP_XRealIP_Fallback(t *testing.T) {
	f := security.NewIPFilter()
	engine := newTestEngine(f)

	code, body := doRequest(engine, "192.168.1.1:80", "", "203.0.113.20")
	if code != http.StatusOK {
		t.Fatalf("期望 200，实际 %d", code)
	}
	ip, _ := body["ip"].(string)
	if ip != "203.0.113.20" {
		t.Errorf("期望 X-Real-IP 回退，实际 %q", ip)
	}
}

// TestRealIP_RemoteAddr_Fallback 都无 header 时用 RemoteAddr
func TestRealIP_RemoteAddr_Fallback(t *testing.T) {
	f := security.NewIPFilter()
	engine := newTestEngine(f)

	code, body := doRequest(engine, "203.0.113.99:12345", "", "")
	if code != http.StatusOK {
		t.Fatalf("期望 200，实际 %d", code)
	}
	ip, _ := body["ip"].(string)
	if ip != "203.0.113.99" {
		t.Errorf("期望 RemoteAddr 回退，实际 %q", ip)
	}
}

// ---------- GetFilter ----------

func TestIPFilterMiddleware_GetFilter(t *testing.T) {
	f := security.NewIPFilter()
	mw := NewIPFilterMiddleware(f, IPFilterConfig{})
	if mw.GetFilter() != f {
		t.Error("GetFilter 应返回同一个 IPFilter 实例")
	}
}

// ---------- 并发安全 ----------

func TestIPFilter_ConcurrentRequests(t *testing.T) {
	f := security.NewIPFilter()
	_ = f.AddToBlacklist("66.66.66.66", "并发测试封禁", 0)
	engine := newTestEngine(f)

	done := make(chan struct{}, 20)
	for i := 0; i < 20; i++ {
		go func(i int) {
			defer func() { done <- struct{}{} }()
			var remoteAddr string
			if i%2 == 0 {
				remoteAddr = "66.66.66.66:9000"
			} else {
				remoteAddr = "1.2.3.4:9000"
			}
			code, _ := doRequest(engine, remoteAddr, "", "")
			if i%2 == 0 && code != http.StatusForbidden {
				t.Errorf("并发：封禁 IP 应返回 403，实际 %d", code)
			} else if i%2 != 0 && code != http.StatusOK {
				t.Errorf("并发：正常 IP 应返回 200，实际 %d", code)
			}
		}(i)
	}
	for i := 0; i < 20; i++ {
		<-done
	}
}
