package main

import (
	"fmt"
	"strings"

	"github.com/go-ldap/ldap/v3"
)

const default_users, default_groups = 1000, 1000

func Init() (*instance, error) {
	inst, err := NewInstance(default_users, default_groups)
	return inst, err
}

//Part to compare our datas
func (inst *instance) CompareOurDataWithConsul() (bool, error) {
	if ok, err := inst.VerifyOurData(inst.dataUsers); !ok {
		return false, err
	}
	if ok, err := inst.VerifyOurData(inst.dataGroups); !ok {
		return false, err
	}
	return true, nil
}

func (inst *instance) VerifyOurData(tabData []data_DN) (bool, error) {
	for _, value := range tabData {
		names := getNamesAtt(value)
		cn := strings.Split(value.DN, ",")[0]
		res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), names)
		if err != nil {
			return false, err
		}
		if len(res.Entries) != 1 {
			return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries))
		}
		if !Compare(value, res.Entries[0]) {
			return false, fmt.Errorf("no match with the DN: %s", value.DN)
		}
	}
	return true, nil
}

func Compare(dat data_DN, ent *ldap.Entry) bool {
	for _, value := range dat.Attributes {
		logging.Debugf("Attributes from %s is now: %s.", dat.DN, dat.Attributes)
		entVal := GetAttributeValuesBottin(ent, value.Name)
		logging.Debugf("Values of the Entry: attributName: %s, Values: %s.", value.Name, entVal)
		if !CompareSliceString(entVal, value.Data) {
			logging.Debugf("Values expected: %s, values found: %s.", value.Data, entVal)
			return false
		}
	}
	return true
}

//Part modify datas
func (inst *instance) ModifyRandomAllData() error {
	dg, err := inst.ModifyRandom(inst.dataGroups, []string{"description"})
	if err != nil {
		return err
	} else {
		inst.dataGroups = dg
	}

	dg, err = inst.ModifyRandom(inst.dataUsers, []string{"displayname"})
	if err != nil {
		return err
	} else {
		inst.dataUsers = dg
	}
	return nil
}

//Function which modify random way the attributes in attName of a data_DN's slice, it can delete, replace and delete
//The function modify also in the dat object
func (inst *instance) ModifyRandom(dat []data_DN, attName []string) ([]data_DN, error) {
	for index, value := range dat {
		del := make(map[string][]string)
		add := make(map[string][]string)
		replace := make(map[string][]string)

		for _, att := range attName {

			switch selNumber := R.Intn(3); selNumber {
			case 0:
				del[att] = []string{}
				value = DelAtt(att, value)
				logging.Debug(fmt.Sprintf("Delete the attribute %s of the DN %s.", att, value.DN))
			case 1:
				name := inst.GenerateName()
				value = AddAtt(name, []string{name}, value)
				add[name] = []string{name}
				logging.Debug(fmt.Sprintf("Add the attribute %s with value %s of the DN %s.", name, name, value.DN))
			case 2:
				name := inst.GenerateName()
				value = ReplaceAtt(att, []string{name}, value)
				replace[att] = []string{name}
				logging.Debug(fmt.Sprintf("Replace the attribute %s with value %s of the DN %s.", att, name, value.DN))
			}

		}

		err := inst.Modify_Request(value.DN, add, del, replace)
		if err != nil {
			return dat, err
		}
		dat[index] = value
	}
	return dat, nil
}

//Add all users in a random group
func (inst *instance) AddAllUsersInGroup() error {
	for _, value := range inst.dataGroups {
		valueRand := (len(inst.dataUsers) + 1) / 30
		if valueRand == 0 {
			valueRand = 1
		}
		numberOfMembers := R.Intn(valueRand) + 1
		logging.Debugf("%s will be have %d members.", value.DN, numberOfMembers)

		groupMemory := make(map[int]struct{})
		users_cn := []string{}

		for i := 0; i < numberOfMembers; i++ {
			selectGroup := R.Intn(len(inst.dataUsers))
			for _, ok := groupMemory[selectGroup]; ok; _, ok = groupMemory[selectGroup] {
				selectGroup = R.Intn(len(inst.dataUsers))

				logging.Debugf("Search an other member. The value is %d , and we have %d members available.", selectGroup, len(inst.dataUsers))
			}
			groupMemory[selectGroup] = struct{}{}

			users_cn = append(users_cn, inst.dataGroups[selectGroup].DN)

		}
		err := inst.AddUserSliceInGroup(users_cn, value.DN)
		if err != nil {
			return err
		}

	}
	return nil
}

//Check if the groups in memberOf exist in Consul
func (inst *instance) CheckMemberOf() (bool, error) {
	for _, value := range inst.dataUsers {
		cn := strings.Split(value.DN, ",")[0]
		res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), []string{"memberOf"})
		if err != nil {
			return false, err
		}
		if len(res.Entries) != 1 {
			return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries))
		}
		attValues := GetAttributeValuesBottin(res.Entries[0], "memberOf")
		for _, dnGroup := range attValues {
			logging.Debugf("Verify if the group %s exist...", dnGroup)
			ok, err := inst.VerifyGroup(data_DN{DN: dnGroup})
			if err != nil {
				return false, err
			}
			if !ok {
				return false, fmt.Errorf("don't found the group: %s", dnGroup)
			}
		}
	}
	return true, nil
}
