Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 62 additions & 4 deletions internal/domain/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (
type SessionStatus string

const (
StatusActive SessionStatus = "ACTIVE"
StatusMatched SessionStatus = "MATCHED"
StatusFinished SessionStatus = "FINISHED"
StatusActive SessionStatus = "ACTIVE"
StatusMatched SessionStatus = "MATCHED"
StatusFinished SessionStatus = "FINISHED"
StatusHostTieBreaker SessionStatus = "HOST_TIE_BREAKER"
)

type VoteType string
Expand All @@ -35,6 +36,7 @@ type Session struct {
Pool []Restaurant `json:"pool"`
MatchedID string `json:"matched_id,omitempty"`
Votes map[string]map[string]VoteType `json:"votes"`
TiedIDs []string `json:"tied_ids,omitempty"`
CreatedAt time.Time `json:"created_at"`
}

Expand Down Expand Up @@ -70,7 +72,15 @@ func (s *Session) RecordVote(userID, restaurantID string, vote VoteType) (bool,

s.Votes[restaurantID][userID] = vote

return s.CheckConsensus(restaurantID), nil
if s.CheckConsensus(restaurantID) {
return true, nil
}

if s.CheckCompletionFallback() {
return true, nil
}

return false, nil
}

func (s *Session) CheckConsensus(restaurantID string) bool {
Expand All @@ -93,3 +103,51 @@ func (s *Session) CheckConsensus(restaurantID string) bool {

return false
}

func (s *Session) CheckCompletionFallback() bool {
totalExpectedVotes := len(s.Participants) * len(s.Pool)
totalCastVotes := 0

for _, userVotes := range s.Votes {
totalCastVotes += len(userVotes)
}

if totalCastVotes < totalExpectedVotes || totalExpectedVotes == 0 {
return false
}

bestScore := -9999
var tiedIDs []string

for restID, userVotes := range s.Votes {
score := 0
for _, vote := range userVotes {
if vote == VoteLike {
score += 1
} else if vote == VoteSuperLike {
score += 2
} else if vote == VoteDislike {
score -= 1
}
}

if score > bestScore {
bestScore = score
tiedIDs = []string{restID}
} else if score == bestScore {
tiedIDs = append(tiedIDs, restID)
}
}

if len(tiedIDs) == 1 {
s.Status = StatusMatched
s.MatchedID = tiedIDs[0]
return true
} else if len(tiedIDs) > 1 {
s.Status = StatusHostTieBreaker
s.TiedIDs = tiedIDs
return true
}

return false
}
63 changes: 63 additions & 0 deletions internal/interfaces/ws/ws_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,69 @@ func (h *WSHandler) HandleConnection(w http.ResponseWriter, r *http.Request) {
"session": updatedSession,
"is_match": isMatch,
})
} else if msg.Action == "RESOLVE_TIE" {
session, err := h.sessionRepo.GetByID(context.Background(), sessionID)
if err != nil {
continue
}

if session.HostID != msg.UserID {
log.Println("Unauthorized tie resolution attempt")
continue
}

session.Status = domain.StatusMatched
session.MatchedID = msg.RestaurantID

err = h.sessionRepo.Save(context.Background(), session)
if err != nil {
log.Printf("Failed to save resolved tie: %v", err)
continue
}

h.hub.Broadcast(sessionID, map[string]interface{}{
"event": "SESSION_UPDATED",
"session": session,
"is_match": true,
})
} else if msg.Action == "START_SECOND_ROUND" {
session, err := h.sessionRepo.GetByID(context.Background(), sessionID)
if err != nil {
continue
}

if session.HostID != msg.UserID {
log.Println("Unauthorized second round attempt")
continue
}

var newPool []domain.Restaurant
for _, rest := range session.Pool {
for _, tiedID := range session.TiedIDs {
if rest.ID == tiedID {
newPool = append(newPool, rest)
break
}
}
}

session.Pool = newPool
session.Votes = make(map[string]map[string]domain.VoteType)
session.Status = domain.StatusActive
session.TiedIDs = nil
session.MatchedID = ""

err = h.sessionRepo.Save(context.Background(), session)
if err != nil {
log.Printf("Failed to start second round: %v", err)
continue
}

h.hub.Broadcast(sessionID, map[string]interface{}{
"event": "SESSION_UPDATED",
"session": session,
"is_match": false,
})
}
}
}
Loading