package rest

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

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/internal/application/services"
	"github.com/nexuscrm/backend/internal/features/automation/autonumber"
	"github.com/nexuscrm/backend/internal/interfaces/middleware"
	"github.com/nexuscrm/backend/pkg/auth"
	"github.com/nexuscrm/shared/pkg/constants"
)

// TestAdminAutoNumberHandler_RefreshMetadata_Happy — Round-6 R5-H1.
// A POST to the handler with a populated MetadataSource refreshes and
// returns 200 with ruleCount in the body.
func TestAdminAutoNumberHandler_RefreshMetadata_Happy(t *testing.T) {
	gin.SetMode(gin.TestMode)

	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	rows := sqlmock.NewRows([]string{
		"Id", "ApiName", "ApiName2", "AutoNumberFormat",
		"Scope", "Start", "TokenRefs",
	}).AddRow("fld-1", "Account", "AccountNumber", "A-{NNNN}", "global", int64(1), "")
	mock.ExpectQuery(`SELECT f\.Id`).WillReturnRows(rows)

	src := autonumber.NewMetadataSource(db, nil)
	// Ensure first refresh loads something so ListRules is non-zero.
	if err := src.Refresh(context.Background()); err != nil {
		t.Fatalf("initial Refresh: %v", err)
	}

	// Handler will trigger a *second* Refresh — expect a second query.
	rows2 := sqlmock.NewRows([]string{
		"Id", "ApiName", "ApiName2", "AutoNumberFormat",
		"Scope", "Start", "TokenRefs",
	}).AddRow("fld-1", "Account", "AccountNumber", "A-{NNNN}", "global", int64(1), "")
	mock.ExpectQuery(`SELECT f\.Id`).WillReturnRows(rows2)

	svcMgr := &services.ServiceManager{AutoNumberMetadata: src}
	h := NewAdminAutoNumberHandler(svcMgr)

	r := gin.New()
	r.POST("/admin/autonumber/refresh-metadata", h.RefreshMetadata)

	req := httptest.NewRequest(http.MethodPost, "/admin/autonumber/refresh-metadata", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status = %d, body = %s", w.Code, w.Body.String())
	}
	body := w.Body.String()
	if body == "" {
		t.Fatal("empty body")
	}
	if e := mock.ExpectationsWereMet(); e != nil {
		t.Errorf("unmet expectations: %v", e)
	}
}

