Clone
mini_game_channel.ex
defmodule PiDayWeb.MiniGameChannel do
  use PiDayWeb, :channel

  alias PiDay.Game

  @valid_games ~w(pi_memory monte_carlo slice_the_pi pi_trivia projectile_pi)

  @impl true
  def join("game:mini:" <> game_type, _payload, socket) when game_type in @valid_games do
    socket = assign(socket, :game_type, game_type)
    {:ok, socket}
  end

  def join(_, _, _), do: {:error, %{reason: "invalid game"}}

  # --- Pi Memory Sprint ---

  @impl true
  def handle_in("pi_check_digit", %{"position" => pos, "digit" => digit}, socket) do
    correct = Game.check_pi_digit(pos, digit)

    if correct do
      broadcast!(socket, "pi_progress", %{
        player_id: socket.assigns.player.id,
        name: socket.assigns.player.name,
        position: pos + 1
      })
    end

    {:reply, {:ok, %{correct: correct}}, socket}
  end

  def handle_in("pi_game_over", %{"score" => score}, socket) do
    player = socket.assigns.player

    Game.record_score(%{
      player_id: player.id,
      game_type: "pi_memory",
      score: score,
      metadata: %{digits: score}
    })

    broadcast!(socket, "pi_score", %{
      player_id: player.id,
      name: player.name,
      score: score
    })

    {:reply, {:ok, %{recorded: true}}, socket}
  end

  # --- Monte Carlo Pi ---

  def handle_in("mc_submit", %{"estimate" => estimate, "darts" => darts}, socket) do
    player = socket.assigns.player
    error = abs(estimate - :math.pi())
    score = max(0, round(1000 - error * 1000))

    Game.record_score(%{
      player_id: player.id,
      game_type: "monte_carlo",
      score: score,
      metadata: %{estimate: estimate, darts: darts, error: Float.round(error, 6)}
    })

    broadcast!(socket, "mc_result", %{
      player_id: player.id,
      name: player.name,
      estimate: estimate,
      score: score
    })

    {:reply, {:ok, %{score: score, error: Float.round(error, 6)}}, socket}
  end

  # --- Slice the Pi (speed math) ---

  def handle_in("slice_get_problem", _payload, socket) do
    problem = Game.generate_math_problem()
    {:reply, {:ok, problem}, socket}
  end

  def handle_in("slice_answer", %{"correct" => correct, "time_ms" => time_ms, "streak" => streak}, socket) do
    player = socket.assigns.player
    points = if correct, do: max(10, 100 - div(time_ms, 100)) + streak * 10, else: 0

    if correct do
      broadcast!(socket, "slice_correct", %{
        player_id: player.id,
        name: player.name,
        streak: streak
      })
    end

    {:reply, {:ok, %{points: points}}, socket}
  end

  def handle_in("slice_game_over", %{"score" => score, "correct" => correct, "total" => total}, socket) do
    player = socket.assigns.player

    Game.record_score(%{
      player_id: player.id,
      game_type: "slice_the_pi",
      score: score,
      metadata: %{correct: correct, total: total}
    })

    broadcast!(socket, "slice_score", %{
      player_id: player.id,
      name: player.name,
      score: score
    })

    {:reply, {:ok, %{recorded: true}}, socket}
  end

  # --- Pi Trivia Blitz ---

  def handle_in("trivia_get_question", _payload, socket) do
    question = Game.get_trivia_question()
    {:reply, {:ok, question}, socket}
  end

  def handle_in("trivia_answer", %{"correct" => correct, "time_ms" => time_ms, "streak" => streak}, socket) do
    player = socket.assigns.player
    points = if correct, do: max(10, 150 - div(time_ms, 100)) + streak * 15, else: 0

    if correct do
      broadcast!(socket, "trivia_correct", %{
        player_id: player.id,
        name: player.name,
        streak: streak
      })
    end

    {:reply, {:ok, %{points: points}}, socket}
  end

  def handle_in("trivia_game_over", %{"score" => score, "correct" => correct, "total" => total}, socket) do
    player = socket.assigns.player

    Game.record_score(%{
      player_id: player.id,
      game_type: "pi_trivia",
      score: score,
      metadata: %{correct: correct, total: total}
    })

    broadcast!(socket, "trivia_score", %{
      player_id: player.id,
      name: player.name,
      score: score
    })

    {:reply, {:ok, %{recorded: true}}, socket}
  end

  # --- Projectile Pi ---

  def handle_in("projectile_get_target", _payload, socket) do
    target = Game.generate_projectile_target()
    {:reply, {:ok, target}, socket}
  end

  def handle_in("projectile_submit", %{"score" => score, "target_x" => target_x, "best_error" => best_error}, socket) do
    player = socket.assigns.player

    Game.record_score(%{
      player_id: player.id,
      game_type: "projectile_pi",
      score: score,
      metadata: %{target_x: target_x, best_error: best_error}
    })

    broadcast!(socket, "projectile_score", %{
      player_id: player.id,
      name: player.name,
      score: score
    })

    {:reply, {:ok, %{recorded: true}}, socket}
  end
end