package helpers

import (
	"encoding/json"
	"time"

	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gexec"
)

const NonUAAOrigin = "cli-oidc-provider"

type User struct {
	GUID      string
	Username  string
	Origin    string
	CreatedAt time.Time
}

// ListUsers returns all the users in the targeted environment.
func GetUsers() []User {
	var userPagesResponse struct {
		NextURL   *string `json:"next_url"`
		Resources []struct {
			Metadata struct {
				GUID      string    `json:"guid"`
				CreatedAt time.Time `json:"created_at"`
			} `json:"metadata"`
			Entity struct {
				Username string `json:"username"`
			} `json:"entity"`
		} `json:"resources"`
	}

	var allUsers []User
	nextURL := "/v2/users?results-per-page=50"

	for {
		session := CF("curl", nextURL)
		Eventually(session).Should(Exit(0))

		err := json.Unmarshal(session.Out.Contents(), &userPagesResponse)
		Expect(err).NotTo(HaveOccurred())
		for _, resource := range userPagesResponse.Resources {
			allUsers = append(allUsers, User{
				GUID:      resource.Metadata.GUID,
				CreatedAt: resource.Metadata.CreatedAt,
				Username:  resource.Entity.Username,
			})
		}

		if userPagesResponse.NextURL == nil {
			break
		}
		nextURL = *userPagesResponse.NextURL
	}

	return allUsers
}

func GetUsersV3() []User {
	var userPagesResponse struct {
		Pagination struct {
			NextURL *string `json:"next_url"`
		} `json:"pagination"`
		Resources []struct {
			GUID     string `json:"guid"`
			Origin   string `json:"origin"`
			Username string `json:"username"`
		} `json:"resources"`
	}

	var allUsers []User
	nextURL := "/v3/users?per_page=50"

	for {
		session := CF("curl", nextURL)
		Eventually(session).Should(Exit(0))

		err := json.Unmarshal(session.Out.Contents(), &userPagesResponse)
		Expect(err).NotTo(HaveOccurred())
		for _, resource := range userPagesResponse.Resources {
			allUsers = append(allUsers, User{
				Origin:   resource.Origin,
				Username: resource.Username,
			})
		}

		if userPagesResponse.Pagination.NextURL == nil {
			break
		}
		nextURL = *userPagesResponse.Pagination.NextURL
	}

	return allUsers
}

// CreateUser creates a user with a random username and password and returns both.
func CreateUser() (string, string) {
	username := NewUsername()
	password := RandomName()

	// env := map[string]string{
	// 	"NEW_USER_PASSWORD": password,
	// }

	// session := CFWithEnv(env, "create-user", username, "$NEW_USER_PASSWORD")
	session := CF("create-user", username, password)
	Eventually(session).Should(Exit(0))

	return username, password
}

// DeleteUser deletes the user specified by username.
func DeleteUser(username string) {
	session := CF("delete-user", username, "-f")
	Eventually(session).Should(Exit(0))
}

// CreateUserInOrgRole creates a user with a random username and password and gives them the specified role within
// a specific org. The new user's username and password are returned.
func CreateUserInOrgRole(org, role string) (string, string) {
	username, password := CreateUser()

	session := CF("set-org-role", username, org, role)
	Eventually(session).Should(Exit(0))

	return username, password
}

// CreateUserInSpaceRole creates a user with a random username and password and gives them the specified role within
// a specific space. The new user's username and password are returned.
func CreateUserInSpaceRole(org, space, role string) (string, string) {
	username, password := CreateUser()

	session := CF("set-space-role", username, org, space, role)
	Eventually(session).Should(Exit(0))

	return username, password
}
