package rbac

import (
	"encoding/json"

	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
	"github.com/google/uuid"
	"go.mongodb.org/mongo-driver/bson"
)

// Role represents the data of a role used for access control and is stored in a storage.
type Role struct {
	RoleID      uuid.UUID `json:"_id"`
	RoleName    string    `json:"rolename"`
	Description string    `json:"description,omitempty"`
	Permissions []string  `json:"permissions,omitempty"`
}

// NewRole creates a new role.
func NewRole(id uuid.UUID,
	name string,
	description string,
	permissions []string) rbac.Role {
	return &Role{
		RoleID:      id,
		RoleName:    name,
		Description: description,
		Permissions: permissions,
	}
}

// ID returns a UUID of the role.
func (r Role) ID() uuid.UUID {
	return r.RoleID
}

// Name returns the name of the role.
func (r Role) Name() string {
	return r.RoleName
}

// GetDescription returns the description of the role.
func (r *Role) GetDescription() string {
	return r.Description
}

// GetPermissions returns the permissions linked to the role.
func (r Role) GetPermissions() []string {
	return r.Permissions
}

// RemovePermissionsFromRole takes permissions that should be removed from a role and updates the current permissions accordingly.
func (r *Role) RemovePermissionsFromRole(permissionsToRemove []string) {
	for _, permToRemove := range permissionsToRemove {
		r.removePermissionFromRoles(permToRemove)
	}
}

func (r *Role) removePermissionFromRoles(permToRemove string) {
	for i, perm := range r.Permissions {
		if perm == permToRemove {
			r.Permissions = append(r.Permissions[:i], r.Permissions[i+1:]...)
			break
		}
	}
}

// MarshalJSON implements the MarshalJSON interface to store a role as JSON.
func (r *Role) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		RoleID      uuid.UUID `json:"_id"`
		RoleName    string    `json:"rolename"`
		Description string    `json:"description,omitempty"`
		Permissions []string  `json:"permissions,omitempty"`
	}{
		RoleID:      r.ID(),
		RoleName:    r.Name(),
		Description: r.Description,
		Permissions: r.Permissions,
	})
}

// MarshalBSON implments the MarshalBSON interface to store a role as BSON.
func (r *Role) MarshalBSON() ([]byte, error) {
	return bson.Marshal(&struct {
		RoleID      string   `bson:"_id"`
		RoleName    string   `bson:"rolename"`
		Description string   `bson:"description,omitempty"`
		Permissions []string `bson:"permissions,omitempty"`
	}{
		RoleID:      r.ID().String(),
		RoleName:    r.Name(),
		Description: r.Description,
		Permissions: r.Permissions,
	})
}
