package main

import (
	"sort"
)

func (server *Server) memberOfAdd(member string, group string) {
	// Retreive previous memberOf value
	memberGroups, err := server.getAttribute(member, ATTR_MEMBEROF)
	if err != nil {
		server.logger.Warnf("Could not add %s to memberOf of %s: %s", group, member, err)
		return
	}

	// Return early if group is already in memberOf
	for _, g := range memberGroups {
		if g == group {
			server.logger.Warnf("Inconsistency detected, %s was memberOf %s at a time when it didn't exist! (not an issue anymore)",
				member, group)
			return
		}
	}

	// Add group to memberOf
	memberGroups = append(memberGroups, group)
	err = server.putAttributes(member, Entry{
		ATTR_MEMBEROF: memberGroups,
	})
	if err != nil {
		server.logger.Warnf("Could not add %s to memberOf of %s: %s", group, member, err)
	}
}

func (server *Server) memberOfRemove(member string, group string) {
	// Retreive previous memberOf value
	memberOf, err := server.getAttribute(member, ATTR_MEMBEROF)
	if err != nil || memberOf == nil {
		server.logger.Warnf("Could not remove %s from memberOf of %s: %s", group, member, err)
		return
	}

	// Filter out group
	newMemberOf := []string{}
	for _, g := range memberOf {
		if g != group {
			newMemberOf = append(newMemberOf, g)
		}
	}

	// Return early if group already not in memberOf
	if len(newMemberOf) == len(memberOf) {
		server.logger.Warnf("Inconsistency detected, %s was not memberOf %s at a time when it should have been! (not an issue anymore)",
			member, group)
		return
	}

	// Update value of memberOf
	err = server.putAttributes(member, Entry{
		ATTR_MEMBEROF: newMemberOf,
	})
	if err != nil {
		server.logger.Warnf("Could not remove %s from memberOf of %s: %s", group, member, err)
	}
}

func (server *Server) memberOfResync() error {
	server.logger.Warnf("Starting resync of member/memberOf values")

	// Load the whole LDAP database
	basePath, err := dnToConsul(server.config.Suffix)
	if err != nil {
		return err
	}

	data, _, err := server.kv.List(basePath, &server.readOpts)
	if err != nil {
		return err
	}

	entries, err := parseConsulResult(data)
	if err != nil {
		return err
	}

	// Check member values for all objects, and remove deleted objects
	// Calculate new memberOf sets for all objects
	newMemberOf := make(map[string][]string)
	for dn := range entries {
		newMemberOf[dn] = []string{}
	}

	for dn, entry := range entries {
		member, ok := entry[ATTR_MEMBER]
		if !ok {
			continue
		}
		keepMember := []string{}
		for _, mem := range member {
			if _, exists := entries[mem]; exists {
				keepMember = append(keepMember, mem)
				newMemberOf[mem] = append(newMemberOf[mem], dn)
			} else {
				server.logger.Warnf("Fixing: %s had as member %s which doesn't exist", dn, mem)
			}
		}
		if len(keepMember) != len(member) {
			server.logger.Warnf("Writing new member list for %s", dn)
			err = server.putAttributes(dn, Entry{
				ATTR_MEMBER: keepMember,
			})
			if err != nil {
				return err
			}
		}
	}

	// Write updated memberOf values
	for dn, newMemberOfV := range newMemberOf {
		prevMemberOf := []string{}
		if v, ok := entries[dn][ATTR_MEMBEROF]; ok {
			prevMemberOf = v
		}
		different := (len(prevMemberOf) != len(newMemberOfV))
		if !different {
			sort.Strings(newMemberOfV)
			sort.Strings(prevMemberOf)
			for i := range newMemberOfV {
				if newMemberOfV[i] != prevMemberOf[i] {
					different = true
					break
				}
			}
		}
		if different {
			server.logger.Warnf("Fixing memberOf for %s (%d -> %d)",
				dn, len(prevMemberOf), len(newMemberOfV))
			err = server.putAttributes(dn, Entry{
				ATTR_MEMBEROF: newMemberOfV,
			})
			if err != nil {
				return err
			}
		}
	}

	server.logger.Warnf("Done resyncing member/memberOf values")
	return nil
}
