package accesscontrol

import (
	"context"
	"testing"

	"github.com/grafana/grafana/pkg/infra/db"
	"github.com/grafana/grafana/pkg/services/annotations"
	"github.com/grafana/grafana/pkg/services/dashboards"
	"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
	"github.com/grafana/grafana/pkg/services/featuremgmt"
	"github.com/grafana/grafana/pkg/services/search/model"
	"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
	"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
	"github.com/grafana/grafana/pkg/services/user"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/tests/testsuite"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

func TestMain(m *testing.M) {
	testsuite.Run(m)
}
func TestDashboardsWithVisibleAnnotations(t *testing.T) {
	store := db.InitTestDB(t)
	cfg := setting.NewCfg()

	user := &user.SignedInUser{
		OrgID: 1,
	}

	// Create permission filters
	p1 := permissions.NewAccessControlDashboardPermissionFilter(user, dashboardaccess.PERMISSION_VIEW, searchstore.TypeAnnotation, featuremgmt.WithFeatures(), true, store.GetDialect(), cfg.MaxNestedFolderDepth)
	p2 := searchstore.OrgFilter{OrgId: 1}

	// If DashboardUID is provided, it should be added as a filter
	p3 := searchstore.DashboardFilter{UIDs: []string{"uid1"}}

	dashSvc := &dashboards.FakeDashboardService{}

	// First call, without DashboardUID
	queryNoDashboardUID := &dashboards.FindPersistedDashboardsQuery{
		OrgId:        1,
		SignedInUser: user,
		Type:         "dash-annotation",
		Limit:        int64(100),
		Page:         int64(1),
		Filters: []any{
			p1,
			p2,
		},
	}
	dashSvc.On("SearchDashboards", mock.Anything, queryNoDashboardUID).Return(model.HitList{
		&model.Hit{UID: "uid1", ID: 101},
		&model.Hit{UID: "uid2", ID: 102},
	}, nil)

	// Second call, with DashboardUID filter
	queryWithDashboardUID := &dashboards.FindPersistedDashboardsQuery{
		OrgId:        1,
		SignedInUser: user,
		Type:         "dash-annotation",
		Limit:        int64(100),
		Page:         int64(1),
		Filters: []any{
			p1,
			p2,
			// This filter should be added on second call
			p3,
		},
		DashboardUIDs: []string{"uid1"},
	}

	dashSvc.On("SearchDashboards", mock.Anything, queryWithDashboardUID).Return(model.HitList{
		&model.Hit{UID: "uid1", ID: 101},
	}, nil)

	// Create auth service
	authz := &AuthService{
		db:                        store,
		features:                  featuremgmt.WithFeatures(),
		dashSvc:                   dashSvc,
		searchDashboardsPageLimit: 100,
		maxDepth:                  cfg.MaxNestedFolderDepth,
	}

	// First call without DashboardUID
	result, err := authz.dashboardsWithVisibleAnnotations(context.Background(), annotations.ItemQuery{
		SignedInUser: user,
		OrgID:        1,
		Page:         1,
	})
	assert.NoError(t, err)
	// Should return two dashboards
	assert.Equal(t, map[string]int64{"uid1": 101, "uid2": 102}, result)
	// Ensure SearchDashboards was called with correct query
	dashSvc.AssertCalled(t, "SearchDashboards", mock.Anything, queryNoDashboardUID)

	// Second call with DashboardUID
	result, err = authz.dashboardsWithVisibleAnnotations(context.Background(), annotations.ItemQuery{
		SignedInUser: user,
		OrgID:        1,
		Page:         1,
		DashboardUID: "uid1",
	})
	assert.NoError(t, err)
	// Should only return one dashboard
	assert.Equal(t, map[string]int64{"uid1": 101}, result)
	// Ensure SearchDashboards was called with correct query (including DashboardUID filter)
	dashSvc.AssertCalled(t, "SearchDashboards", mock.Anything, queryWithDashboardUID)
}
