package service import ( "errors" "narasi-ahli-be/app/database/entity" "narasi-ahli-be/app/module/chat/mapper" "narasi-ahli-be/app/module/chat/repository" "narasi-ahli-be/app/module/chat/request" usersRepository "narasi-ahli-be/app/module/users/repository" "narasi-ahli-be/utils/paginator" utilSvc "narasi-ahli-be/utils/service" "time" "github.com/rs/zerolog" ) type chatScheduleService struct { chatScheduleRepository repository.ChatScheduleRepository chatRepository repository.ChatRepository chatService ChatService chatScheduleMapper *mapper.ChatScheduleMapper Log zerolog.Logger UsersRepo usersRepository.UsersRepository } type ChatScheduleService interface { // Chat Schedule CRUD operations CreateChatSchedule(authToken string, req request.ChatScheduleCreateRequest) (dataResult *mapper.ChatScheduleResponse, err error) GetAllChatSchedules(authToken string, req request.ChatScheduleQueryRequest) (schedules []*mapper.ChatScheduleResponse, paging paginator.Pagination, err error) GetChatScheduleByID(authToken string, id uint) (schedule *mapper.ChatScheduleResponse, err error) UpdateChatSchedule(authToken string, id uint, req request.ChatScheduleUpdateRequest) (err error) DeleteChatSchedule(authToken string, id uint) (err error) // Additional schedule operations GetUpcomingSchedules(authToken string, limit int) (schedules []*mapper.ChatScheduleResponse, err error) GetSchedulesByStatus(authToken string, status string) (schedules []*mapper.ChatScheduleResponse, err error) SendScheduleReminder(authToken string, scheduleID uint) (err error) } func NewChatScheduleService( chatScheduleRepository repository.ChatScheduleRepository, chatRepository repository.ChatRepository, chatService ChatService, log zerolog.Logger, usersRepo usersRepository.UsersRepository, ) ChatScheduleService { return &chatScheduleService{ chatScheduleRepository: chatScheduleRepository, chatRepository: chatRepository, chatService: chatService, chatScheduleMapper: mapper.NewChatScheduleMapper(), Log: log, UsersRepo: usersRepo, } } // CreateChatSchedule - Create a new chat schedule func (_i *chatScheduleService) CreateChatSchedule(authToken string, req request.ChatScheduleCreateRequest) (dataResult *mapper.ChatScheduleResponse, err error) { userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if userInfo == nil { return nil, errors.New("user not found") } userID := userInfo.ID // Validate that the scheduled time is in the future if req.ScheduledAt.Before(time.Now()) { return nil, errors.New("scheduled time must be in the future") } var chatSessionID uint // If ChatSessionID is not provided, create a new chat session if req.ChatSessionID == nil { // Create a new personal chat session for the schedule chatSessionReq := request.ChatSessionCreateRequest{ Name: &req.Title, // Use schedule title as chat session name Type: "group", // Default to group chat for schedules UserIDs: []uint{}, // Empty for now, can be populated later } chatSession, err := _i.chatService.CreateChatSession(authToken, chatSessionReq) if err != nil { return nil, errors.New("failed to create chat session: " + err.Error()) } chatSessionID = chatSession.ID } else { chatSessionID = *req.ChatSessionID // Check if user is participant in the existing chat session isParticipant, err := _i.chatRepository.CheckUserInChatSession(userID, chatSessionID) if err != nil { return nil, err } if !isParticipant { return nil, errors.New("user is not a participant in this chat session") } } // Convert request to entity schedule := _i.chatScheduleMapper.ToEntity(req) schedule.ChatSessionID = chatSessionID schedule.CreatedBy = userID // Create schedule result, err := _i.chatScheduleRepository.CreateChatSchedule(schedule) if err != nil { return nil, err } // Convert to response dataResult = _i.chatScheduleMapper.ToResponse(result) return } // GetAllChatSchedules - Get all chat schedules for a user func (_i *chatScheduleService) GetAllChatSchedules(authToken string, req request.ChatScheduleQueryRequest) (schedules []*mapper.ChatScheduleResponse, 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") } userID := userInfo.ID // Get schedules from repository scheduleEntities, paging, err := _i.chatScheduleRepository.GetAllChatSchedules(userID, req) if err != nil { return nil, paginator.Pagination{}, err } // Convert to response schedules = _i.chatScheduleMapper.ToResponseList(scheduleEntities) return } // GetChatScheduleByID - Get a specific chat schedule func (_i *chatScheduleService) GetChatScheduleByID(authToken string, id uint) (schedule *mapper.ChatScheduleResponse, err error) { // Get schedule from repository scheduleEntity, err := _i.chatScheduleRepository.GetChatScheduleByID(id) if err != nil { return nil, err } // Convert to response schedule = _i.chatScheduleMapper.ToResponse(scheduleEntity) return } // UpdateChatSchedule - Update a chat schedule func (_i *chatScheduleService) UpdateChatSchedule(authToken string, id uint, req request.ChatScheduleUpdateRequest) (err error) { userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if userInfo == nil { return errors.New("user not found") } userID := userInfo.ID // Check if user is participant in the chat session isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, id) if err != nil { return err } if !isParticipant { return errors.New("user is not a participant in this chat session") } // Get existing schedule to check if user is the creator existingSchedule, err := _i.chatScheduleRepository.GetChatScheduleByID(id) if err != nil { return err } // Only creator can update the schedule if existingSchedule.CreatedBy != userID { return errors.New("only the creator can update this schedule") } // Validate scheduled time if provided if !req.ScheduledAt.IsZero() && req.ScheduledAt.Before(time.Now()) { return errors.New("scheduled time must be in the future") } // Convert request to entity schedule := _i.chatScheduleMapper.ToUpdateEntity(req) // Update schedule err = _i.chatScheduleRepository.UpdateChatSchedule(id, schedule) return } // DeleteChatSchedule - Delete a chat schedule func (_i *chatScheduleService) DeleteChatSchedule(authToken string, id uint) (err error) { userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if userInfo == nil { return errors.New("user not found") } userID := userInfo.ID // Get existing schedule to check if user is the creator existingSchedule, err := _i.chatScheduleRepository.GetChatScheduleByID(id) if err != nil { return err } // Only creator can delete the schedule if existingSchedule.CreatedBy != userID { return errors.New("only the creator can delete this schedule") } // Delete schedule err = _i.chatScheduleRepository.DeleteChatSchedule(id) return } // GetUpcomingSchedules - Get upcoming schedules for a user func (_i *chatScheduleService) GetUpcomingSchedules(authToken string, limit int) (schedules []*mapper.ChatScheduleResponse, err error) { userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if userInfo == nil { return nil, errors.New("user not found") } userID := userInfo.ID // Get upcoming schedules from repository scheduleEntities, err := _i.chatScheduleRepository.GetUpcomingSchedules(userID, limit) if err != nil { return nil, err } // Convert to response schedules = _i.chatScheduleMapper.ToResponseList(scheduleEntities) return } // GetSchedulesByStatus - Get schedules by status func (_i *chatScheduleService) GetSchedulesByStatus(authToken string, status string) (schedules []*mapper.ChatScheduleResponse, err error) { userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if userInfo == nil { return nil, errors.New("user not found") } userID := userInfo.ID // Validate status validStatuses := []string{"scheduled", "ongoing", "completed", "cancelled"} isValidStatus := false for _, validStatus := range validStatuses { if status == validStatus { isValidStatus = true break } } if !isValidStatus { return nil, errors.New("invalid status") } // Get schedules by status from repository scheduleEntities, err := _i.chatScheduleRepository.GetSchedulesByStatus(status) if err != nil { return nil, err } // Filter by user participation var userSchedules []*entity.ChatSchedules for _, schedule := range scheduleEntities { isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, schedule.ID) if err != nil { continue } if isParticipant { userSchedules = append(userSchedules, schedule) } } // Convert to response schedules = _i.chatScheduleMapper.ToResponseList(userSchedules) return } // SendScheduleReminder - Send reminder for a schedule func (_i *chatScheduleService) SendScheduleReminder(authToken string, scheduleID uint) (err error) { userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if userInfo == nil { return errors.New("user not found") } userID := userInfo.ID // Get schedule schedule, err := _i.chatScheduleRepository.GetChatScheduleByID(scheduleID) if err != nil { return err } // Only creator can send reminders if schedule.CreatedBy != userID { return errors.New("only the creator can send reminders") } // Check if reminder was already sent if schedule.IsReminderSent { return errors.New("reminder has already been sent") } // TODO: Implement actual reminder sending logic (email, push notification, etc.) // For now, just update the reminder status now := time.Now() schedule.IsReminderSent = true schedule.ReminderSentAt = &now err = _i.chatScheduleRepository.UpdateChatSchedule(scheduleID, schedule) return }