package router_test

import (
	"fmt"
	"testing"

	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/router"
	apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
	bearerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
	"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
	"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
	"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine/inmemory"
	resourcetest "git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil"
	nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
	"github.com/stretchr/testify/require"
)

const (
	container = "67ETTZzbzJC6WxdQhHHHsJNCttVMBqYrSoFaUFVDNfiX"
	rootNs    = ""
)

var (
	allowBySourceIP = &chain.Chain{
		Rules: []chain.Rule{
			{
				Status:    chain.Allow,
				Actions:   chain.Actions{Names: []string{nativeschema.MethodPutObject}},
				Resources: chain.Resources{Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container)}},
				Condition: []chain.Condition{
					{
						Op:    chain.CondStringEquals,
						Kind:  chain.KindRequest,
						Key:   "SourceIP",
						Value: "10.122.1.20",
					},
				},
			},
		},
	}

	denyBySourceIP = &chain.Chain{
		Rules: []chain.Rule{
			{
				Status:    chain.AccessDenied,
				Actions:   chain.Actions{Names: []string{nativeschema.MethodPutObject}},
				Resources: chain.Resources{Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container)}},
				Condition: []chain.Condition{
					{
						Op:    chain.CondStringEquals,
						Kind:  chain.KindRequest,
						Key:   "SourceIP",
						Value: "10.122.1.20",
					},
				},
			},
		},
	}
)

func TestBearerChainFedRouter(t *testing.T) {
	t.Run("no bearer token overrides", func(t *testing.T) {
		inmem := inmemory.NewInMemoryLocalOverrides()

		inmem.LocalStorage().AddOverride(chain.Ingress, engine.ContainerTarget(container), denyBySourceIP)
		inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(container), allowBySourceIP)

		_, err := router.BearerChainFeedRouter(inmem.LocalStorage(), inmem.MorphRuleChainStorage(), bearerSDK.APEOverride{})
		require.Error(t, err)
	})
	t.Run("allow by container with deny by bearer overrides", func(t *testing.T) {
		inmem := inmemory.NewInMemoryLocalOverrides()

		inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(container), allowBySourceIP)

		bt := bearerSDK.APEOverride{
			Target: apeSDK.ChainTarget{
				TargetType: apeSDK.TargetTypeContainer,
				Name:       container,
			},
			Chains: []apeSDK.Chain{{
				Raw: denyBySourceIP.Bytes(),
			}},
		}

		r, err := router.BearerChainFeedRouter(inmem.LocalStorage(), inmem.MorphRuleChainStorage(), bt)
		require.NoError(t, err)

		req := resourcetest.NewRequest(nativeschema.MethodPutObject,
			resourcetest.NewResource(fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container), map[string]string{}),
			map[string]string{
				"SourceIP": "10.122.1.20",
				"Actor":    "someOwner",
			},
		)

		st, found, err := r.IsAllowed(chain.Ingress, engine.NewRequestTarget(rootNs, container), req)
		require.NoError(t, err)
		require.True(t, found)
		require.Equal(t, st, chain.AccessDenied)
	})
	t.Run("allow by namespace with deny by bearer overrides", func(t *testing.T) {
		inmem := inmemory.NewInMemoryLocalOverrides()

		inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(container), allowBySourceIP)
		inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(rootNs), allowBySourceIP)

		bt := bearerSDK.APEOverride{
			Target: apeSDK.ChainTarget{
				TargetType: apeSDK.TargetTypeContainer,
				Name:       container,
			},
			Chains: []apeSDK.Chain{{
				Raw: denyBySourceIP.Bytes(),
			}},
		}

		r, err := router.BearerChainFeedRouter(inmem.LocalStorage(), inmem.MorphRuleChainStorage(), bt)
		require.NoError(t, err)

		req := resourcetest.NewRequest(nativeschema.MethodPutObject,
			resourcetest.NewResource(fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container), map[string]string{}),
			map[string]string{
				"SourceIP": "10.122.1.20",
				"Actor":    "someOwner",
			},
		)

		st, found, err := r.IsAllowed(chain.Ingress, engine.NewRequestTarget(rootNs, container), req)
		require.NoError(t, err)
		require.True(t, found)
		require.Equal(t, st, chain.AccessDenied)
	})
	t.Run("deny by namespace with allow by bearer overrides", func(t *testing.T) {
		inmem := inmemory.NewInMemoryLocalOverrides()

		inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(rootNs), denyBySourceIP)

		bt := bearerSDK.APEOverride{
			Target: apeSDK.ChainTarget{
				TargetType: apeSDK.TargetTypeContainer,
				Name:       container,
			},
			Chains: []apeSDK.Chain{{
				Raw: allowBySourceIP.Bytes(),
			}},
		}

		r, err := router.BearerChainFeedRouter(inmem.LocalStorage(), inmem.MorphRuleChainStorage(), bt)
		require.NoError(t, err)

		req := resourcetest.NewRequest(nativeschema.MethodPutObject,
			resourcetest.NewResource(fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container), map[string]string{}),
			map[string]string{
				"SourceIP": "10.122.1.20",
				"Actor":    "someOwner",
			},
		)

		st, found, err := r.IsAllowed(chain.Ingress, engine.NewRequestTarget(rootNs, container), req)
		require.NoError(t, err)
		require.True(t, found)
		require.Equal(t, st, chain.AccessDenied)
	})
}