// TestAdminAutoNumberHandler_RefreshMetadata_NilSource — defensive path.
// If svcMgr.AutoNumberMetadata is nil (should not happen in production),
// return 503 rather than panic.
func TestAdminAutoNumberHandler_RefreshMetadata_NilSource(t *testing.T) {
	gin.SetMode(gin.TestMode)

	svcMgr := &services.ServiceManager{}
	h := NewAdminAutoNumberHandler(svcMgr)

	r := gin.New()
	r.POST("/admin/autonumber/refresh-metadata", h.RefreshMetadata)

	req := httptest.NewRequest(http.MethodPost, "/admin/autonumber/refresh-metadata", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	if w.Code != http.StatusServiceUnavailable {
		t.Fatalf("status = %d, want 503; body = %s", w.Code, w.Body.String())
	}
}

// TestAdminAutoNumberHandler_MiddlewareAuth proves the refresh endpoint is
// gated by the CustomizeApplication permission boundary — i.e. when mounted
// behind the same middleware used in cmd/server/routes.go (line 336-346),
// the endpoint refuses callers that lack the permission and only permits
// admins. V2-Codex-R6 flagged the existing tests as handler-isolated (raw
// r.POST) and unable to detect an accidental removal of the RequirePermission
// middleware on the /admin/autonumber/refresh-metadata route.
//
// Scenarios:
//   1) No user in context   -> 403 (RequirePermission fails closed; in the
//                              full stack RequireAuth would 401 first, but
//                              either way the endpoint is not reachable).
//   2) Standard user w/o CustomizeApplication -> 403 MISSING_PERMISSION.
//   3) System admin (super-user)              -> 200, refresh invoked.
func TestAdminAutoNumberHandler_MiddlewareAuth(t *testing.T) {
	gin.SetMode(gin.TestMode)

	// setUser installs a gin middleware that seeds ContextKeyUser and, for
	// non-super users, also seeds ContextKeySystemPermissions — matching
	// what PermissionLoader would install in the real middleware chain.
	setUser := func(user *auth.UserSession, systemPerms map[string]bool) gin.HandlerFunc {
		return func(c *gin.Context) {
			if user != nil {
				c.Set(constants.ContextKeyUser, *user)
			}
			if systemPerms != nil {
				c.Set(constants.ContextKeySystemPermissions, systemPerms)
			}
			c.Next()
		}
	}

	newRouter := func(seed gin.HandlerFunc, svcMgr *services.ServiceManager) *gin.Engine {
		h := NewAdminAutoNumberHandler(svcMgr)
		r := gin.New()
		grp := r.Group("")
		grp.Use(seed, middleware.RequirePermission(constants.PermCustomizeApplication))
		grp.POST("/admin/autonumber/refresh-metadata", h.RefreshMetadata)
		return r
	}

	doPOST := func(r *gin.Engine) *httptest.ResponseRecorder {
		req := httptest.NewRequest(http.MethodPost, "/admin/autonumber/refresh-metadata", nil)
		w := httptest.NewRecorder()
		r.ServeHTTP(w, req)
		return w
	}

	t.Run("unauthenticated_is_denied", func(t *testing.T) {
		r := newRouter(setUser(nil, nil), &services.ServiceManager{})
		w := doPOST(r)
		if w.Code != http.StatusForbidden && w.Code != http.StatusUnauthorized {
			t.Fatalf("status = %d, want 401 or 403; body = %s", w.Code, w.Body.String())
		}
	})

	t.Run("non_admin_without_permission_is_denied", func(t *testing.T) {
		user := &auth.UserSession{
			ID:        "user-1",
			Name:      "Standard User",
			Email:     "std@example.com",
			ProfileId: constants.ProfileStandardUser,
		}
		// Empty perms map — user authenticated but lacks CustomizeApplication.
		r := newRouter(setUser(user, map[string]bool{}), &services.ServiceManager{})
		w := doPOST(r)
		if w.Code != http.StatusForbidden {
			t.Fatalf("status = %d, want 403; body = %s", w.Code, w.Body.String())
		}
	})

	t.Run("admin_with_permission_succeeds_and_triggers_refresh", func(t *testing.T) {
		db, mock, err := sqlmock.New()
		if err != nil {
			t.Fatalf("sqlmock.New: %v", err)
		}
		defer db.Close()

		rows := sqlmock.NewRows([]string{
			"Id", "ApiName", "ApiName2", "AutoNumberFormat",
			"Scope", "Start", "TokenRefs",
		}).AddRow("fld-1", "Account", "AccountNumber", "A-{NNNN}", "global", int64(1), "")
		mock.ExpectQuery(`SELECT f\.Id`).WillReturnRows(rows)

		src := autonumber.NewMetadataSource(db, nil)
		svcMgr := &services.ServiceManager{AutoNumberMetadata: src}

		admin := &auth.UserSession{
			ID:        "admin-1",
			Name:      "System Admin",
			Email:     "admin@example.com",
			ProfileId: constants.ProfileSystemAdmin, // super-user bypass
		}
		// Super users bypass the RequirePermission perms-map lookup, so we
		// do not need to seed system permissions — match production semantics.
		r := newRouter(setUser(admin, nil), svcMgr)
		w := doPOST(r)
		if w.Code != http.StatusOK {
			t.Fatalf("status = %d, want 200; body = %s", w.Code, w.Body.String())
		}
		if e := mock.ExpectationsWereMet(); e != nil {
			t.Errorf("refresh did not query DB as expected: %v", e)
		}
	})
}
