package valueobjects import ( "fmt" "time" "github.com/knowfoolery/backend/shared/errors" "github.com/knowfoolery/backend/shared/types" ) type AdminActionType string const ( AdminActionRead AdminActionType = "read" AdminActionCreate AdminActionType = "create" AdminActionUpdate AdminActionType = "update" AdminActionDelete AdminActionType = "delete" AdminActionExecute AdminActionType = "execute" AdminActionApprove AdminActionType = "approve" AdminActionReject AdminActionType = "reject" AdminActionSuspend AdminActionType = "suspend" AdminActionRestore AdminActionType = "restore" ) type AdminActionStatus string const ( AdminActionStatusPending AdminActionStatus = "pending" AdminActionStatusExecuting AdminActionStatus = "executing" AdminActionStatusCompleted AdminActionStatus = "completed" AdminActionStatusFailed AdminActionStatus = "failed" AdminActionStatusCancelled AdminActionStatus = "cancelled" ) type AdminAction struct { id types.AdminActionID adminID types.UserID adminName string sessionID types.AdminSessionID actionType AdminActionType resourceType string resourceID string status AdminActionStatus description string parameters map[string]interface{} results map[string]interface{} startedAt time.Time completedAt time.Time duration time.Duration errorMessage string requiresApproval bool approvedBy types.UserID approvedAt time.Time metadata map[string]interface{} } func NewAdminAction( id types.AdminActionID, adminID types.UserID, adminName string, sessionID types.AdminSessionID, actionType AdminActionType, resourceType, resourceID string, ) (*AdminAction, error) { if id == "" { return nil, errors.ErrValidationFailed("admin_action_id", "admin action ID cannot be empty") } if adminID == "" { return nil, errors.ErrInvalidPlayerID } if adminName == "" { return nil, errors.ErrValidationFailed("admin_name", "admin name cannot be empty") } if actionType == "" { return nil, errors.ErrValidationFailed("action_type", "action type cannot be empty") } return &AdminAction{ id: id, adminID: adminID, adminName: adminName, sessionID: sessionID, actionType: actionType, resourceType: resourceType, resourceID: resourceID, status: AdminActionStatusPending, parameters: make(map[string]interface{}), results: make(map[string]interface{}), startedAt: time.Now(), metadata: make(map[string]interface{}), }, nil } func (aa *AdminAction) ID() types.AdminActionID { return aa.id } func (aa *AdminAction) AdminID() types.UserID { return aa.adminID } func (aa *AdminAction) AdminName() string { return aa.adminName } func (aa *AdminAction) SessionID() types.AdminSessionID { return aa.sessionID } func (aa *AdminAction) ActionType() AdminActionType { return aa.actionType } func (aa *AdminAction) ResourceType() string { return aa.resourceType } func (aa *AdminAction) ResourceID() string { return aa.resourceID } func (aa *AdminAction) Status() AdminActionStatus { return aa.status } func (aa *AdminAction) Description() string { return aa.description } func (aa *AdminAction) Parameters() map[string]interface{} { result := make(map[string]interface{}) for k, v := range aa.parameters { result[k] = v } return result } func (aa *AdminAction) Results() map[string]interface{} { result := make(map[string]interface{}) for k, v := range aa.results { result[k] = v } return result } func (aa *AdminAction) StartedAt() time.Time { return aa.startedAt } func (aa *AdminAction) CompletedAt() time.Time { return aa.completedAt } func (aa *AdminAction) Duration() time.Duration { return aa.duration } func (aa *AdminAction) ErrorMessage() string { return aa.errorMessage } func (aa *AdminAction) RequiresApproval() bool { return aa.requiresApproval } func (aa *AdminAction) ApprovedBy() types.UserID { return aa.approvedBy } func (aa *AdminAction) ApprovedAt() time.Time { return aa.approvedAt } func (aa *AdminAction) Metadata() map[string]interface{} { result := make(map[string]interface{}) for k, v := range aa.metadata { result[k] = v } return result } func (aa *AdminAction) SetDescription(description string) error { if description == "" { return errors.ErrValidationFailed("description", "description cannot be empty") } aa.description = description return nil } func (aa *AdminAction) AddParameter(key string, value interface{}) error { if key == "" { return errors.ErrValidationFailed("parameter_key", "parameter key cannot be empty") } aa.parameters[key] = value return nil } func (aa *AdminAction) RemoveParameter(key string) { delete(aa.parameters, key) } func (aa *AdminAction) GetParameter(key string) (interface{}, bool) { value, exists := aa.parameters[key] return value, exists } func (aa *AdminAction) AddResult(key string, value interface{}) error { if key == "" { return errors.ErrValidationFailed("result_key", "result key cannot be empty") } aa.results[key] = value return nil } func (aa *AdminAction) RemoveResult(key string) { delete(aa.results, key) } func (aa *AdminAction) GetResult(key string) (interface{}, bool) { value, exists := aa.results[key] return value, exists } func (aa *AdminAction) SetRequiresApproval(requiresApproval bool) { aa.requiresApproval = requiresApproval } func (aa *AdminAction) Approve(approverID types.UserID) error { if approverID == "" { return errors.ErrInvalidPlayerID } if !aa.requiresApproval { return errors.ErrOperationNotAllowed("approve_action", "action does not require approval") } if aa.status != AdminActionStatusPending { return errors.ErrOperationNotAllowed("approve_action", "action is not pending") } aa.approvedBy = approverID aa.approvedAt = time.Now() return nil } func (aa *AdminAction) StartExecution() error { if aa.requiresApproval && aa.approvedBy == "" { return errors.ErrOperationNotAllowed("start_execution", "action requires approval") } if aa.status != AdminActionStatusPending { return errors.ErrOperationNotAllowed("start_execution", "action is not pending") } aa.status = AdminActionStatusExecuting aa.startedAt = time.Now() return nil } func (aa *AdminAction) CompleteExecution() error { if aa.status != AdminActionStatusExecuting { return errors.ErrOperationNotAllowed("complete_execution", "action is not executing") } now := time.Now() aa.status = AdminActionStatusCompleted aa.completedAt = now aa.duration = now.Sub(aa.startedAt) aa.errorMessage = "" return nil } func (aa *AdminAction) FailExecution(errorMessage string) error { if aa.status != AdminActionStatusExecuting { return errors.ErrOperationNotAllowed("fail_execution", "action is not executing") } now := time.Now() aa.status = AdminActionStatusFailed aa.completedAt = now aa.duration = now.Sub(aa.startedAt) aa.errorMessage = errorMessage return nil } func (aa *AdminAction) CancelExecution() error { if aa.status != AdminActionStatusPending && aa.status != AdminActionStatusExecuting { return errors.ErrOperationNotAllowed("cancel_execution", "action cannot be cancelled") } now := time.Now() aa.status = AdminActionStatusCancelled aa.completedAt = now if aa.status == AdminActionStatusExecuting { aa.duration = now.Sub(aa.startedAt) } return nil } func (aa *AdminAction) SetMetadata(key string, value interface{}) error { if key == "" { return errors.ErrInvalidMetadata } aa.metadata[key] = value return nil } func (aa *AdminAction) RemoveMetadata(key string) { delete(aa.metadata, key) } func (aa *AdminAction) IsCompleted() bool { return aa.status == AdminActionStatusCompleted } func (aa *AdminAction) IsFailed() bool { return aa.status == AdminActionStatusFailed } func (aa *AdminAction) IsCancelled() bool { return aa.status == AdminActionStatusCancelled } func (aa *AdminAction) IsFinished() bool { return aa.IsCompleted() || aa.IsFailed() || aa.IsCancelled() } func (aa *AdminAction) IsExecuting() bool { return aa.status == AdminActionStatusExecuting } func (aa *AdminAction) IsPending() bool { return aa.status == AdminActionStatusPending } func (aa *AdminAction) IsApproved() bool { return !aa.requiresApproval || aa.approvedBy != "" } func (aa *AdminAction) GetElapsedTime() time.Duration { if aa.IsFinished() { return aa.duration } return time.Since(aa.startedAt) } func (aa *AdminAction) IsHighRisk() bool { switch aa.actionType { case AdminActionDelete, AdminActionSuspend: return true default: switch aa.resourceType { case "admin", "system_config": return true default: return false } } } func (aa *AdminAction) GetResourceIdentifier() string { if aa.resourceID != "" { return fmt.Sprintf("%s/%s", aa.resourceType, aa.resourceID) } return aa.resourceType } func (aa *AdminAction) String() string { return fmt.Sprintf("AdminAction{ID: %s, Type: %s, Resource: %s, Status: %s, Admin: %s}", aa.id, aa.actionType, aa.GetResourceIdentifier(), aa.status, aa.adminName) } type AdminActionSnapshot struct { ID types.AdminActionID `json:"id"` AdminID types.UserID `json:"admin_id"` AdminName string `json:"admin_name"` SessionID types.AdminSessionID `json:"session_id"` ActionType AdminActionType `json:"action_type"` ResourceType string `json:"resource_type"` ResourceID string `json:"resource_id"` Status AdminActionStatus `json:"status"` Description string `json:"description"` Parameters map[string]interface{} `json:"parameters"` Results map[string]interface{} `json:"results"` StartedAt time.Time `json:"started_at"` CompletedAt time.Time `json:"completed_at"` Duration time.Duration `json:"duration"` ErrorMessage string `json:"error_message"` RequiresApproval bool `json:"requires_approval"` ApprovedBy types.UserID `json:"approved_by"` ApprovedAt time.Time `json:"approved_at"` Metadata map[string]interface{} `json:"metadata"` } func (aa *AdminAction) ToSnapshot() AdminActionSnapshot { return AdminActionSnapshot{ ID: aa.id, AdminID: aa.adminID, AdminName: aa.adminName, SessionID: aa.sessionID, ActionType: aa.actionType, ResourceType: aa.resourceType, ResourceID: aa.resourceID, Status: aa.status, Description: aa.description, Parameters: aa.Parameters(), Results: aa.Results(), StartedAt: aa.startedAt, CompletedAt: aa.completedAt, Duration: aa.duration, ErrorMessage: aa.errorMessage, RequiresApproval: aa.requiresApproval, ApprovedBy: aa.approvedBy, ApprovedAt: aa.approvedAt, Metadata: aa.Metadata(), } } func (aa *AdminAction) FromSnapshot(snapshot AdminActionSnapshot) error { if snapshot.ID == "" { return errors.ErrValidationFailed("admin_action_id", "admin action ID cannot be empty") } if snapshot.AdminID == "" { return errors.ErrInvalidPlayerID } aa.id = snapshot.ID aa.adminID = snapshot.AdminID aa.adminName = snapshot.AdminName aa.sessionID = snapshot.SessionID aa.actionType = snapshot.ActionType aa.resourceType = snapshot.ResourceType aa.resourceID = snapshot.ResourceID aa.status = snapshot.Status aa.description = snapshot.Description aa.startedAt = snapshot.StartedAt aa.completedAt = snapshot.CompletedAt aa.duration = snapshot.Duration aa.errorMessage = snapshot.ErrorMessage aa.requiresApproval = snapshot.RequiresApproval aa.approvedBy = snapshot.ApprovedBy aa.approvedAt = snapshot.ApprovedAt aa.parameters = make(map[string]interface{}) for k, v := range snapshot.Parameters { aa.parameters[k] = v } aa.results = make(map[string]interface{}) for k, v := range snapshot.Results { aa.results[k] = v } aa.metadata = make(map[string]interface{}) for k, v := range snapshot.Metadata { aa.metadata[k] = v } return nil }