package allowlist

import (
	"sync"
	"testing"
)

func TestAllowList(t *testing.T) {
	allowList := &UnionAllowList{
		Dedicated: &AllowList{
			Rules: []orgRule{
				{
					OrgId:    1,
					AllowAll: true,
				},
				{
					OrgId:           2,
					AllowAll:        false,
					AllowProjectIds: []uint64{4, 5, 6},
					BlockProjectIds: []uint64{7, 8, 9},
				},
			},
			allowCache: make(map[uint64]map[uint64]bool),
			lock:       &sync.Mutex{},
		},
	}

	// TODO: test cache hit/miss
	// TODO: test affectAfter

	// dedicated  Allow
	// return true
	for _, i := range [][]uint64{{2, 4}, {2, 5}, {2, 6}, {1, 111}, {1, 888}} {
		if allow, err := allowList.Dedicated.IsAllow(t.Context(), i[0], i[1]); !allow && err != nil {
			t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", i, allow)
		}
	}
	t.Log(allowList.Dedicated.allowCache)

	// return false
	for _, i := range [][]uint64{{2, 7}, {2, 8}, {2, 9}, {3, 11}, {66, 77}} {
		if allow, err := allowList.Dedicated.IsAllow(t.Context(), i[0], i[1]); allow && err != nil {
			t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want false", i, allow)
		}
	}
	t.Log(allowList.Dedicated.allowCache)
}

func TestAllowListAllowAll(t *testing.T) {
	allowList := &UnionAllowList{
		Dedicated: &AllowList{
			Rules: []orgRule{
				{
					OrgId:    1,
					AllowAll: true,
				},
				{
					OrgId:           2,
					AllowAll:        false,
					AllowProjectIds: []uint64{4, 5, 6},
					BlockProjectIds: []uint64{7, 8, 9},
				},
			},
			AllowAll:   true,
			allowCache: make(map[uint64]map[uint64]bool),
			lock:       &sync.Mutex{},
		},
	}

	// return true
	// if AllowAll is true and the org not in the rules, return true
	allow, err := allowList.Dedicated.IsAllow(t.Context(), 3, 333)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 333, allow)
	}

	// return true
	// if AllowAll is true and the project is not in the rules, return true
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 2, 111)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 111, allow)
	}

	// return false
	// if AllowAll is false and the project is not in the rules, return false
	allowList.Dedicated.AllowAll = false
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 2, 111)
	if allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want false", 111, allow)
	}

	t.Log(allowList.Dedicated.allowCache)
}

func TestAllowListPhase1(t *testing.T) {
	// The project [Id is 1] to be migrated is not the last project for the tenant,
	allowList := &UnionAllowList{
		Dedicated: &AllowList{
			Rules: []orgRule{
				{
					OrgId:           1,
					AllowAll:        false,
					BlockAll:        false,
					AllowProjectIds: []uint64{1},
					BlockProjectIds: []uint64{},
				},
			},
			AllowAll:   false,
			allowCache: make(map[uint64]map[uint64]bool),
			lock:       &sync.Mutex{},
		},
	}

	// return true
	allow, err := allowList.Dedicated.IsAllow(t.Context(), 1, 1)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 1, allow)
	}

	// return false
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 1, 2)
	if allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 2, allow)
	}

	// The project to be migrated is the last project [Id is P2] for the tenant
	allowList.Dedicated.Rules[0].AllowAll = true

	// return true
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 1, 1)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 1, allow)
	}

	// return true
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 1, 2)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 2, allow)
	}

	t.Log(allowList.Dedicated.allowCache)
}

func TestAllowListPhase2(t *testing.T) {
	// only the orgs in the block list will continue to use the old architecture.
	allowList := &UnionAllowList{
		Dedicated: &AllowList{
			Rules: []orgRule{
				{
					OrgId:           2,
					AllowAll:        false,
					BlockAll:        true,
					AllowProjectIds: []uint64{},
					BlockProjectIds: []uint64{},
				},
				{
					OrgId:           100,
					AllowAll:        false,
					BlockAll:        true,
					AllowProjectIds: []uint64{},
					BlockProjectIds: []uint64{},
				},
			},
			AllowAll:   true,
			allowCache: make(map[uint64]map[uint64]bool),
			lock:       &sync.Mutex{},
		},
	}

	// return false
	allow, err := allowList.Dedicated.IsAllow(t.Context(), 2, 21)
	if allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want false", 21, allow)
	}

	// return false
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 2, 22)
	if allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want false", 22, allow)
	}

	// return false
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 100, 1001)
	if allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want false", 1001, allow)
	}

	// return true
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 3, 33)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 33, allow)
	}

	// return true
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 1, 1)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 1, allow)
	}
}

func TestAllowListPhase3(t *testing.T) {
	// The project [Id is 50] to be migrated is not the last project for the tenant
	allowList := &UnionAllowList{
		Dedicated: &AllowList{
			Rules: []orgRule{
				{
					OrgId:           100,
					AllowAll:        false,
					BlockAll:        false,
					AllowProjectIds: []uint64{},
					BlockProjectIds: []uint64{
						51,
						52,
						53,
					},
				},
			},
			AllowAll:   true,
			allowCache: make(map[uint64]map[uint64]bool),
			lock:       &sync.Mutex{},
		},
	}

	// return true
	allow, err := allowList.Dedicated.IsAllow(t.Context(), 100, 50)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 50, allow)
	}

	// return false
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 100, 51)
	if allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want false", 51, allow)
	}

	// The project [Id is 51] to be migrated is the last project for the tenant
	allowList.Dedicated.Rules[0].BlockProjectIds = []uint64{}
	allowList.Dedicated.Rules[0].AllowAll = true

	// return true
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 100, 50)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 50, allow)
	}

	// return true
	allow, err = allowList.Dedicated.IsAllow(t.Context(), 100, 51)
	if !allow && err != nil {
		t.Errorf("allowList.Dedicated.IsAllow(%d) = %t; want true", 51, allow)
	}
}
