package com.faforever.api.data.domain;

import com.faforever.api.data.checks.Prefab;
import com.faforever.api.data.converter.VictoryConditionConverter;
import com.faforever.api.data.listeners.GameEnricher;
import com.yahoo.elide.annotation.ComputedAttribute;
import com.yahoo.elide.annotation.Include;
import com.yahoo.elide.annotation.UpdatePermission;
import lombok.Setter;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Immutable;
import org.jetbrains.annotations.Nullable;

import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.time.OffsetDateTime;
import java.util.List;

@Entity
@Table(name = "game_stats")
@Include(rootLevel = true, type = "game")
@Immutable
@Setter
@EntityListeners(GameEnricher.class)
public class Game {

  private int id;
  private OffsetDateTime startTime;
  private OffsetDateTime endTime;
  private Integer replayTicks;
  private VictoryCondition victoryCondition;
  private FeaturedMod featuredMod;
  private Player host;
  private MapVersion mapVersion;
  private String name;
  private Validity validity;
  private List<GamePlayerStats> playerStats;
  private String replayUrl;
  private List<GameReview> reviews;
  private GameReviewsSummary reviewsSummary;
  private MatchmakerQueue matchmakerQueue;

  @Id
  @Column(name = "id")
  public int getId() {
    return id;
  }

  @Column(name = "startTime")
  public OffsetDateTime getStartTime() {
    return startTime;
  }

  @Column(name = "replay_ticks")
  public Integer getReplayTicks() {
    return replayTicks;
  }

  @Column(name = "gameType")
  @Convert(converter = VictoryConditionConverter.class)
  public VictoryCondition getVictoryCondition() {
    return victoryCondition;
  }

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "gameMod")
  public FeaturedMod getFeaturedMod() {
    return featuredMod;
  }

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "host")
  public Player getHost() {
    return host;
  }

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "mapId")
  public MapVersion getMapVersion() {
    return mapVersion;
  }

  @Column(name = "gameName")
  public String getName() {
    return name;
  }

  @Column(name = "validity")
  @Enumerated(EnumType.ORDINAL)
  public Validity getValidity() {
    return validity;
  }

  @OneToMany(mappedBy = "game")
  @BatchSize(size = 1000)
  public List<GamePlayerStats> getPlayerStats() {
    return playerStats;
  }

  @Column(name = "endTime")
  @Nullable
  public OffsetDateTime getEndTime() {
    return endTime;
  }

  @Transient
  @ComputedAttribute
  public String getReplayUrl() {
    return replayUrl;
  }

  @OneToMany(mappedBy = "game")
  @UpdatePermission(expression = Prefab.ALL)
  @BatchSize(size = 1000)
  public List<GameReview> getReviews() {
    return reviews;
  }

  @OneToOne(fetch = FetchType.LAZY)
  @PrimaryKeyJoinColumn
  @UpdatePermission(expression = Prefab.ALL)
  @BatchSize(size = 1000)
  public GameReviewsSummary getReviewsSummary() {
    return reviewsSummary;
  }

  @JoinTable(name = "matchmaker_queue_game",
    joinColumns = @JoinColumn(name = "game_stats_id"),
    inverseJoinColumns = @JoinColumn(name = "matchmaker_queue_id")
  )
  @ManyToOne
  @Nullable
  public MatchmakerQueue getMatchmakerQueue() {
    return matchmakerQueue;
  }
}

@@ -821,44 +821,8 @@
 mn;%0A
-import javax.persistence.JoinTable;%0A
 impo
@@ -1089,19 +1089,18 @@
 va.util.
-Lis
+Se
 t;%0A%0A@Ent
@@ -1563,27 +1563,26 @@
 ;%0A  private 
-Lis
+Se
 t%3CGamePlayer
@@ -1639,19 +1639,18 @@
 private 
-Lis
+Se
 t%3CGameRe
@@ -1712,51 +1712,8 @@
 ary;
-%0A  private MatchmakerQueue matchmakerQueue;
 %0A%0A  
@@ -2797,27 +2797,26 @@
 0)%0A  public 
-Lis
+Se
 t%3CGamePlayer
@@ -3180,19 +3180,18 @@
  public 
-Lis
+Se
 t%3CGameRe
@@ -3449,16 +3449,424 @@
 y;%0A  %7D%0A%0A
+  /**%0A   * This ManyToOne relationship leads to a double left outer join through Elide causing an additional full table%0A   * scan on the matchmaker_queue table. Even though it has only 3 records, it causes MySql 5.7 and MySQL to run%0A   * a list of all games %3E 1 min on prod where it was ~1 second before.%0A   *%0A   * This can be fixed by migrating to MariaDB.%0A   */%0A//  private Integer matchmakerQueueId;%0A//%0A//
   @JoinT
@@ -3902,16 +3902,18 @@
 _game%22,%0A
+//
     join
@@ -3959,16 +3959,18 @@
 s_id%22),%0A
+//
     inve
@@ -4032,12 +4032,16 @@
 d%22)%0A
+//
   )%0A
+//
   @M
@@ -4048,17 +4048,43 @@
 anyToOne
-%0A
+(fetch = FetchType.LAZY)%0A//
   @Nulla
@@ -4079,32 +4079,34 @@
 )%0A//  @Nullable%0A
+//
   public Matchma
@@ -4098,31 +4098,23 @@
  public 
-MatchmakerQueue
+Integer
  getMatc
@@ -4124,21 +4124,25 @@
 kerQueue
+Id
 () %7B%0A
+//
     retu
@@ -4159,16 +4159,20 @@
 kerQueue
-;%0A
+Id;%0A//
   %7D%0A%7D%0A

Remove matchmakerQueueId from Game