package middleware

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

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/kernel/pkg/auth"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/shared/pkg/constants"
)

type mutableSystemPermissionProvider struct {
	mu    sync.Mutex
	grant bool
	calls int
}

func (p *mutableSystemPermissionProvider) GetEffectiveSystemPermissions(context.Context, string, string) (map[string]bool, error) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.calls++
	return map[string]bool{"ManageUsers": p.grant}, nil
}

func (p *mutableSystemPermissionProvider) GetEffectiveCustomPermissions(context.Context, string, string) (map[string]bool, error) {
	return map[string]bool{}, nil
}

func (p *mutableSystemPermissionProvider) setGrant(grant bool) {
	p.mu.Lock()
	p.grant = grant
	p.mu.Unlock()
}

func (p *mutableSystemPermissionProvider) callCount() int {
	p.mu.Lock()
	defer p.mu.Unlock()
	return p.calls
}

func TestPermissionCacheInvalidatorRevokesSystemPermissionBeforeNextRequest(t *testing.T) {
	gin.SetMode(gin.TestMode)
	provider := &mutableSystemPermissionProvider{grant: true}
	cache := NewPermissionCache(time.Hour)
	svc := &permissions.PermissionService{}
	svc.SetCacheInvalidator(cache)

	router := gin.New()
	router.Use(func(c *gin.Context) {
		c.Set(constants.ContextKeyUser, auth.UserSession{ID: "user-1", ProfileId: "profile-1"})
		c.Next()
	})
	router.Use(PermissionLoader(provider, cache, nil))
	router.GET("/product/notifications", func(c *gin.Context) {
		perms, _ := c.Get(constants.ContextKeySystemPermissions)
		granted := false
		if permissionMap, ok := perms.(map[string]bool); ok {
			granted = permissionMap["ManageUsers"]
		}
		c.JSON(http.StatusOK, gin.H{"granted": granted})
	})

	serve := func() string {
		recorder := httptest.NewRecorder()
		request := httptest.NewRequest(http.MethodGet, "/product/notifications", nil)
		router.ServeHTTP(recorder, request)
		if recorder.Code != http.StatusOK {
			t.Fatalf("status = %d, want 200", recorder.Code)
		}
		return recorder.Body.String()
	}

	if body := serve(); body != "{\"granted\":true}" {
		t.Fatalf("initial permission body = %s", body)
	}
	provider.setGrant(false)
	if body := serve(); body != "{\"granted\":true}" {
		t.Fatalf("pre-invalidation cache body = %s, want cached grant", body)
	}
	if calls := provider.callCount(); calls != 1 {
		t.Fatalf("provider calls before invalidation = %d, want 1", calls)
	}

	svc.InvalidateUserCache("user-1")
	if body := serve(); body != "{\"granted\":false}" {
		t.Fatalf("post-revocation permission body = %s, want revoked grant", body)
	}
	if calls := provider.callCount(); calls != 2 {
		t.Fatalf("provider calls after invalidation = %d, want 2", calls)
	}
}
