package transformer import ( "eslogad-be/internal/contract" "eslogad-be/internal/entities" "github.com/google/uuid" ) func LetterEntityToContract(e *entities.LetterIncoming, attachments []entities.LetterIncomingAttachment, refs ...interface{}) *contract.IncomingLetterResponse { resp := &contract.IncomingLetterResponse{ ID: e.ID, LetterNumber: e.LetterNumber, ReferenceNumber: e.ReferenceNumber, Subject: e.Subject, Description: e.Description, ReceivedDate: e.ReceivedDate, DueDate: e.DueDate, Status: string(e.Status), CreatedBy: e.CreatedBy, CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt, Attachments: make([]contract.IncomingLetterAttachmentResponse, 0, len(attachments)), } // optional refs: allow passing already-fetched related objects // expected ordering (if provided): *entities.Priority, *entities.Institution for _, r := range refs { switch v := r.(type) { case *entities.Priority: if v != nil { resp.Priority = &contract.PriorityResponse{ ID: v.ID.String(), Name: v.Name, Level: v.Level, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, } } case *entities.Institution: if v != nil { resp.SenderInstitution = &contract.InstitutionResponse{ ID: v.ID.String(), Name: v.Name, Type: string(v.Type), Address: v.Address, ContactPerson: v.ContactPerson, Phone: v.Phone, Email: v.Email, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, } } } } for _, a := range attachments { resp.Attachments = append(resp.Attachments, contract.IncomingLetterAttachmentResponse{ ID: a.ID, FileURL: a.FileURL, FileName: a.FileName, FileType: a.FileType, UploadedAt: a.UploadedAt, }) } return resp } func DispositionsToContract(list []entities.LetterIncomingDisposition) []contract.DispositionResponse { out := make([]contract.DispositionResponse, 0, len(list)) for _, d := range list { out = append(out, DispoToContract(d)) } return out } func DispoToContract(d entities.LetterIncomingDisposition) contract.DispositionResponse { return contract.DispositionResponse{ ID: d.ID, LetterID: d.LetterID, DepartmentID: d.DepartmentID, Notes: d.Notes, ReadAt: d.ReadAt, CreatedBy: d.CreatedBy, CreatedAt: d.CreatedAt, UpdatedAt: d.UpdatedAt, } } func EnhancedDispositionsToContract(list []entities.LetterIncomingDisposition) []contract.EnhancedDispositionResponse { out := make([]contract.EnhancedDispositionResponse, 0, len(list)) for _, d := range list { resp := contract.EnhancedDispositionResponse{ ID: d.ID, LetterID: d.LetterID, DepartmentID: d.DepartmentID, Notes: d.Notes, ReadAt: d.ReadAt, CreatedBy: d.CreatedBy, CreatedAt: d.CreatedAt, UpdatedAt: d.UpdatedAt, Departments: []contract.DispositionDepartmentResponse{}, Actions: []contract.DispositionActionSelectionResponse{}, DispositionNotes: []contract.DispositionNoteResponse{}, } out = append(out, resp) } return out } func DispositionDepartmentsToContract(list []entities.LetterIncomingDispositionDepartment) []contract.DispositionDepartmentResponse { out := make([]contract.DispositionDepartmentResponse, 0, len(list)) for _, d := range list { resp := contract.DispositionDepartmentResponse{ ID: d.ID, DepartmentID: d.DepartmentID, CreatedAt: d.CreatedAt, } out = append(out, resp) } return out } func DispositionDepartmentsWithDetailsToContract(list []entities.LetterIncomingDispositionDepartment) []contract.DispositionDepartmentResponse { out := make([]contract.DispositionDepartmentResponse, 0, len(list)) for _, d := range list { resp := contract.DispositionDepartmentResponse{ ID: d.ID, DepartmentID: d.DepartmentID, CreatedAt: d.CreatedAt, } // Include department details if preloaded if d.Department != nil { resp.Department = &contract.DepartmentResponse{ ID: d.Department.ID, Name: d.Department.Name, Code: d.Department.Code, Path: d.Department.Path, } } out = append(out, resp) } return out } func DispositionActionSelectionsToContract(list []entities.LetterDispositionActionSelection) []contract.DispositionActionSelectionResponse { out := make([]contract.DispositionActionSelectionResponse, 0, len(list)) for _, d := range list { resp := contract.DispositionActionSelectionResponse{ ID: d.ID, ActionID: d.ActionID, Action: nil, // Will be populated by processor Note: d.Note, CreatedBy: d.CreatedBy, CreatedAt: d.CreatedAt, } out = append(out, resp) } return out } func DispositionActionSelectionsWithDetailsToContract(list []entities.LetterDispositionActionSelection) []contract.DispositionActionSelectionResponse { out := make([]contract.DispositionActionSelectionResponse, 0, len(list)) for _, d := range list { resp := contract.DispositionActionSelectionResponse{ ID: d.ID, ActionID: d.ActionID, Action: nil, // Will be populated by processor Note: d.Note, CreatedBy: d.CreatedBy, CreatedAt: d.CreatedAt, } // Include action details if preloaded if d.Action != nil { resp.Action = &contract.DispositionActionResponse{ ID: d.Action.ID.String(), Code: d.Action.Code, Label: d.Action.Label, Description: d.Action.Description, RequiresNote: d.Action.RequiresNote, GroupName: d.Action.GroupName, SortOrder: d.Action.SortOrder, IsActive: d.Action.IsActive, CreatedAt: d.Action.CreatedAt, UpdatedAt: d.Action.UpdatedAt, } } out = append(out, resp) } return out } func DispositionNotesToContract(list []entities.DispositionNote) []contract.DispositionNoteResponse { out := make([]contract.DispositionNoteResponse, 0, len(list)) for _, d := range list { resp := contract.DispositionNoteResponse{ ID: d.ID, UserID: d.UserID, Note: d.Note, CreatedAt: d.CreatedAt, } out = append(out, resp) } return out } func DispositionNotesWithDetailsToContract(list []entities.DispositionNote) []contract.DispositionNoteResponse { out := make([]contract.DispositionNoteResponse, 0, len(list)) for _, d := range list { resp := contract.DispositionNoteResponse{ ID: d.ID, UserID: d.UserID, Note: d.Note, CreatedAt: d.CreatedAt, } // Include user details if preloaded if d.User != nil { resp.User = &contract.UserResponse{ ID: d.User.ID, Name: d.User.Name, Email: d.User.Email, IsActive: d.User.IsActive, CreatedAt: d.User.CreatedAt, UpdatedAt: d.User.UpdatedAt, } } out = append(out, resp) } return out } func DiscussionEntityToContract(e *entities.LetterDiscussion) *contract.LetterDiscussionResponse { var mentions map[string]interface{} if e.Mentions != nil { mentions = map[string]interface{}(e.Mentions) } return &contract.LetterDiscussionResponse{ ID: e.ID, LetterID: e.LetterID, ParentID: e.ParentID, UserID: e.UserID, Message: e.Message, Mentions: mentions, CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt, EditedAt: e.EditedAt, } } func DiscussionsWithPreloadedDataToContract(list []entities.LetterDiscussion, mentionedUsers []entities.User) []contract.LetterDiscussionResponse { // Create a map for efficient user lookup userMap := make(map[uuid.UUID]entities.User) for _, user := range mentionedUsers { userMap[user.ID] = user } out := make([]contract.LetterDiscussionResponse, 0, len(list)) for _, d := range list { resp := contract.LetterDiscussionResponse{ ID: d.ID, LetterID: d.LetterID, ParentID: d.ParentID, UserID: d.UserID, Message: d.Message, Mentions: map[string]interface{}(d.Mentions), CreatedAt: d.CreatedAt, UpdatedAt: d.UpdatedAt, EditedAt: d.EditedAt, } // Include user profile if preloaded if d.User != nil { resp.User = &contract.UserResponse{ ID: d.User.ID, Name: d.User.Name, Email: d.User.Email, IsActive: d.User.IsActive, CreatedAt: d.User.CreatedAt, UpdatedAt: d.User.UpdatedAt, } // Include user profile if available if d.User.Profile != nil { resp.User.Profile = &contract.UserProfileResponse{ UserID: d.User.Profile.UserID, FullName: d.User.Profile.FullName, DisplayName: d.User.Profile.DisplayName, Phone: d.User.Profile.Phone, AvatarURL: d.User.Profile.AvatarURL, JobTitle: d.User.Profile.JobTitle, EmployeeNo: d.User.Profile.EmployeeNo, Bio: d.User.Profile.Bio, Timezone: d.User.Profile.Timezone, Locale: d.User.Profile.Locale, } } } // Process mentions to get mentioned users with profiles if d.Mentions != nil { mentions := map[string]interface{}(d.Mentions) if userIDs, ok := mentions["user_ids"]; ok { if userIDList, ok := userIDs.([]interface{}); ok { mentionedUsersList := make([]contract.UserResponse, 0) for _, userID := range userIDList { if userIDStr, ok := userID.(string); ok { if userUUID, err := uuid.Parse(userIDStr); err == nil { if user, exists := userMap[userUUID]; exists { userResp := contract.UserResponse{ ID: user.ID, Name: user.Name, Email: user.Email, IsActive: user.IsActive, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, } // Include user profile if available if user.Profile != nil { userResp.Profile = &contract.UserProfileResponse{ UserID: user.Profile.UserID, FullName: user.Profile.FullName, DisplayName: user.Profile.DisplayName, Phone: user.Profile.Phone, AvatarURL: user.Profile.AvatarURL, JobTitle: user.Profile.JobTitle, EmployeeNo: user.Profile.EmployeeNo, Bio: user.Profile.Bio, Timezone: user.Profile.Timezone, Locale: user.Profile.Locale, } } mentionedUsersList = append(mentionedUsersList, userResp) } } } } resp.MentionedUsers = mentionedUsersList } } } out = append(out, resp) } return out } func EnhancedDispositionsWithPreloadedDataToContract(list []entities.LetterIncomingDisposition) []contract.EnhancedDispositionResponse { out := make([]contract.EnhancedDispositionResponse, 0, len(list)) for _, d := range list { resp := contract.EnhancedDispositionResponse{ ID: d.ID, LetterID: d.LetterID, DepartmentID: d.DepartmentID, Notes: d.Notes, ReadAt: d.ReadAt, CreatedBy: d.CreatedBy, CreatedAt: d.CreatedAt, UpdatedAt: d.UpdatedAt, Departments: []contract.DispositionDepartmentResponse{}, Actions: []contract.DispositionActionSelectionResponse{}, DispositionNotes: []contract.DispositionNoteResponse{}, Department: DepartmentToContract(d.Department), } if len(d.Departments) > 0 { resp.Departments = DispositionDepartmentsWithDetailsToContract(d.Departments) } // Include preloaded action selections with details if len(d.ActionSelections) > 0 { resp.Actions = DispositionActionSelectionsWithDetailsToContract(d.ActionSelections) } // Include preloaded notes with user details if len(d.DispositionNotes) > 0 { resp.DispositionNotes = DispositionNotesWithDetailsToContract(d.DispositionNotes) } out = append(out, resp) } return out }