class IgdbMatchSuggestion < ApplicationRecord # Associations belongs_to :game belongs_to :igdb_game, optional: true # Enums enum :status, { pending: "pending", approved: "approved", rejected: "rejected" }, prefix: true # Validations validates :igdb_id, presence: true validates :igdb_name, presence: true validates :game_id, uniqueness: { scope: :igdb_id } # Scopes scope :pending_review, -> { status_pending.order(confidence_score: :desc, created_at: :asc) } scope :for_user, ->(user) { joins(:game).where(games: { user_id: user.id }) } scope :high_confidence, -> { where("confidence_score >= ?", 80.0) } # Instance methods def approve! transaction do update!(status: "approved", reviewed_at: Time.current) # Update the game with the matched IGDB ID game.update!( igdb_id: igdb_id, igdb_matched_at: Time.current, igdb_match_status: "matched", igdb_match_confidence: confidence_score ) # Find or create the IgdbGame record with full data igdb_game_record = IgdbGame.find_or_create_by!(igdb_id: igdb_id) do |ig| ig.name = igdb_name ig.slug = igdb_slug ig.cover_url = igdb_cover_url ig.summary = igdb_summary ig.first_release_date = igdb_release_date ig.last_synced_at = Time.current end # Update summary if it wasn't already set if igdb_game_record.summary.blank? && igdb_summary.present? igdb_game_record.update(summary: igdb_summary) end igdb_game_record.increment_match_count! # Map and assign IGDB genres to the game sync_genres_to_game if igdb_genres.present? # Reject all other pending suggestions for this game game.igdb_match_suggestions .where.not(id: id) .status_pending .update_all( status: "rejected", reviewed_at: Time.current ) end end def reject! transaction do update!( status: "rejected", reviewed_at: Time.current ) # Reject all other pending suggestions for this game game.igdb_match_suggestions .where.not(id: id) .status_pending .update_all( status: "rejected", reviewed_at: Time.current ) # Mark game as manually reviewed with no match (only if all suggestions rejected) if game.igdb_match_suggestions.status_pending.none? game.update!( igdb_match_status: "no_match", igdb_matched_at: Time.current ) end end end def cover_image_url(size = "cover_big") return nil unless igdb_cover_url.present? "https://images.igdb.com/igdb/image/upload/t_#{size}/#{igdb_cover_url}.jpg" end private def sync_genres_to_game return unless igdb_genres.is_a?(Array) && igdb_genres.any? # Genre mapping: IGDB genre names to our genre names genre_mappings = { "Role-playing (RPG)" => "RPG", "Fighting" => "Fighting", "Shooter" => "Shooter", "Music" => "Music", "Platform" => "Platformer", "Puzzle" => "Puzzle", "Racing" => "Racing", "Real Time Strategy (RTS)" => "Strategy", "Simulator" => "Simulation", "Sport" => "Sports", "Strategy" => "Strategy", "Turn-based strategy (TBS)" => "Strategy", "Tactical" => "Strategy", "Hack and slash/Beat 'em up" => "Action", "Quiz/Trivia" => "Puzzle", "Pinball" => "Arcade", "Adventure" => "Adventure", "Indie" => "Indie", "Arcade" => "Arcade", "Visual Novel" => "Adventure", "Card & Board Game" => "Puzzle", "MOBA" => "Strategy", "Point-and-click" => "Adventure" } # Find or create matching genres igdb_genres.each do |igdb_genre_name| # Try exact match first local_genre = Genre.find_by("LOWER(name) = ?", igdb_genre_name.downcase) # Try mapped name if local_genre.nil? && genre_mappings[igdb_genre_name] mapped_name = genre_mappings[igdb_genre_name] local_genre = Genre.find_by("LOWER(name) = ?", mapped_name.downcase) end # Add genre to game if found and not already assigned if local_genre && !game.genres.include?(local_genre) game.genres << local_genre Rails.logger.info("Added genre '#{local_genre.name}' to game #{game.id} from IGDB") end end end end