package authn

import (
	"testing"

	"github.com/grafana/authlib/types"
	"github.com/stretchr/testify/assert"
)

func TestAccessToken_getInnermostActor(t *testing.T) {
	t.Run("no actor", func(t *testing.T) {
		claims := AccessTokenClaims{}
		actor := claims.getInnermostActor()
		var nilActor *ActorClaims
		assert.Equal(t, nilActor, actor)
	})

	t.Run("root-level actor", func(t *testing.T) {
		claims := AccessTokenClaims{
			Actor: &ActorClaims{
				Subject:         "subject",
				ServiceIdentity: "service-identity",
			},
		}

		actor := claims.getInnermostActor()
		assert.Equal(t, "subject", actor.Subject)
		assert.Equal(t, "service-identity", actor.ServiceIdentity)
	})

	t.Run("innermost actor", func(t *testing.T) {
		claims := AccessTokenClaims{
			Actor: &ActorClaims{
				Subject:         "subject",
				ServiceIdentity: "service-identity",
				Actor: &ActorClaims{
					Subject:         "nested-subject",
					ServiceIdentity: "nested-service-identity",
					Actor: &ActorClaims{
						Subject:         "innermost-subject",
						ServiceIdentity: "innermost-service-identity",
					},
				},
			},
		}

		actor := claims.getInnermostActor()
		assert.Equal(t, "innermost-subject", actor.Subject)
		assert.Equal(t, "innermost-service-identity", actor.ServiceIdentity)
	})
}

func TestAccessToken_getIdentityActor(t *testing.T) {
	t.Run("no actor", func(t *testing.T) {
		claims := AccessTokenClaims{}
		actor := claims.getIdentityActor()
		var nilActor *ActorClaims
		assert.Equal(t, nilActor, actor)
	})

	t.Run("non-identity actor", func(t *testing.T) {
		claims := AccessTokenClaims{
			Actor: &ActorClaims{
				IDTokenClaims: IDTokenClaims{
					Type: types.TypeAccessPolicy,
				},
			},
		}

		actor := claims.getIdentityActor()
		var nilActor *ActorClaims
		assert.Equal(t, nilActor, actor)
	})

	t.Run("nested user actor", func(t *testing.T) {
		claims := AccessTokenClaims{
			Actor: &ActorClaims{
				Actor: &ActorClaims{
					Subject: "nested-user-actor",
					IDTokenClaims: IDTokenClaims{
						Type: types.TypeUser,
					},
				},
			},
		}

		actor := claims.getIdentityActor()
		assert.Equal(t, "nested-user-actor", actor.Subject)
	})

	t.Run("nested service account actor", func(t *testing.T) {
		claims := AccessTokenClaims{
			Actor: &ActorClaims{
				Actor: &ActorClaims{
					Subject: "nested-user-actor",
					IDTokenClaims: IDTokenClaims{
						Type: types.TypeServiceAccount,
					},
				},
			},
		}

		actor := claims.getIdentityActor()
		assert.Equal(t, "nested-user-actor", actor.Subject)
	})
}

func TestAccessToken_IsOnBehalfOfUser(t *testing.T) {
	t.Run("no actor", func(t *testing.T) {
		claims := AccessTokenClaims{}
		assert.False(t, claims.IsOnBehalfOfUser())
	})

	t.Run("user actor", func(t *testing.T) {
		claims := AccessTokenClaims{
			Actor: &ActorClaims{
				IDTokenClaims: IDTokenClaims{
					Type: types.TypeUser,
				},
			},
		}

		assert.True(t, claims.IsOnBehalfOfUser())
	})

	t.Run("service account actor", func(t *testing.T) {
		claims := AccessTokenClaims{
			Actor: &ActorClaims{
				IDTokenClaims: IDTokenClaims{
					Type: types.TypeServiceAccount,
				},
			},
		}

		assert.True(t, claims.IsOnBehalfOfUser())
	})

	t.Run("non-user actor", func(t *testing.T) {
		claims := AccessTokenClaims{
			Actor: &ActorClaims{
				IDTokenClaims: IDTokenClaims{
					Type: types.TypeAccessPolicy,
				},
			},
		}

		assert.False(t, claims.IsOnBehalfOfUser())
	})
}
