package rbac

import (
	"encoding/json"

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

// User represents the data of a user for access control and is stored in a storage.
type User struct {
	UserID   uuid.UUID         `json:"_id"`
	Metadata conflict.Metadata `json:"metadata"`
	UserName string            `json:"username"`
	Roles    map[string]string `json:"roles,omitempty"`
	Password string            `json:"password"`
	Token    string            `json:"token,omitempty"`
	Salt     string            `json:"salt"`
}

// NewUser creates a new user.
func NewUser(id uuid.UUID,
	name string,
	roles map[string]string,
	pw string,
	token string,
	salt string,
	metadata conflict.Metadata,
) rbac.User {
	return &User{
		UserID:   id,
		UserName: name,
		Roles:    roles,
		Password: pw,
		Token:    token,
		Salt:     salt,
		Metadata: metadata,
	}
}

// ID returns a UUID of the user.
func (u *User) ID() uuid.UUID {
	return u.UserID
}

// GetMetadata returns the metadata of a user.
func (u *User) GetMetadata() conflict.Metadata {
	return u.Metadata
}

// Name returns the name of the user.
func (u *User) Name() string {
	return u.UserName
}

// GetPassword returns the password of the user.
func (u *User) GetPassword() string {
	return u.Password
}

// GetRoles returns the roles of the user.
func (u *User) GetRoles() map[string]string {
	return u.Roles
}

// GetToken returns the token of the user.
func (u *User) GetToken() string {
	return u.Token
}

// SetName sets the name of the user.
func (u *User) SetName(name string) {
	u.UserName = name
}

// SetToken sets the token of the user.
func (u *User) SetToken(token string) {
	u.Token = token
}

// GetSalt returns the salt of the user.
func (u *User) GetSalt() string {
	return u.Salt
}

// MarshalJSON implements the MarshalJSON interface to store a user as JSON.
func (u *User) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		UserID   uuid.UUID         `json:"_id"`
		UserName string            `json:"username"`
		Roles    map[string]string `json:"roles,omitempty"`
		Password string            `json:"password"`
		Token    string            `json:"token,omitempty"`
		Salt     string            `json:"salt"`
		Metadata conflict.Metadata `json:"metadata"`
	}{
		UserID:   u.ID(),
		UserName: u.Name(),
		Roles:    u.Roles,
		Password: u.Password,
		Token:    u.Token,
		Salt:     u.Salt,
		Metadata: u.Metadata,
	})
}

// MarshalBSON implments the MarshalBSON interface to store a user as BSON.
func (u *User) MarshalBSON() ([]byte, error) {
	return bson.Marshal(&struct {
		UserID   string            `bson:"_id"`
		UserName string            `bson:"username"`
		Roles    map[string]string `bson:"roles,omitempty"`
		Password string            `bson:"password"`
		Token    string            `bson:"token,omitempty"`
		Salt     string            `bson:"salt"`
		Metadata conflict.Metadata `bson:"metadata"`
	}{
		UserID:   u.ID().String(),
		UserName: u.Name(),
		Roles:    u.Roles,
		Password: u.Password,
		Token:    u.Token,
		Salt:     u.Salt,
		Metadata: u.Metadata,
	})
}
