package inquiry import ( "context" "errors" "time" "tm/pkg/logger" orm "tm/pkg/mongo" "tm/pkg/response" "go.mongodb.org/mongo-driver/v2/bson" ) // Repository defines methods for inquiry data access type Repository interface { Create(ctx context.Context, inquiry *Inquiry) error GetByID(ctx context.Context, id string) (*Inquiry, error) UpdateStatus(ctx context.Context, id string, status, reason, changedBy, description string) error Search(ctx context.Context, form *SearchInquiriesForm, pagination *response.Pagination) ([]Inquiry, int64, error) Delete(ctx context.Context, id string) error CheckPendingInquiry(ctx context.Context, email string) (*Inquiry, error) CheckPendingInquiryByPhone(ctx context.Context, phone string) (*Inquiry, error) } // inquiryRepository implements the Repository interface using the MongoDB ORM type inquiryRepository struct { ormRepo orm.Repository[Inquiry] logger logger.Logger } // NewInquiryRepository creates a new inquiry repository func NewInquiryRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { // Create indexes using the ORM's index management indexes := []orm.Index{ *orm.CreateTextIndex("search_idx", "full_name", "company_name", "work_email"), *orm.NewIndex("company_name_idx", bson.D{bson.E{Key: "company_name", Value: 1}}), *orm.NewIndex("work_email_idx", bson.D{bson.E{Key: "work_email", Value: 1}}), *orm.NewIndex("phone_number_idx", bson.D{bson.E{Key: "phone_number", Value: 1}}), *orm.NewIndex("status_idx", bson.D{bson.E{Key: "status", Value: 1}}), *orm.NewIndex("created_at_idx", bson.D{bson.E{Key: "created_at", Value: -1}}), // Compound index for duplicate prevention *orm.NewIndex("email_status_idx", bson.D{ bson.E{Key: "work_email", Value: 1}, bson.E{Key: "status", Value: 1}, }), *orm.NewIndex("phone_status_idx", bson.D{ bson.E{Key: "phone_number", Value: 1}, bson.E{Key: "status", Value: 1}, }), } // Create indexes err := mongoManager.CreateIndexes("inquiries", indexes) if err != nil { logger.Warn("Failed to create inquiry indexes", map[string]interface{}{ "error": err.Error(), }) } // Create ORM repository ormRepo := orm.NewRepository[Inquiry](mongoManager.GetCollection("inquiries"), logger) return &inquiryRepository{ ormRepo: ormRepo, logger: logger, } } // Create creates a new inquiry func (r *inquiryRepository) Create(ctx context.Context, inquiry *Inquiry) error { // Set created timestamp using Unix timestamp now := time.Now().Unix() inquiry.SetCreatedAt(now) inquiry.SetUpdatedAt(now) // Set initial status to pending inquiry.Status = StatusPending // Add initial status change to history inquiry.AddStatusChange(StatusPending, "Inquiry submitted", "system", "Initial inquiry submission") // Use ORM to create inquiry err := r.ormRepo.Create(ctx, inquiry) if err != nil { r.logger.Error("Failed to create inquiry", map[string]interface{}{ "error": err.Error(), "work_email": inquiry.WorkEmail, "company_name": inquiry.CompanyName, }) return err } r.logger.Info("Inquiry created successfully", map[string]interface{}{ "inquiry_id": inquiry.ID, "work_email": inquiry.WorkEmail, "company_name": inquiry.CompanyName, "status": inquiry.Status, }) return nil } // GetByID retrieves an inquiry by ID func (r *inquiryRepository) GetByID(ctx context.Context, id string) (*Inquiry, error) { inquiry, err := r.ormRepo.FindByID(ctx, id) if err != nil { if errors.Is(err, orm.ErrDocumentNotFound) { return nil, errors.New("inquiry not found") } r.logger.Error("Failed to get inquiry by ID", map[string]interface{}{ "error": err.Error(), "inquiry_id": id, }) return nil, err } return inquiry, nil } // UpdateStatus updates the status of an inquiry func (r *inquiryRepository) UpdateStatus(ctx context.Context, id string, status, reason, changedBy, description string) error { // Get inquiry first inquiry, err := r.GetByID(ctx, id) if err != nil { return err } // Add status change to history inquiry.AddStatusChange(status, reason, changedBy, description) // Update in database - use the correct Update method signature err = r.ormRepo.Update(ctx, inquiry) if err != nil { r.logger.Error("Failed to update inquiry status", map[string]interface{}{ "error": err.Error(), "inquiry_id": id, "new_status": status, }) return err } r.logger.Info("Inquiry status updated successfully", map[string]interface{}{ "inquiry_id": id, "old_status": inquiry.StatusHistory[len(inquiry.StatusHistory)-2].Status, // Previous status "new_status": status, "changed_by": changedBy, }) return nil } // CheckPendingInquiry checks if there's a pending inquiry for the given email func (r *inquiryRepository) CheckPendingInquiry(ctx context.Context, email string) (*Inquiry, error) { filter := bson.M{ "work_email": email, "status": bson.M{"$in": []string{StatusPending, StatusReviewed}}, } pagination := orm.NewPaginationBuilder(). Limit(1). SortBy("created_at", "desc"). Build() result, err := r.ormRepo.FindAll(ctx, filter, pagination) if err != nil { r.logger.Error("Failed to check pending inquiry by email", map[string]interface{}{ "error": err.Error(), "work_email": email, }) return nil, err } if len(result.Items) > 0 { return &result.Items[0], nil } return nil, nil } // CheckPendingInquiryByPhone checks if there's a pending inquiry for the given phone number func (r *inquiryRepository) CheckPendingInquiryByPhone(ctx context.Context, phone string) (*Inquiry, error) { filter := bson.M{ "phone_number": phone, "status": bson.M{"$in": []string{StatusPending, StatusReviewed}}, } pagination := orm.NewPaginationBuilder(). Limit(1). SortBy("created_at", "desc"). Build() result, err := r.ormRepo.FindAll(ctx, filter, pagination) if err != nil { r.logger.Error("Failed to check pending inquiry by phone", map[string]interface{}{ "error": err.Error(), "phone_number": phone, }) return nil, err } if len(result.Items) > 0 { return &result.Items[0], nil } return nil, nil } // Search retrieves inquiries with pagination and filters func (r *inquiryRepository) Search(ctx context.Context, form *SearchInquiriesForm, pagination *response.Pagination) ([]Inquiry, int64, error) { filter := bson.M{} // Add search filter if form.Search != nil { filter["$or"] = bson.A{ bson.M{"full_name": bson.M{"$regex": form.Search, "$options": "i"}}, bson.M{"company_name": bson.M{"$regex": form.Search, "$options": "i"}}, bson.M{"work_email": bson.M{"$regex": form.Search, "$options": "i"}}, } } // Add status filter if form.Status != nil { filter["status"] = *form.Status } // Build sort sort := "created_at" if pagination.SortBy != "" { sort = pagination.SortBy } sortOrder := "desc" if pagination.SortOrder != "" { sortOrder = pagination.SortOrder } // Build pagination paginationBuilder := orm.NewPaginationBuilder(). Limit(pagination.Limit). Skip(pagination.Offset). SortBy(sort, sortOrder). Build() // Use ORM to find inquiries result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder) if err != nil { r.logger.Error("Failed to search inquiries", map[string]interface{}{ "error": err.Error(), }) return nil, 0, err } return result.Items, result.TotalCount, nil } // Delete deletes an inquiry func (r *inquiryRepository) Delete(ctx context.Context, id string) error { // Get inquiry first to ensure it exists _, err := r.GetByID(ctx, id) if err != nil { r.logger.Error("Failed to delete inquiry", map[string]interface{}{ "error": err.Error(), "inquiry_id": id, }) if errors.Is(err, orm.ErrDocumentNotFound) { return errors.New("inquiry not found") } return err } // Delete in database err = r.ormRepo.Delete(ctx, id) if err != nil { r.logger.Error("Failed to delete inquiry", map[string]interface{}{ "error": err.Error(), "inquiry_id": id, }) return err } r.logger.Info("Inquiry deleted successfully", map[string]interface{}{ "inquiry_id": id, }) return nil }