lib/islands/engine/game_server/reply_tuple.ex

defmodule Islands.Engine.GameServer.ReplyTuple do
  alias Islands.Engine.GameServer
  alias Islands.{Game, PlayerID, Request, Tally}

  @player_turns [:player1_turn, :player2_turn]
  @position_actions [:position_island, :position_all_islands]
  @timeout :timer.minutes(30)

  @typedoc "Action like :add_player or reason like :duplicate_player_name"
  @type action_or_reason :: atom
  @type t :: {:reply, Tally.t(), Game.t(), timeout}

  @spec new(Game.t(), PlayerID.t()) :: t
  def new(game, player_id),
    do: {:reply, Tally.new(game, player_id), game, @timeout}

  @spec new(action_or_reason, Game.t(), Request.t(), PlayerID.t()) :: t
  def new(:add_player = _action, game, request, player_id) do
    reason =
      case game.state.game_state do
        :initialized -> :unexpected
        :players_set -> :player2_already_added
        game_state when game_state in @player_turns -> :player2_already_added
        :game_over -> :game_over
      end

    new(reason, game, request, player_id)
  end

  def new(action, game, request, player_id) when action in @position_actions do
    reason =
      case game.state.game_state do
        :initialized -> :player2_not_added
        :players_set -> :islands_already_set
        game_state when game_state in @player_turns -> :islands_already_set
        :game_over -> :game_over
      end

    new(reason, game, request, player_id)
  end

  def new(:set_islands = _action, game, request, player_id) do
    reason =
      case game.state.game_state do
        :initialized -> :player2_not_added
        :players_set -> :unexpected
        game_state when game_state in @player_turns -> :both_players_islands_set
        :game_over -> :game_over
      end

    new(reason, game, request, player_id)
  end

  def new(:guess_coord = _action, game, request, player_id) do
    reason =
      case game.state.game_state do
        :initialized -> :player2_not_added
        :players_set -> :not_both_players_islands_set
        game_state when game_state in @player_turns -> :not_player_turn
        :game_over -> :game_over
      end

    new(reason, game, request, player_id)
  end

  def new(:stop = _action, game, request, player_id) do
    reason =
      case game.state.game_state do
        :initialized -> :player2_not_added
        :players_set -> :not_both_players_islands_set
        game_state when game_state in @player_turns -> :not_player_turn
        :game_over -> :game_over
      end

    new(reason, game, request, player_id)
  end

  def new(reason, game, request, player_id) do
    game
    |> Game.update_request(request)
    |> Game.update_response({:error, reason})
    |> GameServer.save()
    |> new(player_id)
  end
end