package agent

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

	"github.com/gin-gonic/gin"

	"security-center-backend/internal/config"
	"security-center-backend/internal/middleware"
	"security-center-backend/internal/pkg/response"
	"security-center-backend/internal/service/jwt"
)

// stubAuth simulates the JWT auth middleware: requests carrying
// X-Test-Permissions get a claims context, others are rejected with the
// standard HTTP 200 + body code 401 envelope.
func stubAuth(c *gin.Context) {
	raw := c.GetHeader("X-Test-Permissions")
	if raw == "" {
		response.Error(c, http.StatusUnauthorized, "not authenticated")
		c.Abort()
		return
	}
	permissions := make([]string, 0)
	for _, part := range strings.Split(raw, ",") {
		if permission := strings.TrimSpace(part); permission != "" {
			permissions = append(permissions, permission)
		}
	}
	c.Set("current_user", &jwt.Claims{UserID: 1, Email: "agent-test@example.com", Permissions: permissions})
	c.Next()
}

func newTestEngine() *gin.Engine {
	gin.SetMode(gin.TestMode)
	engine := gin.New()
	h := New(nil, config.AgentConfig{Enabled: true, KeyPrefix: "sk-agent-"})
	group := engine.Group("/api/v1/agent/api-keys")
	group.Use(stubAuth, middleware.Permission("agent:manage_own_keys"))
	h.RegisterRoutes(group)
	return engine
}

func doRequest(t *testing.T, engine *gin.Engine, method, path, permissions string) *httptest.ResponseRecorder {
	t.Helper()
	req := httptest.NewRequest(method, path, nil)
	if permissions != "" {
		req.Header.Set("X-Test-Permissions", permissions)
	}
	w := httptest.NewRecorder()
	engine.ServeHTTP(w, req)
	if w.Code != http.StatusOK {
		t.Fatalf("http status = %d, want 200 (envelope convention); body=%s", w.Code, w.Body.String())
	}
	return w
}

func assertBodyCode(t *testing.T, w *httptest.ResponseRecorder, want int) {
	t.Helper()
	var body struct {
		Code int `json:"code"`
	}
	if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil {
		t.Fatalf("decode API response: %v; body=%s", err, w.Body.String())
	}
	if body.Code != want {
		t.Fatalf("body code = %d, want %d; body=%s", body.Code, want, w.Body.String())
	}
}

func TestRegisterRoutesRegistersAllSixEndpoints(t *testing.T) {
	engine := newTestEngine()

	registered := make(map[string]bool)
	for _, route := range engine.Routes() {
		registered[route.Method+" "+route.Path] = true
	}
	for _, key := range []string{
		"POST /api/v1/agent/api-keys",
		"GET /api/v1/agent/api-keys",
		"GET /api/v1/agent/api-keys/:id",
		"DELETE /api/v1/agent/api-keys/:id",
		"POST /api/v1/agent/api-keys/:id/rotate",
		"GET /api/v1/agent/api-keys/:id/usage",
	} {
		if !registered[key] {
			t.Fatalf("route %q not registered; routes=%v", key, registered)
		}
	}
}

func TestUnauthenticatedGets401BodyCode(t *testing.T) {
	engine := newTestEngine()
	w := doRequest(t, engine, http.MethodGet, "/api/v1/agent/api-keys", "")
	assertBodyCode(t, w, http.StatusUnauthorized)
}

func TestUserWithoutPermissionGets403BodyCode(t *testing.T) {
	engine := newTestEngine()
	w := doRequest(t, engine, http.MethodGet, "/api/v1/agent/api-keys", "caasm:view_assets")
	assertBodyCode(t, w, http.StatusForbidden)
}
