// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0

package rbac

import (
	"sort"
)

// +kubebuilder:object:generate=true
type UserListSpec []UserSpec

func (o *UserListSpec) Upsert(newUser UserSpec) {
	users := *o

	// Comparator consistent with ByKindName
	less := func(a, b UserSpec) bool {
		ak, bk := a.Kind.String(), b.Kind.String()
		if ak != bk {
			return ak < bk
		}

		return a.Name < b.Name
	}

	// Ensure sorted before binary search
	sort.Sort(ByKindName(users))

	// Find first index where users[i] >= newUser
	idx := sort.Search(len(users), func(i int) bool {
		return !less(users[i], newUser)
	})

	// In this case merging for duplicates makes little sense as the values are identical
	if idx < len(users) && !less(newUser, users[idx]) && !less(users[idx], newUser) {
		return
	}

	users = append(users, newUser)

	sort.Sort(ByKindName(users))

	*o = users
}

func (u UserListSpec) IsPresent(name string, groups []string) bool {
	groupSet := make(map[string]struct{}, len(groups))
	for _, g := range groups {
		groupSet[g] = struct{}{}
	}

	for _, user := range u {
		switch user.Kind {
		case UserOwner, ServiceAccountOwner:
			if user.Name == name {
				return true
			}
		case GroupOwner:
			if _, ok := groupSet[user.Name]; ok {
				return true
			}
		}
	}

	return false
}

func (o UserListSpec) FindUser(name string, kind OwnerKind) (UserSpec, bool) {
	sort.Sort(ByKindName(o))

	targetKind := kind.String()
	n := len(o)

	idx := sort.Search(n, func(i int) bool {
		ki := o[i].Kind.String()

		switch {
		case ki > targetKind:
			return true
		case ki < targetKind:
			return false
		default:
			return o[i].Name >= name
		}
	})

	if idx < n &&
		o[idx].Kind.String() == targetKind &&
		o[idx].Name == name {
		return o[idx], true
	}

	return UserSpec{}, false
}

func (o UserListSpec) SplitUsersAndGroups() (users []string, groups []string) {
	seenU := make(map[string]struct{}, len(o))
	seenG := make(map[string]struct{}, len(o))

	for _, s := range o {
		if s.Name == "" {
			continue
		}

		switch s.Kind {
		case UserOwner, ServiceAccountOwner:
			seenU[s.Name] = struct{}{}
		case GroupOwner:
			seenG[s.Name] = struct{}{}
		default:
			continue
		}
	}

	if len(seenU) > 0 {
		users = make([]string, 0, len(seenU))

		for u := range seenU {
			users = append(users, u)
		}

		sort.Strings(users)
	}

	if len(seenG) > 0 {
		groups = make([]string, 0, len(seenG))

		for g := range seenG {
			groups = append(groups, g)
		}

		sort.Strings(groups)
	}

	return users, groups
}

func (o UserListSpec) GetByKinds(kinds []OwnerKind) []string {
	if len(o) == 0 || len(kinds) == 0 {
		return nil
	}

	kindSet := make(map[OwnerKind]struct{}, len(kinds))
	for _, k := range kinds {
		kindSet[k] = struct{}{}
	}

	names := make([]string, 0, len(o))

	for _, u := range o {
		if _, ok := kindSet[u.Kind]; ok {
			names = append(names, u.Name)
		}
	}

	if len(names) == 0 {
		return nil
	}

	sort.Strings(names)

	return names
}

type ByKindName UserListSpec

func (b ByKindName) Len() int {
	return len(b)
}

func (b ByKindName) Less(i, j int) bool {
	if b[i].Kind.String() != b[j].Kind.String() {
		return b[i].Kind.String() < b[j].Kind.String()
	}

	return b[i].Name < b[j].Name
}

func (b ByKindName) Swap(i, j int) {
	b[i], b[j] = b[j], b[i]
}
