package middleware

import (
	"context"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"

	"github.com/sundynix/sundynix-gateway/internal/store"
)

// fakeRoleResolver 按 (tenant,user) 返回预置角色，用于隔离测试门控逻辑（不碰 DB）。
type fakeRoleResolver struct{ role string }

func (f fakeRoleResolver) MemberRole(context.Context, string, string) string { return f.role }

// 组装一条 /tasks 路由，模拟 RequireAuth+TenantContext 已注入 uid/tenant_id，再挂门控。
func roleGateEngine(uid, tenant, memberRole, minRole string) *gin.Engine {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		if uid != "" {
			c.Set(CtxUserID, uid)
		}
		if tenant != "" {
			c.Set(CtxTenantID, tenant)
		}
		c.Next()
	})
	r.POST("/tasks", RequireTenantRole(fakeRoleResolver{role: memberRole}, minRole),
		func(c *gin.Context) { c.String(http.StatusOK, "submitted") })
	return r
}

func TestRequireTenantRole_Gate(t *testing.T) {
	cases := []struct {
		name       string
		uid        string
		tenant     string
		memberRole string
		wantCode   int
	}{
		{"owner 放行", "u1", "t1", store.RoleOwner, http.StatusOK},
		{"admin 放行", "u1", "t1", store.RoleAdmin, http.StatusOK},
		{"member 放行", "u1", "t1", store.RoleMember, http.StatusOK},
		{"billing_admin 拦下(财务只读不能提交)", "u1", "t1", store.RoleBillingAdmin, http.StatusForbidden},
		{"viewer 拦下(只读不能提交)", "u1", "t1", store.RoleViewer, http.StatusForbidden},
		{"非成员(空角色) 拦下", "u1", "t1", "", http.StatusForbidden},
		{"未登录 401", "", "t1", store.RoleOwner, http.StatusUnauthorized},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			r := roleGateEngine(tc.uid, tc.tenant, tc.memberRole, store.RoleMember)
			w := httptest.NewRecorder()
			r.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/tasks", nil))
			if w.Code != tc.wantCode {
				t.Errorf("状态码=%d, 期望 %d（body=%s）", w.Code, tc.wantCode, w.Body.String())
			}
		})
	}
}

// 成员自管写路由用 ≥admin 门槛（薄 Web 面）：member 也只能看不能动。
func TestRequireTenantRole_AdminGate(t *testing.T) {
	cases := []struct {
		name       string
		memberRole string
		wantCode   int
	}{
		{"owner 放行", store.RoleOwner, http.StatusOK},
		{"admin 放行", store.RoleAdmin, http.StatusOK},
		{"member 拦下(成员管理要 admin)", store.RoleMember, http.StatusForbidden},
		{"viewer 拦下", store.RoleViewer, http.StatusForbidden},
		{"billing_admin 拦下", store.RoleBillingAdmin, http.StatusForbidden},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			r := roleGateEngine("u1", "t1", tc.memberRole, store.RoleAdmin)
			w := httptest.NewRecorder()
			r.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/tasks", nil))
			if w.Code != tc.wantCode {
				t.Errorf("状态码=%d, 期望 %d（body=%s）", w.Code, tc.wantCode, w.Body.String())
			}
		})
	}
}
