package cms import ( "context" "errors" "fmt" "tm/pkg/logger" "tm/pkg/response" ) // Service defines the interface for CMS business operations type Service interface { // Create creates a new CMS entry Create(ctx context.Context, form *CMSForm) (*CMSResponse, error) // GetByID retrieves a CMS entry by ID GetByID(ctx context.Context, id string) (*CMSResponse, error) // GetByKey retrieves a CMS entry by key GetByKey(ctx context.Context, key string) (*CMSResponse, error) // Update updates a CMS entry Update(ctx context.Context, id string, form *CMSForm) (*CMSResponse, error) // Delete deletes a CMS entry by ID Delete(ctx context.Context, id string) error // Search searches CMS entries with filters and pagination Search(ctx context.Context, form *SearchCMSForm, pagination *response.Pagination) (*CMSListResponse, error) } // cmsService implements Service interface type cmsService struct { repo Repository logger logger.Logger } // NewService creates a new CMS service func NewService(repo Repository, logger logger.Logger) Service { return &cmsService{ repo: repo, logger: logger, } } // Create creates a new CMS entry func (s *cmsService) Create(ctx context.Context, form *CMSForm) (*CMSResponse, error) { s.logger.Info("Creating new CMS entry", map[string]interface{}{ "key": form.Key, }) // Check if CMS with this key already exists existing, err := s.repo.GetByKey(ctx, form.Key) if err == nil && existing != nil { s.logger.Warn("CMS with this key already exists", map[string]interface{}{ "key": form.Key, }) return nil, ErrCMSKeyExists } // Create CMS entity cms := &CMS{ Key: form.Key, } // Copy form data to entity s.copyFormToEntity(form, cms) // Save to database if err := s.repo.Create(ctx, cms); err != nil { s.logger.Error("Failed to create CMS", map[string]interface{}{ "key": form.Key, "error": err.Error(), }) return nil, fmt.Errorf("failed to create CMS: %w", err) } s.logger.Info("CMS entry created successfully", map[string]interface{}{ "id": cms.GetID(), "key": form.Key, }) return cms.ToResponse(), nil } // GetByID retrieves a CMS entry by ID func (s *cmsService) GetByID(ctx context.Context, id string) (*CMSResponse, error) { s.logger.Debug("Retrieving CMS by ID", map[string]interface{}{ "id": id, }) cms, err := s.repo.GetByID(ctx, id) if err != nil { if errors.Is(err, ErrCMSNotFound) { return nil, ErrCMSNotFound } s.logger.Error("Failed to get CMS by ID", map[string]interface{}{ "id": id, "error": err.Error(), }) return nil, fmt.Errorf("failed to get CMS: %w", err) } return cms.ToResponse(), nil } // GetByKey retrieves a CMS entry by key func (s *cmsService) GetByKey(ctx context.Context, key string) (*CMSResponse, error) { s.logger.Debug("Retrieving CMS by key", map[string]interface{}{ "key": key, }) cms, err := s.repo.GetByKey(ctx, key) if err != nil { if errors.Is(err, ErrCMSNotFound) { return nil, ErrCMSNotFound } s.logger.Error("Failed to get CMS by key", map[string]interface{}{ "key": key, "error": err.Error(), }) return nil, fmt.Errorf("failed to get CMS: %w", err) } return cms.ToResponse(), nil } // Update updates a CMS entry func (s *cmsService) Update(ctx context.Context, id string, form *CMSForm) (*CMSResponse, error) { s.logger.Info("Updating CMS entry", map[string]interface{}{ "id": id, "key": form.Key, }) // Get existing CMS cms, err := s.repo.GetByID(ctx, id) if err != nil { if errors.Is(err, ErrCMSNotFound) { return nil, ErrCMSNotFound } s.logger.Error("Failed to get existing CMS for update", map[string]interface{}{ "id": id, "error": err.Error(), }) return nil, fmt.Errorf("failed to get CMS for update: %w", err) } // Check if key is being changed and if new key already exists if cms.Key != form.Key { existing, err := s.repo.GetByKey(ctx, form.Key) if err == nil && existing != nil && existing.GetID() != id { s.logger.Warn("CMS with this key already exists", map[string]interface{}{ "key": form.Key, }) return nil, ErrCMSKeyExists } cms.Key = form.Key } // Copy form data to entity s.copyFormToEntity(form, cms) // Update in database if err := s.repo.Update(ctx, cms); err != nil { s.logger.Error("Failed to update CMS", map[string]interface{}{ "id": id, "key": form.Key, "error": err.Error(), }) return nil, fmt.Errorf("failed to update CMS: %w", err) } s.logger.Info("CMS entry updated successfully", map[string]interface{}{ "id": id, "key": form.Key, }) return cms.ToResponse(), nil } // Delete deletes a CMS entry by ID func (s *cmsService) Delete(ctx context.Context, id string) error { s.logger.Info("Deleting CMS entry", map[string]interface{}{ "id": id, }) if err := s.repo.Delete(ctx, id); err != nil { if errors.Is(err, ErrCMSNotFound) { return ErrCMSNotFound } s.logger.Error("Failed to delete CMS", map[string]interface{}{ "id": id, "error": err.Error(), }) return fmt.Errorf("failed to delete CMS: %w", err) } s.logger.Info("CMS entry deleted successfully", map[string]interface{}{ "id": id, }) return nil } // Search searches CMS entries with filters and pagination func (s *cmsService) Search(ctx context.Context, form *SearchCMSForm, pagination *response.Pagination) (*CMSListResponse, error) { s.logger.Debug("Searching CMS entries", map[string]interface{}{ "filters": form, "pagination": pagination, }) result, err := s.repo.Search(ctx, form, pagination) if err != nil { s.logger.Error("Failed to search CMS entries", map[string]interface{}{ "filters": form, "error": err.Error(), }) return nil, fmt.Errorf("failed to search CMS entries: %w", err) } // Convert to response format cmsResponses := make([]*CMSResponse, len(result.Items)) for i, cms := range result.Items { cmsResponses[i] = cms.ToResponse() } s.logger.Info("CMS search completed", map[string]interface{}{ "total_count": result.TotalCount, "items_count": len(result.Items), }) return &CMSListResponse{ CMS: cmsResponses, Meta: pagination.ListMeta(result.TotalCount, result.NextCursor, result.HasMore, result.PageOffset), }, nil } // copyFormToEntity copies data from form to entity func (s *cmsService) copyFormToEntity(form *CMSForm, cms *CMS) { if form.Type != "" { cms.Type = CMSType(form.Type) } switch cms.Type { case CMSTypeCompany: if form.Language != "" { cms.Language = form.Language } if form.CompanyName != "" { cms.CompanyName = form.CompanyName } if form.Country != "" { cms.Country = form.Country } case CMSTypeOrganization: cms.Language = "" cms.CompanyName = "" cms.Country = "" } // Hero section if form.Hero.Title != "" { cms.Hero.Title = form.Hero.Title } if form.Hero.Description != "" { cms.Hero.Description = form.Hero.Description } if form.Hero.ButtonText != "" { cms.Hero.ButtonText = form.Hero.ButtonText } if form.Hero.ButtonLink != "" { cms.Hero.ButtonLink = form.Hero.ButtonLink } if form.Hero.GifFile != "" { cms.Hero.GifFile = form.Hero.GifFile } // Chart section if form.Chart.Title != "" { cms.Chart.Title = form.Chart.Title } if form.Chart.MissedAmount != "" { cms.Chart.MissedAmount = form.Chart.MissedAmount } if len(form.Chart.Data) > 0 { cms.Chart.Data = make([]chartPoint, len(form.Chart.Data)) for i, point := range form.Chart.Data { cms.Chart.Data[i] = chartPoint(point) } } // Features section s.copyCardSectionForm(&form.Features, &cms.Features) // Challenges section s.copyCardSectionForm(&form.Challenges, &cms.Challenges) // Advantages section s.copyCardSectionForm(&form.Advantages, &cms.Advantages) // Contact section if form.Contact.Title != "" { cms.Contact.Title = form.Contact.Title } if form.Contact.Description != "" { cms.Contact.Description = form.Contact.Description } if form.Contact.SubmitButtonText != "" { cms.Contact.SubmitButtonText = form.Contact.SubmitButtonText } if len(form.Contact.Fields) > 0 { cms.Contact.Fields = make([]formField, len(form.Contact.Fields)) for i, field := range form.Contact.Fields { cms.Contact.Fields[i] = formField{ Name: field.Name, Label: field.Label, Placeholder: field.Placeholder, Required: field.Required, } } } // Footer section if form.Footer.Email != "" { cms.Footer.Email = form.Footer.Email } if form.Footer.Phone != "" { cms.Footer.Phone = form.Footer.Phone } if form.Footer.Location != "" { cms.Footer.Location = form.Footer.Location } if form.Footer.Tagline != "" { cms.Footer.Tagline = form.Footer.Tagline } if form.Footer.Copyright != "" { cms.Footer.Copyright = form.Footer.Copyright } } // copyCardSectionForm copies card section form data to entity func (s *cmsService) copyCardSectionForm(formSection *cardSectionForm, entitySection *cardSection) { if formSection.Title != "" { entitySection.Title = formSection.Title } if formSection.Description != "" { entitySection.Description = formSection.Description } if len(formSection.Cards) > 0 { entitySection.Cards = make([]card, len(formSection.Cards)) for i, cardForm := range formSection.Cards { entitySection.Cards[i] = card{ Icon: cardForm.Icon, Title: cardForm.Title, Description: cardForm.Description, } } } }