lib/game_server.ex

defmodule Perudex.GameServer do
  @moduledoc """
  This module is a GenServer implementation to control the Perudo game state.
  """

  use GenServer, restart: :transient

  alias Perudex.{Game, NotifierServer}

  @type id :: any
  @type player :: %{id: any, callback_mod: module, callback_arg: any}
  @type callback_arg :: any

  @impl true
  def init({id, players_ids}),
    do: {:ok, players_ids |> Game.start(5) |> handle_move_result(%{id: id, game: nil})}

  def start_link({id, players}),
    do: GenServer.start_link(__MODULE__, {id, Enum.map(players, & &1.id)}, name: service_name(id))

  @spec move(id, Game.player_id(), Game.move()) :: any
  def move(game_id, player_id, move),
    do: GenServer.call(service_name(game_id), {:move, player_id, move})

  @impl true
  def handle_call({:move, player_id, move}, _from, state),
    do:
      {:reply, :ok,
       state.game
       |> Game.play_move(player_id, move)
       |> handle_move_result(state)}

  defp handle_move_result({instructions, game}, state),
    do: Enum.reduce(instructions, %{state | game: game}, &handle_instruction(&2, &1))

  defp handle_instruction(state, {:notify_player, player_id, instruction_payload}) do
    NotifierServer.publish(state.id, player_id, instruction_payload)
    state
  end

  defp service_name(game_id), do: Perudex.service_name({__MODULE__, game_id})
end