package repository import ( "bugulma/backend/internal/domain" "context" "encoding/json" "time" "github.com/google/uuid" "gorm.io/datatypes" "gorm.io/gorm" ) // MatchRepository implements domain.MatchRepository with GORM type MatchRepository struct { *BaseRepository[domain.Match] } // NewMatchRepository creates a new GORM-based match repository func NewMatchRepository(db *gorm.DB) domain.MatchRepository { return &MatchRepository{ BaseRepository: NewBaseRepository[domain.Match](db), } } // GetByResourceID retrieves matches involving a specific resource flow func (r *MatchRepository) GetByResourceID(ctx context.Context, resourceID string) ([]*domain.Match, error) { return r.FindWhereWithContext(ctx, "source_resource_id = ? OR target_resource_id = ?", resourceID, resourceID) } // GetByOrganizationID retrieves matches involving a specific organization func (r *MatchRepository) GetByOrganizationID(ctx context.Context, organizationID string) ([]*domain.Match, error) { var matches []*domain.Match query := ` SELECT m.* FROM matches m JOIN resource_flows rf1 ON m.source_resource_id = rf1.id JOIN resource_flows rf2 ON m.target_resource_id = rf2.id WHERE rf1.organization_id = ? OR rf2.organization_id = ? ` result := r.DB().WithContext(ctx).Raw(query, organizationID, organizationID).Scan(&matches) if result.Error != nil { return nil, result.Error } return matches, nil } // GetByStatus retrieves matches by status func (r *MatchRepository) GetByStatus(ctx context.Context, status domain.MatchStatus) ([]*domain.Match, error) { return r.FindWhereWithContext(ctx, "status = ?", status) } // GetWithNegotiationHistory retrieves a match with its complete negotiation history func (r *MatchRepository) GetWithNegotiationHistory(matchID string) (*domain.Match, error) { var match domain.Match err := r.DB().Preload("NegotiationHistory").Where("id = ?", matchID).First(&match).Error if err != nil { return nil, err } return &match, nil } // GetTopMatches retrieves the top matches ordered by compatibility score func (r *MatchRepository) GetTopMatches(ctx context.Context, limit int) ([]*domain.Match, error) { var matches []*domain.Match result := r.DB().WithContext(ctx). Where("status = ?", domain.MatchStatusSuggested). Order("compatibility_score DESC, economic_value DESC"). Limit(limit). Find(&matches) if result.Error != nil { return nil, result.Error } return matches, nil } // CheckReservationConflicts checks if a resource has conflicting reservations func (r *MatchRepository) CheckReservationConflicts(ctx context.Context, resourceID string) ([]*domain.Match, error) { now := time.Now() var matches []*domain.Match result := r.DB().WithContext(ctx).Where( "(source_resource_id = ? OR target_resource_id = ?) AND status IN (?, ?) AND (reserved_until IS NULL OR reserved_until > ?)", resourceID, resourceID, domain.MatchStatusReserved, domain.MatchStatusContracted, now, ).Find(&matches) if result.Error != nil { return nil, result.Error } return matches, nil } // UpdateStatus updates the match status and adds a history entry func (r *MatchRepository) UpdateStatus(ctx context.Context, matchID string, newStatus domain.MatchStatus, actor string, notes string) error { return r.Transaction(func(tx *gorm.DB) error { // Get the current match var match domain.Match if err := tx.WithContext(ctx).First(&match, "id = ?", matchID).Error; err != nil { return err } oldStatus := match.Status match.Status = newStatus // Add negotiation history entry negotiationEntry := &domain.NegotiationHistoryEntry{ ID: uuid.New().String(), MatchID: matchID, Timestamp: time.Now(), ActorID: actor, Action: "status_change", Notes: notes, } // Set old/new values oldValue, _ := json.Marshal(map[string]interface{}{"status": oldStatus}) newValue, _ := json.Marshal(map[string]interface{}{"status": newStatus}) negotiationEntry.OldValue = datatypes.JSON(oldValue) negotiationEntry.NewValue = datatypes.JSON(newValue) // Save negotiation history entry if err := tx.WithContext(ctx).Create(negotiationEntry).Error; err != nil { return err } // Save the match return tx.WithContext(ctx).Save(&match).Error }) } // GetByOrganizationIDAndStatus retrieves matches for an organization with a specific status func (r *MatchRepository) GetByOrganizationIDAndStatus(ctx context.Context, orgID string, status domain.MatchStatus) ([]*domain.Match, error) { var matches []*domain.Match err := r.DB().WithContext(ctx).Where("organization_id = ? AND status = ?", orgID, status).Find(&matches).Error return matches, err } // GetExpiredReservations finds matches with expired reservations func (r *MatchRepository) GetExpiredReservations() ([]*domain.Match, error) { var matches []*domain.Match now := time.Now() err := r.DB().Where("status = ? AND reserved_until < ?", domain.MatchStatusReserved, now).Find(&matches).Error return matches, err } // GetPendingNegotiations finds matches in negotiation status func (r *MatchRepository) GetPendingNegotiations() ([]*domain.Match, error) { var matches []*domain.Match err := r.DB().Where("status = ?", domain.MatchStatusNegotiating).Find(&matches).Error return matches, err }