defmodule AprsmeWeb.Plugs.HealthCheck do
  @moduledoc """
  Health check plug that returns appropriate status based on application state.
  Used by Kubernetes liveness and readiness probes.
  """

  import Plug.Conn

  alias Ecto.Adapters.SQL

  def init(opts), do: opts

  def call(%{request_path: "/health"} = conn, opts) do
    probe_type = opts[:probe_type] || :readiness

    case check_health(probe_type) do
      {:ok, message} ->
        conn
        |> put_resp_content_type("text/plain")
        |> send_resp(200, message)
        |> halt()

      {:error, message} ->
        conn
        |> put_resp_content_type("text/plain")
        |> send_resp(503, message)
        |> halt()
    end
  end

  def call(conn, _opts), do: conn

  defp check_health(:liveness) do
    # Liveness probe - only fails if the application is truly broken
    # Continue returning OK even during shutdown to prevent unnecessary restarts
    case basic_health_checks() do
      :ok -> {:ok, "OK"}
      {:error, reason} -> {:error, "Liveness check failed: #{reason}"}
    end
  end

  defp check_health(:readiness) do
    # Readiness probe - fails when shutting down to stop new traffic.
    health_status = Application.get_env(:aprsme, :health_status, :healthy)
    readiness_status(health_status, shutting_down?())
  end

  defp readiness_status(:draining, _shutting_down?), do: {:error, "Application is draining connections"}

  defp readiness_status(_status, true), do: {:error, "Application is shutting down"}

  defp readiness_status(_status, false) do
    case full_health_checks() do
      :ok -> {:ok, "OK"}
      {:error, reason} -> {:error, "Readiness check failed: #{reason}"}
    end
  end

  # Basic checks for liveness
  # Check if the application is running
  defp basic_health_checks do
    _ = Application.get_env(:aprsme, :env)
    :ok
  rescue
    _ -> {:error, "Application not responding"}
  end

  defp full_health_checks do
    # Comprehensive checks for readiness
    with :ok <- check_database_connection() do
      check_pubsub()
    end
  end

  defp check_database_connection do
    case SQL.query(Aprsme.Repo, "SELECT 1", [], timeout: 1000) do
      {:ok, _} -> :ok
      _ -> {:error, "Database connection failed"}
    end
  rescue
    _ -> {:error, "Database check failed"}
  end

  defp check_pubsub do
    _ = Phoenix.PubSub.broadcast(Aprsme.PubSub, "health_check", :ping)
    :ok
  rescue
    _ -> {:error, "PubSub check failed"}
  end

  defp shutting_down? do
    # Check if ShutdownHandler process exists and is shutting down
    Aprsme.ShutdownHandler |> Process.whereis() |> ask_shutting_down?()
  end

  # No ShutdownHandler process → definitely not shutting down.
  defp ask_shutting_down?(nil), do: false

  defp ask_shutting_down?(pid) when is_pid(pid) do
    ask_if_alive(Process.alive?(pid), pid)
  end

  defp ask_if_alive(false, _pid), do: false

  defp ask_if_alive(true, pid) do
    timeout = Application.get_env(:aprsme, :health_check_call_timeout_ms, 5000)
    GenServer.call(pid, :shutting_down?, timeout)
  catch
    _kind, _reason -> false
  end
end
