defmodule MatchmakingEx.Player do
  @moduledoc "A GenServer representing a single player in the matchmaking system."
  use GenServer

  alias MatchmakingEx.Server
  alias MatchmakingEx.PendingMatch

  # --- Public API ---

  @doc "Starts a Player process."
  @spec start_link(player_id :: String.t()) :: GenServer.on_start()
  def start_link(player_id) do
    GenServer.start_link(__MODULE__, player_id, name: via_name(player_id))
  end

  @doc "Initiates a match search for the player."
  @spec search(player_id :: String.t(), filters :: map()) :: :ok | {:error, String.t()}
  def search(player_id, filters \\ %{}) do
    case Registry.lookup(MatchmakingEx.Registry, player_id) do
      [{pid, _value}] ->
        Server.find_match(pid, player_id, filters)
        :ok
      [] ->
        {:error, "Player #{player_id} not found."}
    end
  end

  @doc "Accepts the current pending match."
  @spec accept(player_id :: String.t()) :: :ok | any
  def accept(player_id) do
    with {:ok, %{current_match_id: match_id}} when not is_nil(match_id) <- get_player_state(player_id) do
      PendingMatch.accept(match_id, player_id)
    else
      _ -> IO.puts "[Player #{player_id}] No pending match to accept."
    end
  end

  @doc "Declines the current pending match."
  @spec decline(player_id :: String.t()) :: :ok | any
  def decline(player_id) do
    with {:ok, %{current_match_id: match_id}} when not is_nil(match_id) <- get_player_state(player_id) do
      PendingMatch.decline(match_id, player_id)
    else
      _ -> IO.puts "[Player #{player_id}] No pending match to decline."
    end
  end

  # --- GenServer Callbacks ---

  @impl true
  def init(player_id) do
    IO.puts("[Player #{player_id}] Process started (PID: #{inspect(self())}).")
    {:ok, %{id: player_id, current_match_id: nil}}
  end

  @impl true
  def handle_info({:match_ready_for_confirmation, match_id, players}, state) do
    IO.puts("""
    [Player #{state.id}] Match ready for confirmation! (ID: #{match_id})
    Players: #{inspect(players)}
    You have 15 seconds to accept.
    To accept, use: MatchmakingEx.Player.accept("#{state.id}")
    To decline, use: MatchmakingEx.Player.decline("#{state.id}")
    """)
    {:noreply, %{state | current_match_id: match_id}}
  end

  @impl true
  def handle_info({:match_confirmed, match_id}, state) do
    IO.puts("[Player #{state.id}] Match #{match_id} CONFIRMED! Entering game...")
    {:noreply, %{state | current_match_id: nil}}
  end

  @impl true
  def handle_info({:match_cancelled, reason}, state) do
    IO.puts("[Player #{state.id}] Match CANCELLED. Reason: #{reason}")
    {:noreply, %{state | current_match_id: nil}}
  end

  @impl true
  def handle_info({:search_rejected_cooldown, expiration_time}, state) do
    remaining = DateTime.diff(expiration_time, DateTime.utc_now(), :second)
    IO.puts("[Player #{state.id}] You cannot search for a match. You are on cooldown for #{remaining} more seconds.")
    {:noreply, state}
  end

  @impl true
  def handle_call(:get_state, _from, state) do
    {:reply, {:ok, state}, state}
  end

  # --- Private Functions ---

  defp via_name(player_id), do: {:via, Registry, {MatchmakingEx.Registry, player_id}}

  defp get_player_state(player_id) do
    case GenServer.call(via_name(player_id), :get_state) do
      {:ok, state} -> {:ok, state}
      _ -> {:error, :not_found}
    end
  end
end
