package recommendations

import (
	"mal/internal/db"
	"sort"
	"sync"
)

type candidateStore struct {
	watchlistAnimeIDs map[int]struct{}
	byID              map[int]rankedCandidate
	mu                sync.Mutex
}

func newCandidateStore(watchlist []db.GetUserWatchListRow) *candidateStore {
	watchlistAnimeIDs := make(map[int]struct{}, len(watchlist))
	for _, entry := range watchlist {
		if entry.AnimeID <= 0 {
			continue
		}
		watchlistAnimeIDs[int(entry.AnimeID)] = struct{}{}
	}

	return &candidateStore{
		watchlistAnimeIDs: watchlistAnimeIDs,
		byID:              map[int]rankedCandidate{},
	}
}

func (s *candidateStore) upsert(candidate rankedCandidate) {
	if candidate.id <= 0 {
		return
	}
	if _, exists := s.watchlistAnimeIDs[candidate.id]; exists {
		return
	}

	s.mu.Lock()
	defer s.mu.Unlock()

	current, ok := s.byID[candidate.id]
	if !ok {
		s.byID[candidate.id] = candidate
		return
	}

	current.collaborativeScore += candidate.collaborativeScore
	current.profileSearchScore += candidate.profileSearchScore
	if candidate.hasAnime {
		current.anime = candidate.anime
		current.hasAnime = true
	}
	s.byID[candidate.id] = current
}

func (s *candidateStore) ranked() []rankedCandidate {
	ranked := make([]rankedCandidate, 0, len(s.byID))
	for _, item := range s.byID {
		ranked = append(ranked, item)
	}

	sort.Slice(ranked, func(i, j int) bool {
		left := rankedCandidateRetrievalScore(ranked[i].collaborativeScore, ranked[i].profileSearchScore)
		right := rankedCandidateRetrievalScore(ranked[j].collaborativeScore, ranked[j].profileSearchScore)
		if left == right {
			return ranked[i].id < ranked[j].id
		}
		return left > right
	})

	return ranked
}
