package navtreeimpl

import (
	"github.com/grafana/grafana/pkg/apimachinery/identity"
	"github.com/grafana/grafana/pkg/login/social"
	ac "github.com/grafana/grafana/pkg/services/accesscontrol"
	"github.com/grafana/grafana/pkg/services/accesscontrol/ssoutils"
	"github.com/grafana/grafana/pkg/services/cloudmigration"
	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
	"github.com/grafana/grafana/pkg/services/correlations"
	"github.com/grafana/grafana/pkg/services/featuremgmt"
	"github.com/grafana/grafana/pkg/services/navtree"
	"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol"
	"github.com/grafana/grafana/pkg/services/serviceaccounts"
	"github.com/grafana/grafana/pkg/setting"
)

// nolint: gocyclo
func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink, error) {
	var configNodes []*navtree.NavLink
	ctx := c.Req.Context()
	hasAccess := ac.HasAccess(s.accessControl, c)
	hasGlobalAccess := ac.HasGlobalAccess(s.accessControl, s.authnService, c)
	orgsAccessEvaluator := ac.EvalPermission(ac.ActionOrgsRead)
	authConfigUIAvailable := s.license.FeatureEnabled(social.SAMLProviderName) || s.cfg.LDAPAuthEnabled

	generalNodeLinks := []*navtree.NavLink{}
	if hasAccess(ac.OrgPreferencesAccessEvaluator) {
		generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
			Text:     "Default preferences",
			Id:       "org-settings",
			SubTitle: "Manage preferences across an organization",
			Icon:     "sliders-v-alt",
			Url:      s.cfg.AppSubURL + "/org",
		})
	}
	if hasAccess(ac.EvalPermission(ac.ActionSettingsRead, ac.ScopeSettingsAll)) {
		generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
			Text: "Settings", SubTitle: "View the settings defined in your Grafana config", Id: "server-settings", Url: s.cfg.AppSubURL + "/admin/settings", Icon: "sliders-v-alt",
		})
	}
	if hasGlobalAccess(orgsAccessEvaluator) {
		generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
			Text: "Organizations", SubTitle: "Isolated instances of Grafana running on the same server", Id: "global-orgs", Url: s.cfg.AppSubURL + "/admin/orgs", Icon: "building",
		})
	}
	if hasAccess(cloudmigration.MigrationAssistantAccess) && s.cfg.CloudMigration.Enabled {
		generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
			Text:     "Migrate to Grafana Cloud",
			Id:       "migrate-to-cloud",
			SubTitle: "Copy resources from your self-managed installation to a cloud stack",
			Url:      s.cfg.AppSubURL + "/admin/migrate-to-cloud",
		})
	}
	//nolint:staticcheck // not yet migrated to OpenFeature
	if c.HasRole(identity.RoleAdmin) &&
		(s.cfg.StackID == "" || // show OnPrem even when provisioning is disabled
			s.features.IsEnabledGlobally(featuremgmt.FlagProvisioning)) {
		generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
			Text:     "Provisioning",
			Id:       "provisioning",
			SubTitle: "View and manage your provisioning connections",
			Url:      s.cfg.AppSubURL + "/admin/provisioning",
		})
	}

	generalNode := &navtree.NavLink{
		Text:     "General",
		SubTitle: "Manage default preferences and settings across Grafana",
		Id:       navtree.NavIDCfgGeneral,
		Url:      s.cfg.AppSubURL + "/admin/general",
		Icon:     "shield",
		Children: generalNodeLinks,
	}

	if len(generalNode.Children) > 0 {
		configNodes = append(configNodes, generalNode)
	}

	pluginsNodeLinks := []*navtree.NavLink{}
	// FIXME: If plugin admin is disabled or externally managed, server admins still need to access the page, this is why
	// while we don't have a permissions for listing plugins the legacy check has to stay as a default
	if pluginaccesscontrol.ReqCanAdminPlugins(s.cfg)(c) || hasAccess(pluginaccesscontrol.AdminAccessEvaluator) {
		pluginsNodeLinks = append(pluginsNodeLinks, &navtree.NavLink{
			Text:     "Plugins",
			Id:       "plugins",
			SubTitle: "Extend the Grafana experience with plugins",
			Icon:     "plug",
			Url:      s.cfg.AppSubURL + "/plugins",
		})
	}
	if hasAccess(correlations.ConfigurationPageAccess) {
		pluginsNodeLinks = append(pluginsNodeLinks, &navtree.NavLink{
			Text:     "Correlations",
			Icon:     "gf-glue",
			SubTitle: "Add and configure correlations",
			Id:       "correlations",
			Url:      s.cfg.AppSubURL + "/datasources/correlations",
		})
	}

	//nolint:staticcheck // not yet migrated to OpenFeature
	if (s.cfg.Env == setting.Dev) || s.features.IsEnabled(ctx, featuremgmt.FlagEnableExtensionsAdminPage) && hasAccess(pluginaccesscontrol.AdminAccessEvaluator) {
		pluginsNodeLinks = append(pluginsNodeLinks, &navtree.NavLink{
			Text:     "Extensions",
			Icon:     "plug",
			SubTitle: "Extend the UI of plugins and Grafana",
			Id:       "extensions",
			Url:      s.cfg.AppSubURL + "/admin/extensions",
		})
	}

	pluginsNode := &navtree.NavLink{
		Text:     "Plugins and data",
		SubTitle: "Install plugins and define the relationships between data",
		Id:       navtree.NavIDCfgPlugins,
		Url:      s.cfg.AppSubURL + "/admin/plugins",
		Icon:     "shield",
		Children: pluginsNodeLinks,
	}

	if len(pluginsNode.Children) > 0 {
		configNodes = append(configNodes, pluginsNode)
	}

	accessNodeLinks := []*navtree.NavLink{}
	if hasAccess(ac.EvalAny(ac.EvalPermission(ac.ActionOrgUsersRead), ac.EvalPermission(ac.ActionUsersRead, ac.ScopeGlobalUsersAll))) {
		accessNodeLinks = append(accessNodeLinks, &navtree.NavLink{
			Text: "Users", SubTitle: "Manage users in Grafana", Id: "global-users", Url: s.cfg.AppSubURL + "/admin/users", Icon: "user",
		})
	}
	if hasAccess(ac.TeamsAccessEvaluator) {
		accessNodeLinks = append(accessNodeLinks, &navtree.NavLink{
			Text:     "Teams",
			Id:       "teams",
			SubTitle: "Groups of users that have common dashboard and permission needs",
			Icon:     "users-alt",
			Url:      s.cfg.AppSubURL + "/org/teams",
		})
	}
	if enableServiceAccount(s, c) {
		accessNodeLinks = append(accessNodeLinks, &navtree.NavLink{
			Text:     "Service accounts",
			Id:       "serviceaccounts",
			SubTitle: "Use service accounts to run automated workloads in Grafana",
			Icon:     "gf-service-account",
			Url:      s.cfg.AppSubURL + "/org/serviceaccounts",
		})
	}

	//nolint:staticcheck // not yet migrated to OpenFeature
	if s.license.FeatureEnabled("groupsync") &&
		s.features.IsEnabled(ctx, featuremgmt.FlagGroupAttributeSync) &&
		hasAccess(ac.EvalAny(
			ac.EvalPermission("groupsync.mappings:read"),
			ac.EvalPermission("groupsync.mappings:write"),
		)) {
		accessNodeLinks = append(accessNodeLinks, &navtree.NavLink{
			Text:     "External group sync",
			Id:       "groupsync",
			SubTitle: "Manage mappings of Identity Provider groups to Grafana Roles",
			Icon:     "",
			Url:      s.cfg.AppSubURL + "/admin/access/groupsync",
		})
	}

	usersNode := &navtree.NavLink{
		Text:     "Users and access",
		SubTitle: "Configure access for individual users, teams, and service accounts",
		Id:       navtree.NavIDCfgAccess,
		Url:      s.cfg.AppSubURL + "/admin/access",
		Icon:     "shield",
		Children: accessNodeLinks,
	}

	// Always append admin access as it's injected by grafana-auth-app.
	configNodes = append(configNodes, usersNode)

	if authConfigUIAvailable && hasAccess(ssoutils.EvalAuthenticationSettings(s.cfg)) ||
		hasAccess(ssoutils.OauthSettingsEvaluator(s.cfg)) {
		configNodes = append(configNodes, &navtree.NavLink{
			Text:      "Authentication",
			Id:        "authentication",
			SubTitle:  "Manage your auth settings and configure single sign-on",
			Icon:      "signin",
			IsSection: true,
			Url:       s.cfg.AppSubURL + "/admin/authentication",
		})
	}

	configNode := &navtree.NavLink{
		Id:         navtree.NavIDCfg,
		Text:       "Administration",
		SubTitle:   "Organization: " + c.GetOrgName(),
		Icon:       "cog",
		SortWeight: navtree.WeightConfig,
		Children:   configNodes,
		Url:        s.cfg.AppSubURL + "/admin",
	}

	return configNode, nil
}

func enableServiceAccount(s *ServiceImpl, c *contextmodel.ReqContext) bool {
	hasAccess := ac.HasAccess(s.accessControl, c)
	return hasAccess(serviceaccounts.AccessEvaluator)
}
