package enidpsync_test

import (
	"context"
	"testing"

	"github.com/golang-jwt/jwt/v4"
	"github.com/stretchr/testify/require"

	"cdr.dev/slog/sloggers/slogtest"
	"github.com/coder/coder/v2/coderd/entitlements"
	"github.com/coder/coder/v2/coderd/idpsync"
	"github.com/coder/coder/v2/coderd/rbac"
	"github.com/coder/coder/v2/coderd/runtimeconfig"
	"github.com/coder/coder/v2/codersdk"
	"github.com/coder/coder/v2/enterprise/coderd/enidpsync"
)

func TestEnterpriseParseRoleClaims(t *testing.T) {
	t.Parallel()

	entitled := entitlements.New()
	entitled.Update(func(en *codersdk.Entitlements) {
		en.Features[codersdk.FeatureUserRoleManagement] = codersdk.Feature{
			Entitlement: codersdk.EntitlementEntitled,
			Enabled:     true,
		}
	})

	t.Run("NotEntitled", func(t *testing.T) {
		t.Parallel()

		mgr := runtimeconfig.NewManager()
		s := enidpsync.NewSync(slogtest.Make(t, nil), mgr, entitlements.New(), idpsync.DeploymentSyncSettings{})

		params, err := s.ParseRoleClaims(context.Background(), jwt.MapClaims{})
		require.Nil(t, err)
		require.False(t, params.SyncEntitled)
		require.False(t, params.SyncSiteWide)
	})

	t.Run("NotEntitledButEnabled", func(t *testing.T) {
		t.Parallel()
		// Since it is not entitled, it should not be enabled

		mgr := runtimeconfig.NewManager()
		s := enidpsync.NewSync(slogtest.Make(t, nil), mgr, entitlements.New(), idpsync.DeploymentSyncSettings{
			SiteRoleField: "roles",
		})

		params, err := s.ParseRoleClaims(context.Background(), jwt.MapClaims{})
		require.Nil(t, err)
		require.False(t, params.SyncEntitled)
		require.False(t, params.SyncSiteWide)
	})

	t.Run("SiteDisabled", func(t *testing.T) {
		t.Parallel()

		mgr := runtimeconfig.NewManager()
		s := enidpsync.NewSync(slogtest.Make(t, nil), mgr, entitled, idpsync.DeploymentSyncSettings{})

		params, err := s.ParseRoleClaims(context.Background(), jwt.MapClaims{})
		require.Nil(t, err)
		require.True(t, params.SyncEntitled)
		require.False(t, params.SyncSiteWide)
	})

	t.Run("SiteEnabled", func(t *testing.T) {
		t.Parallel()

		mgr := runtimeconfig.NewManager()
		s := enidpsync.NewSync(slogtest.Make(t, nil), mgr, entitled, idpsync.DeploymentSyncSettings{
			SiteRoleField:    "roles",
			SiteRoleMapping:  map[string][]string{},
			SiteDefaultRoles: []string{rbac.RoleTemplateAdmin().Name},
		})

		params, err := s.ParseRoleClaims(context.Background(), jwt.MapClaims{
			"roles": []string{rbac.RoleAuditor().Name},
		})
		require.Nil(t, err)
		require.True(t, params.SyncEntitled)
		require.True(t, params.SyncSiteWide)
		require.ElementsMatch(t, []string{
			rbac.RoleTemplateAdmin().Name,
			rbac.RoleAuditor().Name,
		}, params.SiteWideRoles)
	})

	t.Run("SiteMapping", func(t *testing.T) {
		t.Parallel()

		mgr := runtimeconfig.NewManager()
		s := enidpsync.NewSync(slogtest.Make(t, nil), mgr, entitled, idpsync.DeploymentSyncSettings{
			SiteRoleField: "roles",
			SiteRoleMapping: map[string][]string{
				"foo": {rbac.RoleAuditor().Name, rbac.RoleUserAdmin().Name},
				"bar": {rbac.RoleOwner().Name},
			},
			SiteDefaultRoles: []string{rbac.RoleTemplateAdmin().Name},
		})

		params, err := s.ParseRoleClaims(context.Background(), jwt.MapClaims{
			"roles": []string{"foo", "bar", "random"},
		})
		require.Nil(t, err)
		require.True(t, params.SyncEntitled)
		require.True(t, params.SyncSiteWide)
		require.ElementsMatch(t, []string{
			rbac.RoleTemplateAdmin().Name,
			rbac.RoleAuditor().Name,
			rbac.RoleUserAdmin().Name,
			rbac.RoleOwner().Name,
			// Invalid claims are still passed at this point
			"random",
		}, params.SiteWideRoles)
	})

	t.Run("DuplicateRoles", func(t *testing.T) {
		t.Parallel()

		mgr := runtimeconfig.NewManager()
		s := enidpsync.NewSync(slogtest.Make(t, nil), mgr, entitled, idpsync.DeploymentSyncSettings{
			SiteRoleField: "roles",
			SiteRoleMapping: map[string][]string{
				"foo": {rbac.RoleOwner().Name, rbac.RoleAuditor().Name},
				"bar": {rbac.RoleOwner().Name},
			},
			SiteDefaultRoles: []string{rbac.RoleAuditor().Name},
		})

		params, err := s.ParseRoleClaims(context.Background(), jwt.MapClaims{
			"roles": []string{"foo", "bar", rbac.RoleAuditor().Name, rbac.RoleOwner().Name},
		})
		require.Nil(t, err)
		require.True(t, params.SyncEntitled)
		require.True(t, params.SyncSiteWide)
		require.ElementsMatch(t, []string{
			rbac.RoleAuditor().Name,
			rbac.RoleOwner().Name,
		}, params.SiteWideRoles)
	})
}
