package admin import ( "context" "encoding/json" "errors" "regexp" "strings" "unicode/utf8" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/yuxingu/digital-psychology/apps/api/internal/repository" ) var ( ErrInvalidImageCardDeck = errors.New("invalid image card deck") ErrImageCardDeckConflict = errors.New("image card deck code conflict") imageCardDeckCodeRe = regexp.MustCompile(`^[a-z][a-z0-9_]{1,62}$`) ) // ImageCardDeckWriteBody is JSON for create/update. type ImageCardDeckWriteBody struct { Code string `json:"code"` Title string `json:"title"` Active bool `json:"active"` } // CreateImageCardDeck validates, inserts, audits. func (s *Service) CreateImageCardDeck(ctx context.Context, adminID uuid.UUID, body ImageCardDeckWriteBody) (*repository.ImageCardDeckRow, error) { in, err := normalizeImageCardDeckWrite(body) if err != nil { return nil, err } meta, _ := json.Marshal(map[string]any{"code": in.Code, "active": in.Active}) row, err := s.Repo.CreateImageCardDeckWithAudit(ctx, adminID, in, meta) if repository.ImageCardDeckCodeConflict(err) { return nil, ErrImageCardDeckConflict } return row, err } // UpdateImageCardDeck validates, updates, audits. func (s *Service) UpdateImageCardDeck(ctx context.Context, adminID, id uuid.UUID, body ImageCardDeckWriteBody) (*repository.ImageCardDeckRow, error) { in, err := normalizeImageCardDeckWrite(body) if err != nil { return nil, err } meta, _ := json.Marshal(map[string]any{"code": in.Code, "active": in.Active}) row, err := s.Repo.UpdateImageCardDeckWithAudit(ctx, adminID, id, in, meta) if errors.Is(err, pgx.ErrNoRows) { return nil, ErrImageCardDeckNotFound } if repository.ImageCardDeckCodeConflict(err) { return nil, ErrImageCardDeckConflict } return row, err } func normalizeImageCardDeckWrite(body ImageCardDeckWriteBody) (repository.ImageCardDeckWriteInput, error) { code := strings.TrimSpace(body.Code) title := strings.TrimSpace(body.Title) if !imageCardDeckCodeRe.MatchString(code) { return repository.ImageCardDeckWriteInput{}, ErrInvalidImageCardDeck } if title == "" || utf8.RuneCountInString(title) > 128 { return repository.ImageCardDeckWriteInput{}, ErrInvalidImageCardDeck } return repository.ImageCardDeckWriteInput{Code: code, Title: title, Active: body.Active}, nil }