package feedback import ( "context" "tm/internal/tender" "tm/pkg/logger" "tm/pkg/response" ) // FeedbackService interface defines feedback business logic methods type FeedbackService interface { ToggleFeedback(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) GetFeedback(ctx context.Context, id string) (*FeedbackResponse, error) ListFeedback(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) (*FeedbackListWithTenderResponse, error) DeleteFeedback(ctx context.Context, id string) error GetFeedbackByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error) CompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error) } // feedbackService implements FeedbackService interface type feedbackService struct { feedbackRepo Repository tenderService tender.Service logger logger.Logger } // NewFeedbackService creates a new feedback service func NewFeedbackService(feedbackRepo Repository, tenderService tender.Service, logger logger.Logger) FeedbackService { return &feedbackService{ feedbackRepo: feedbackRepo, tenderService: tenderService, logger: logger, } } func (s *feedbackService) ToggleFeedback(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) { existing, err := s.feedbackRepo.GetByCompanyID(ctx, *companyID, req.TenderID) if err != nil { feedback := &Feedback{ TenderID: req.TenderID, FeedbackType: req.FeedbackType, CompanyID: companyID, CustomerID: customerID, } err = s.feedbackRepo.Create(ctx, feedback) if err != nil { s.logger.Error("Failed to create new feedback", map[string]interface{}{ "tender_id": req.TenderID, "company_id": *companyID, "error": err.Error(), }) return nil, err } return feedback.ToResponse(), nil } if existing.FeedbackType == req.FeedbackType { err = s.feedbackRepo.Delete(ctx, existing.GetID()) if err != nil { s.logger.Error("Failed to update existing feedback", map[string]interface{}{ "feedback_id": existing.GetID(), "tender_id": req.TenderID, "company_id": *companyID, "error": err.Error(), }) return nil, err } if req.FeedbackType == FeedbackTypeLike { existing.FeedbackType = "unlike" } else { existing.FeedbackType = "undislike" } return existing.ToResponse(), nil } if existing.FeedbackType == FeedbackTypeLike { existing.FeedbackType = FeedbackTypeDislike } else { existing.FeedbackType = FeedbackTypeLike } err = s.feedbackRepo.Update(ctx, existing) if err != nil { s.logger.Error("Failed to update existing feedback", map[string]interface{}{ "feedback_id": existing.GetID(), "tender_id": req.TenderID, "company_id": *companyID, "error": err.Error(), }) return nil, err } return existing.ToResponse(), nil } func (s *feedbackService) GetFeedback(ctx context.Context, id string) (*FeedbackResponse, error) { feedback, err := s.feedbackRepo.GetByID(ctx, id) if err != nil { s.logger.Error("Failed to get feedback", map[string]interface{}{ "id": id, "error": err.Error(), }) return nil, err } return feedback.ToResponse(), nil } func (s *feedbackService) ListFeedback(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) (*FeedbackListWithTenderResponse, error) { feedbacks, total, err := s.feedbackRepo.List(ctx, criteria, limit, offset) if err != nil { s.logger.Error("Failed to list feedback", map[string]interface{}{ "criteria": criteria, "limit": limit, "offset": offset, "error": err.Error(), }) return nil, err } meta := &response.Meta{ Total: int(total), Limit: limit, Offset: offset, Page: (offset / limit) + 1, Pages: int((total + int64(limit) - 1) / int64(limit)), } result := make([]*FeedbackWithTenderResponse, len(feedbacks)) for i, feedback := range feedbacks { tender, err := s.tenderService.GetByID(ctx, feedback.TenderID) if err != nil { s.logger.Error("Failed to get tender", map[string]interface{}{ "tender_id": feedback.TenderID, "error": err.Error(), }) return nil, err } resp := feedback.ToResponseWithTender(&tenderDetails{ ID: tender.ID, NoticePublicationID: tender.NoticePublicationID, PublicationDate: tender.PublicationDate, Title: tender.Title, Description: tender.Description, ProcurementTypeCode: tender.ProcurementTypeCode, ProcedureCode: tender.ProcedureCode, EstimatedValue: tender.EstimatedValue, Currency: tender.Currency, Duration: tender.Duration, DurationUnit: tender.DurationUnit, TenderDeadline: tender.TenderDeadline, SubmissionDeadline: tender.SubmissionDeadline, CountryCode: tender.CountryCode, BuyerOrganization: &organizationResponse{Name: tender.BuyerOrganization.Name}, Status: string(tender.Status), }) result[i] = resp } return &FeedbackListWithTenderResponse{ Feedback: result, Meta: meta, }, nil } func (s *feedbackService) DeleteFeedback(ctx context.Context, id string) error { return s.feedbackRepo.Delete(ctx, id) } func (s *feedbackService) GetFeedbackByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error) { feedback, err := s.feedbackRepo.GetByTenderID(ctx, tenderID, companyID) if err != nil { s.logger.Error("Failed to get feedback by tender ID", map[string]interface{}{ "tender_id": tenderID, "error": err.Error(), }) return nil, err } return feedback.ToResponse(), nil } func (s *feedbackService) CompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error) { s.logger.Info("Getting company feedback statistics", map[string]interface{}{ "company_id": companyID, }) stats, err := s.feedbackRepo.GetCompanyFeedbackStats(ctx, companyID) if err != nil { s.logger.Error("Failed to get company feedback statistics", map[string]interface{}{ "company_id": companyID, "error": err.Error(), }) return nil, err } s.logger.Info("Company feedback statistics retrieved successfully", map[string]interface{}{ "company_id": companyID, "total_feedback": stats.TotalFeedback, "total_likes": stats.TotalLikes, "total_dislikes": stats.TotalDislikes, }) return stats, nil }