package main

import (
	"fmt"

	"devel.mephi.ru/iacherepanov/openstack-gophercloud/openstack/identity/v3/projects"
	"devel.mephi.ru/iacherepanov/openstack-gophercloud/openstack/identity/v3/roles"
)

func main() {
	osclient := initIdentityClient()

	fmt.Println("Syncing projects...")
	osclient.syncProjects()
	// fmt.Println("Deleting projects...")
	// osclient.deleteAllProjects()
	fmt.Println("Syncing role assignments...")
	osclient.syncRoleAssignments()
	// fmt.Println("Deleting role assignments...")
	// osclient.deleteAllRoleAssignments()
}

func (osclient OpenstackIdentityClient) syncProjects() {
	allUnits := getCleanUnitsPersNumberMap(osclient.config)
	allProjects := osclient.getProjectsMap()

	for persNumber, unit := range allUnits {
		if _, ok := allProjects[persNumber]; ok {
			updateOpts := makeUpdateOpts(unit, osclient.domainID)
			projectID := allProjects[persNumber].ID
			updateResult := projects.Update(osclient.client, projectID, updateOpts)
			checkErr(updateResult.Err)
			fmt.Printf("Project \"%s\" updated\n", *unit.BriefName)
		} else {
			createOpts := makeCreateOpts(unit, osclient.domainID)
			createResult := projects.Create(osclient.client, createOpts)
			checkErr(createResult.Err)
			fmt.Printf("Project \"%s\" created\n", *unit.BriefName)
		}

		// Add cloud_admin user as admin to project
		projectID := allProjects[persNumber].ID
		assignOpts := roles.AssignOpts{
			UserID:    osclient.cloudAdminID,
			ProjectID: projectID,
		}
		assignmentResult := roles.Assign(osclient.client, osclient.adminRoleID, assignOpts)
		checkErr(assignmentResult.ExtractErr())
		fmt.Printf("Admin role on project %s assigned to cloud_admin\n", projectID)
	}
}

func (osclient OpenstackIdentityClient) deleteAllProjects() {
	allProjects := osclient.getProjectsMap()

	index := 1
	amount := len(allProjects)
	for _, project := range allProjects {
		deleteResult := projects.Delete(osclient.client, project.ID)
		checkErr(deleteResult.ExtractErr())
		fmt.Printf("[%v/%v] Project \"%s\" deleted\n", index, amount, project.Name)
		index++
	}
}

func (osclient OpenstackIdentityClient) syncRoleAssignments() {
	osclient.deleteAllRoleAssignments()

	openstackProjects := osclient.getProjectsMap()
	openstackUsers := osclient.getOpenstackUsersMap()
	units := getCleanUnitsCodeMap(osclient.config)
	people := getPeopleMap(osclient.config)
	roleList := getRoles(osclient.config)

	for _, role := range roleList {
		// Get project ID from unit code
		unitCode := unitCodeToInt(role.Extra)
		unit := units[unitCode]
		// Check if unit doesn't exist in units map
		if unit.Id == 0 {
			continue
		}
		projectID := openstackProjects[*unit.PersNumber].ID

		person := people[role.PersonId]

		// Find user ID by login
		var userID string
		for _, login := range person.Logins {
			user := openstackUsers[login]
			if user.ID != "" {
				userID = user.ID
				break
			}
		}
		// Check if user doesn't exist in Openstack
		if userID == "" {
			continue
		}

		assignOpts := roles.AssignOpts{
			UserID:    userID,
			ProjectID: projectID,
		}

		var roleID string
		if role.RoleName == "openstack_admin" {
			roleID = osclient.adminRoleID
		} else {
			roleID = osclient.userRoleID
		}
		assignmentResult := roles.Assign(osclient.client, roleID, assignOpts)
		checkErr(assignmentResult.ExtractErr())
		fmt.Printf("Role %s on project %s assigned to %s\n", roleID, projectID, userID)
	}
}

func (osclient OpenstackIdentityClient) deleteAllRoleAssignments() {
	listAssignmentsOpts := roles.ListAssignmentsOpts{}

	allPages, err := roles.ListAssignments(osclient.client, listAssignmentsOpts).AllPages()
	checkErr(err)

	allRoleAssignments, err := roles.ExtractRoleAssignments(allPages)
	checkErr(err)

	// Get all mephi-users
	openstackUsers := osclient.getOpenstackUsersMapByID()

	for _, roleAssignment := range allRoleAssignments {
		user := openstackUsers[roleAssignment.User.ID]
		// Check that role assignment belongs to mephi-user
		// This is necessary in order to not remove internal service openstack-users (like cinder, neutron, etc).
		if user.ID == "" {
			continue
		}

		unassignOpts := roles.UnassignOpts{
			UserID:    roleAssignment.User.ID,
			ProjectID: roleAssignment.Scope.Project.ID,
		}
		unassignmentResult := roles.Unassign(osclient.client, roleAssignment.Role.ID, unassignOpts)
		checkErr(unassignmentResult.ExtractErr())
		fmt.Printf("Role of user %s on project %s deleted\n", user.Name, roleAssignment.Scope.Project.ID)
	}
}
