package guardian

/*
ldap_test_util has functionality to properly test ldap,by setup and teardown of users
*/
import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"

	"code.justin.tv/systems/guardian/cfg"
	"gopkg.in/ldap.v2"
)

type resourceTest struct {
	Resource     *LDAPIdentifier
	GroupsNeeded []*Group `json:"groups_needed,omitempty"`
	Users        *userTest
	Version      string
}

type userTest struct {
	Enabled  []*User
	Disabled []*User
}

func (res LDAPIdentifier) createUser(user *User) error {

	ar, err := user.newUserAttributes()
	if err != nil {
		return err
	}
	err = res.Add(ar)
	if err != nil {
		return err
	}
	return err

}

func (res LDAPIdentifier) addToGroup(user *User, groupCN string) error {
	dn := fmt.Sprintf("cn=%s,ou=Groups,dc=justin,dc=tv", groupCN)
	modify := ldap.NewModifyRequest(dn)
	userDN := fmt.Sprintf("cn=%s,ou=Users,dc=justin,dc=tv", user.CN)
	modify.Add("uniqueMember", []string{userDN})

	return res.Modify(modify)
}

func (res LDAPIdentifier) createDisabledUser(user *User) error {
	ar, err := user.newUserAttributes()
	if err != nil {
		return err
	}
	ar.Attribute("ds-pwp-account-disabled", []string{"true"})
	return res.Add(ar)

}

// newResourceTest connects to staging server, creates users in memory from json at
// guardian/guardian/util/ldap/users.json,
// for each user deletes if exists, then creates
func newResourceTest() (*resourceTest, error) {
	// Connect to staging
	conf := cfg.DefaultConfig()
	conf.UseStagingLDAP()
	res, err := NewLDAPIdentifier(conf)
	if err != nil {
		return nil, err
	}
	err = res.BindToUser(conf)
	if err != nil {
		return nil, err
	}

	rt := &resourceTest{
		Resource: res,
	}

	// parse json into object
	data, err := ioutil.ReadFile(conf.Assets.AssetPath + "/guardian/util/ldap/users.json")
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(data, rt)
	if err != nil {
		return nil, err
	}

	return rt, err
}

// verifies that the version of users.json is the same as the attribute on Guardian User.
// If it is, doesn nothing, else deletes all users and remakes
// TODO: this
func (rt *resourceTest) VerifyUsers() error {
	// Creates users...
	var err error
	for _, user := range rt.Users.Enabled {

		err = rt.Resource.createUser(user)
		if err != nil {
			return err
		}

		for _, user := range rt.Users.Enabled {
			for _, group := range user.Groups {
				err = rt.Resource.addToGroup(user, group)
				if err != nil {
					return err
				}
			}
		}

		for _, user := range rt.Users.Disabled {
			for _, group := range user.Groups {
				err = rt.Resource.addToGroup(user, group)
				if err != nil {
					return err
				}
			}
		}

	}
	return nil

}

// Creates a new ldap.AddRequest for a user and adds base attributes
// expected of a user
// NOTE: meant for testing, so sets password to: "navi rules!"
func (user *User) newUserAttributes() (*ldap.AddRequest, error) {

	// Assumes that a user parsed from json will already have a cn and uid
	if user.CN == "" || user.UID == "" {
		return nil, fmt.Errorf("User has no CN or UID: CN: %s, UID: %s", user.CN, user.UID)
	}

	// Assume given cn is structured as "givenName sn"
	name := strings.Split(user.CN, " ")
	if len(name) != 2 {
		return nil, fmt.Errorf("User cn formatted oddly, expecting 'givenName  sn',got %s", user.CN)
	}
	givenName := name[0]
	sn := name[1]

	dn := fmt.Sprintf("cn=%s,ou=Users,dc=justin,dc=tv", user.CN)
	ar := ldap.NewAddRequest(dn)

	ar.Attribute("objectClass", []string{
		"person",
		"organizationalPerson",
		"inetOrgPerson",
		"top",
	})

	ar.Attribute("cn", []string{user.CN})
	ar.Attribute("uid", []string{user.UID})
	ar.Attribute("givenName", []string{givenName})
	ar.Attribute("sn", []string{sn})

	ar.Attribute("userPassword", []string{"navi rules!"})

	return ar, nil

}

func groupToUsersMap(users []*User, groupToUsers map[string][]string) {
	for _, user := range users {
		// check existence of groupCN's array
		for _, groupCN := range user.Groups {
			if _, ok := groupToUsers[groupCN]; ok {
				groupToUsers[groupCN] = append(groupToUsers[groupCN], user.CN)
				continue
			}
			groupToUsers[groupCN] = []string{user.CN}

		}
	}

}
