defmodule Towerops.Preseem.Client do
  @moduledoc """
  HTTP client for the Preseem Model API.

  Uses Req with built-in test support via `Req.Test`.
  Auth: HTTP Basic (API key as username, empty password).
  """

  require Logger

  @base_url "https://api.preseem.com/model/v1"

  @doc """
  Tests the connection to the Preseem API by fetching a page of access points.

  Returns `{:ok, body}` on success, `{:error, reason}` on failure.
  """
  def test_connection(api_key) do
    case request(:get, "#{@base_url}/access_points?limit=100", api_key) do
      {:ok, %{status: status, body: body}} when status in 200..299 ->
        {:ok, body}

      {:ok, %{status: 401}} ->
        {:error, :unauthorized}

      {:ok, %{status: 403}} ->
        {:error, :forbidden}

      {:ok, %{status: status, body: body}} ->
        Logger.warning("Preseem API unexpected status #{status}: #{inspect(body)}")
        {:error, {:unexpected_status, status}}

      {:error, reason} ->
        Logger.error("Preseem API connection error: #{inspect(reason)}")
        {:error, reason}
    end
  end

  @doc """
  Lists all access points with scores from the Preseem API.

  Fetches from `/access_point_scores` which returns rich data per AP.
  Returns `{:ok, [access_point_score]}` on success.
  """
  def list_access_points(api_key) do
    case request(:get, "#{@base_url}/access_point_scores", api_key) do
      {:ok, %{status: status, body: body}} when status in 200..299 ->
        {:ok, normalize_scores_response(body)}

      {:ok, %{status: 401}} ->
        {:error, :unauthorized}

      {:ok, %{status: 403}} ->
        {:error, :forbidden}

      {:ok, %{status: status, body: body}} ->
        {:error, {:unexpected_status, status, body}}

      {:error, reason} ->
        {:error, reason}
    end
  end

  @doc """
  Lists access points from the basic `/access_points` endpoint with pagination.

  Returns `{:ok, [access_point]}` on success.
  """
  def list_access_points_basic(api_key) do
    fetch_all_pages(api_key, "#{@base_url}/access_points?limit=500", [])
  end

  defp fetch_all_pages(api_key, url, acc) do
    case request(:get, url, api_key) do
      {:ok, %{status: status, body: %{"data" => data, "paginator" => paginator}}}
      when status in 200..299 ->
        all = acc ++ data

        if paginator["page"] < paginator["page_count"] do
          next_page = paginator["page"] + 1
          next_url = "#{@base_url}/access_points?limit=500&page=#{next_page}"
          fetch_all_pages(api_key, next_url, all)
        else
          {:ok, all}
        end

      {:ok, %{status: 401}} ->
        {:error, :unauthorized}

      {:ok, %{status: 403}} ->
        {:error, :forbidden}

      {:ok, %{status: status, body: body}} ->
        {:error, {:unexpected_status, status, body}}

      {:error, reason} ->
        {:error, reason}
    end
  end

  # The scores endpoint may return an array, a wrapped object with "data" key,
  # or a single object. Normalize to a list.
  defp normalize_scores_response(body) when is_list(body), do: body

  defp normalize_scores_response(%{"data" => data}) when is_list(data), do: data

  defp normalize_scores_response(body) when is_map(body), do: [body]

  defp request(method, url, api_key) do
    opts = [
      method: method,
      url: url,
      auth: {:basic, "#{api_key}:"},
      headers: [{"accept", "application/json"}]
    ]

    # Only use Req.Test plug in test environment
    opts =
      if Application.get_env(:towerops, :env) == :test do
        Keyword.put(opts, :plug, {Req.Test, __MODULE__})
      else
        opts
      end

    Req.request(opts)
  rescue
    exception ->
      {:error, Exception.message(exception)}
  end
end
