2025-11-15 15:46:24 +00:00
package service
import (
"errors"
2025-11-15 15:59:30 +00:00
"jaecoo-be/app/database/entity"
"jaecoo-be/app/module/chat/mapper"
"jaecoo-be/app/module/chat/repository"
"jaecoo-be/app/module/chat/request"
"jaecoo-be/app/module/chat/response"
usersRepository "jaecoo-be/app/module/users/repository"
"jaecoo-be/utils/paginator"
utilSvc "jaecoo-be/utils/service"
2025-11-15 15:46:24 +00:00
"time"
"github.com/rs/zerolog"
)
type chatService struct {
Repo repository . ChatRepository
UsersRepo usersRepository . UsersRepository
Log zerolog . Logger
}
type ChatService interface {
// Chat Session methods
GetAllChatSessions ( authToken string , req request . ChatSessionQueryRequest ) ( chatSessions [ ] * response . ChatSessionResponse , paging paginator . Pagination , err error )
GetChatSessionByID ( authToken string , id uint ) ( chatSession * response . ChatSessionResponse , err error )
CreateChatSession ( authToken string , req request . ChatSessionCreateRequest ) ( chatSession * response . ChatSessionResponse , err error )
UpdateChatSession ( authToken string , id uint , req request . ChatSessionUpdateRequest ) ( err error )
DeleteChatSession ( authToken string , id uint ) error
// Chat Message methods
GetAllChatMessages ( authToken string , req request . ChatMessageQueryRequest ) ( chatMessages [ ] * response . ChatMessageResponse , paging paginator . Pagination , err error )
GetChatMessageByID ( authToken string , id uint ) ( chatMessage * response . ChatMessageResponse , err error )
CreateChatMessage ( authToken string , req request . ChatMessageCreateRequest ) ( chatMessage * response . ChatMessageResponse , err error )
UpdateChatMessage ( authToken string , id uint , req request . ChatMessageUpdateRequest ) ( err error )
DeleteChatMessage ( authToken string , id uint ) error
// Chat Participant methods
AddParticipantToChat ( authToken string , chatSessionID uint , participantUserID uint ) error
RemoveParticipantFromChat ( authToken string , chatSessionID uint , participantUserID uint ) error
}
func NewChatService ( repo repository . ChatRepository , usersRepo usersRepository . UsersRepository , log zerolog . Logger ) ChatService {
return & chatService {
Repo : repo ,
UsersRepo : usersRepo ,
Log : log ,
}
}
// Chat Session Service Methods
func ( _i * chatService ) GetAllChatSessions ( authToken string , req request . ChatSessionQueryRequest ) ( chatSessions [ ] * response . ChatSessionResponse , paging paginator . Pagination , err error ) {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return nil , paginator . Pagination { } , errors . New ( "user not found" )
}
results , paging , err := _i . Repo . GetAllChatSessions ( userInfo . ID , req )
if err != nil {
return
}
for _ , result := range results {
chatSessions = append ( chatSessions , mapper . ChatSessionResponseMapper ( result ) )
}
return
}
func ( _i * chatService ) GetChatSessionByID ( authToken string , id uint ) ( chatSession * response . ChatSessionResponse , err error ) {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return nil , errors . New ( "user not found" )
}
result , err := _i . Repo . FindChatSessionByUserAndID ( userInfo . ID , id )
if err != nil {
return nil , err
}
if result == nil {
return nil , errors . New ( "chat session not found or access denied" )
}
return mapper . ChatSessionResponseMapper ( result ) , nil
}
func ( _i * chatService ) CreateChatSession ( authToken string , req request . ChatSessionCreateRequest ) ( chatSession * response . ChatSessionResponse , err error ) {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return nil , errors . New ( "user not found" )
}
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "Creating chat session" )
// Validate business rules
if req . Type == "personal" && len ( req . UserIDs ) != 1 {
return nil , errors . New ( "personal chat must have exactly one other participant" )
}
if req . Type == "group" && len ( req . UserIDs ) < 1 {
return nil , errors . New ( "group chat must have at least one participant" )
}
// Check if personal chat already exists
if req . Type == "personal" {
existingChat , err := _i . Repo . FindPersonalChatSession ( userInfo . ID , req . UserIDs [ 0 ] )
if err == nil && existingChat != nil {
return mapper . ChatSessionResponseMapper ( existingChat ) , nil
}
}
// Validate all user IDs exist
for _ , userID := range req . UserIDs {
user , err := _i . UsersRepo . FindOne ( userID )
if err != nil || user == nil {
return nil , errors . New ( "invalid user ID: " + string ( rune ( userID ) ) )
}
}
// Create chat session
entity := req . ToEntity ( userInfo . ID )
result , err := _i . Repo . CreateChatSession ( entity )
if err != nil {
return nil , err
}
// Add creator as participant
creatorParticipant := & request . ChatParticipantCreateRequest {
ChatSessionID : result . ID ,
UserID : userInfo . ID ,
}
_ , err = _i . Repo . CreateChatParticipant ( creatorParticipant . ToEntity ( ) )
if err != nil {
return nil , err
}
// Add other participants
for _ , userID := range req . UserIDs {
participant := & request . ChatParticipantCreateRequest {
ChatSessionID : result . ID ,
UserID : userID ,
}
_ , err = _i . Repo . CreateChatParticipant ( participant . ToEntity ( ) )
if err != nil {
return nil , err
}
}
// Reload with all relationships
finalResult , err := _i . Repo . FindChatSessionByID ( result . ID )
if err != nil {
return nil , err
}
return mapper . ChatSessionResponseMapper ( finalResult ) , nil
}
func ( _i * chatService ) UpdateChatSession ( authToken string , id uint , req request . ChatSessionUpdateRequest ) ( err error ) {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return errors . New ( "user not found" )
}
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "Updating chat session" )
// Check if chat session exists and user has access
existing , err := _i . Repo . FindChatSessionByUserAndID ( userInfo . ID , id )
if err != nil {
return err
}
if existing == nil {
return errors . New ( "chat session not found or access denied" )
}
// Only creator can update chat session
if existing . CreatedBy != userInfo . ID {
return errors . New ( "only chat creator can update chat session" )
}
entity := req . ToEntity ( )
return _i . Repo . UpdateChatSession ( id , entity )
}
func ( _i * chatService ) DeleteChatSession ( authToken string , id uint ) error {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return errors . New ( "user not found" )
}
_i . Log . Info ( ) . Uint ( "userId" , userInfo . ID ) . Uint ( "id" , id ) . Msg ( "Deleting chat session" )
// Check if chat session exists and user has access
existing , err := _i . Repo . FindChatSessionByUserAndID ( userInfo . ID , id )
if err != nil {
return err
}
if existing == nil {
return errors . New ( "chat session not found or access denied" )
}
// Only creator can delete chat session
if existing . CreatedBy != userInfo . ID {
return errors . New ( "only chat creator can delete chat session" )
}
return _i . Repo . DeleteChatSession ( id )
}
// Chat Message Service Methods
func ( _i * chatService ) GetAllChatMessages ( authToken string , req request . ChatMessageQueryRequest ) ( chatMessages [ ] * response . ChatMessageResponse , paging paginator . Pagination , err error ) {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return nil , paginator . Pagination { } , errors . New ( "user not found" )
}
// Set user ID in request for repository
req . UserID = userInfo . ID
results , paging , err := _i . Repo . GetAllChatMessages ( req )
if err != nil {
return
}
for _ , result := range results {
chatMessages = append ( chatMessages , mapper . ChatMessageResponseMapper ( result ) )
}
return
}
func ( _i * chatService ) GetChatMessageByID ( authToken string , id uint ) ( chatMessage * response . ChatMessageResponse , err error ) {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return nil , errors . New ( "user not found" )
}
result , err := _i . Repo . FindChatMessageByUserAndID ( userInfo . ID , id )
if err != nil {
return nil , err
}
if result == nil {
return nil , errors . New ( "chat message not found or access denied" )
}
return mapper . ChatMessageResponseMapper ( result ) , nil
}
func ( _i * chatService ) CreateChatMessage ( authToken string , req request . ChatMessageCreateRequest ) ( chatMessage * response . ChatMessageResponse , err error ) {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return nil , errors . New ( "user not found" )
}
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "Creating chat message" )
// Check if user is participant in the chat session
isParticipant , err := _i . Repo . CheckUserInChatSession ( userInfo . ID , req . ChatSessionID )
if err != nil {
return nil , err
}
if ! isParticipant {
return nil , errors . New ( "user is not a participant in this chat session" )
}
entity := req . ToEntity ( userInfo . ID )
result , err := _i . Repo . CreateChatMessage ( entity )
if err != nil {
return nil , err
}
return mapper . ChatMessageResponseMapper ( result ) , nil
}
func ( _i * chatService ) UpdateChatMessage ( authToken string , id uint , req request . ChatMessageUpdateRequest ) ( err error ) {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return errors . New ( "user not found" )
}
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "Updating chat message" )
// Check if message exists and user has access
existing , err := _i . Repo . FindChatMessageByUserAndID ( userInfo . ID , id )
if err != nil {
return err
}
if existing == nil {
return errors . New ( "chat message not found or access denied" )
}
// Only sender can update message
if existing . SenderID != userInfo . ID {
return errors . New ( "only message sender can update message" )
}
now := time . Now ( )
entity := req . ToEntity ( )
entity . EditedAt = & now
return _i . Repo . UpdateChatMessage ( id , entity )
}
func ( _i * chatService ) DeleteChatMessage ( authToken string , id uint ) error {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return errors . New ( "user not found" )
}
_i . Log . Info ( ) . Uint ( "userId" , userInfo . ID ) . Uint ( "id" , id ) . Msg ( "Deleting chat message" )
// Check if message exists and user has access
existing , err := _i . Repo . FindChatMessageByUserAndID ( userInfo . ID , id )
if err != nil {
return err
}
if existing == nil {
return errors . New ( "chat message not found or access denied" )
}
// Only sender can delete message
if existing . SenderID != userInfo . ID {
return errors . New ( "only message sender can delete message" )
}
return _i . Repo . DeleteChatMessage ( id )
}
// Chat Participant Service Methods
func ( _i * chatService ) AddParticipantToChat ( authToken string , chatSessionID uint , participantUserID uint ) error {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return errors . New ( "user not found" )
}
_i . Log . Info ( ) . Uint ( "userId" , userInfo . ID ) . Uint ( "chatSessionID" , chatSessionID ) . Uint ( "participantUserID" , participantUserID ) . Msg ( "Adding participant to chat" )
// Check if user has access to chat session
existing , err := _i . Repo . FindChatSessionByUserAndID ( userInfo . ID , chatSessionID )
if err != nil {
return err
}
if existing == nil {
return errors . New ( "chat session not found or access denied" )
}
// Only creator can add participants
if existing . CreatedBy != userInfo . ID {
return errors . New ( "only chat creator can add participants" )
}
// Validate participant user exists
user , err := _i . UsersRepo . FindOne ( participantUserID )
if err != nil || user == nil {
return errors . New ( "invalid user ID" )
}
// Check if user is already a participant
isParticipant , err := _i . Repo . CheckUserInChatSession ( participantUserID , chatSessionID )
if err != nil {
return err
}
if isParticipant {
return errors . New ( "user is already a participant in this chat" )
}
participant := & request . ChatParticipantCreateRequest {
ChatSessionID : chatSessionID ,
UserID : participantUserID ,
}
_ , err = _i . Repo . CreateChatParticipant ( participant . ToEntity ( ) )
return err
}
func ( _i * chatService ) RemoveParticipantFromChat ( authToken string , chatSessionID uint , participantUserID uint ) error {
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . UsersRepo , authToken )
if userInfo == nil {
return errors . New ( "user not found" )
}
_i . Log . Info ( ) . Uint ( "userId" , userInfo . ID ) . Uint ( "chatSessionID" , chatSessionID ) . Uint ( "participantUserID" , participantUserID ) . Msg ( "Removing participant from chat" )
// Check if user has access to chat session
existing , err := _i . Repo . FindChatSessionByUserAndID ( userInfo . ID , chatSessionID )
if err != nil {
return err
}
if existing == nil {
return errors . New ( "chat session not found or access denied" )
}
// Only creator can remove participants (or user can remove themselves)
if existing . CreatedBy != userInfo . ID && userInfo . ID != participantUserID {
return errors . New ( "only chat creator can remove participants or user can remove themselves" )
}
// Find participant
participants , err := _i . Repo . FindChatParticipantsBySessionID ( chatSessionID )
if err != nil {
return err
}
var participantToRemove * entity . ChatParticipants
for _ , participant := range participants {
if participant . UserID == participantUserID {
participantToRemove = participant
break
}
}
if participantToRemove == nil {
return errors . New ( "participant not found" )
}
// Soft delete by setting is_active to false
now := time . Now ( )
participantToRemove . IsActive = false
participantToRemove . LeftAt = & now
return _i . Repo . UpdateChatParticipant ( participantToRemove . ID , participantToRemove )
}