defmodule AprsmeWeb.Api.V1.CallsignController do
  @moduledoc """
  API v1 controller for callsign-related endpoints.
  """
  use AprsmeWeb, :controller

  alias Aprsme.ApiMetrics
  alias Aprsme.ErrorHandler
  alias Aprsme.Packets
  alias Aprsme.PlausibleAnalytics
  alias AprsmeWeb.Api.V1.CallsignJSON

  action_fallback AprsmeWeb.Api.V1.FallbackController

  @doc """
  Get the most recent packet for a given callsign.

  ## Parameters
    * `callsign` - The callsign to search for, with optional SSID (e.g., "N0CALL" or "N0CALL-9")

  ## Returns
    * 200 - Success with packet data
    * 404 - No packets found for the callsign
    * 400 - Invalid callsign format
  """
  def show(conn, %{"callsign" => callsign}) do
    PlausibleAnalytics.track(conn, "api_callsign_lookup", %{callsign: String.upcase(String.trim(callsign))})
    ApiMetrics.track(%{}, "api_callsign_lookup")

    with {:ok, normalized_callsign} <- validate_callsign(callsign),
         {:ok, packet} <- get_latest_packet(normalized_callsign) do
      conn
      |> put_status(:ok)
      |> put_view(json: CallsignJSON)
      |> render(:show, packet: packet)
    else
      {:error, :invalid_callsign} ->
        {:error, "Invalid callsign format"}

      {:error, :not_found} ->
        conn
        |> put_status(:ok)
        |> put_view(json: CallsignJSON)
        |> render(:not_found, callsign: callsign)

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

  defp validate_callsign(callsign) when is_binary(callsign) do
    normalized = callsign |> String.trim() |> String.upcase()
    check_callsign_format(validate_callsign_format(normalized), normalized)
  end

  defp check_callsign_format(true, normalized), do: {:ok, normalized}
  defp check_callsign_format(false, _normalized), do: {:error, :invalid_callsign}

  defp validate_callsign_format(callsign) do
    # Basic callsign validation
    # Matches patterns like: N0CALL, N0CALL-9, W1ABC-15, etc.
    # This is a simplified regex - a full implementation might be more comprehensive
    callsign_regex = ~r/^[A-Z0-9]{1,3}[0-9][A-Z]{1,4}(-[0-9]{1,2})?$/

    String.match?(callsign, callsign_regex) and String.length(callsign) <= 12
  end

  defp get_latest_packet(callsign) do
    # Get the most recent packet for this callsign regardless of age or type
    # Use cached version for better performance with error handling
    fn ->
      case Packets.get_latest_packet_for_callsign(callsign) do
        nil -> {:error, :not_found}
        packet -> {:ok, packet}
      end
    end
    |> ErrorHandler.with_error_handling(
      context: %{callsign: callsign, operation: :get_latest_packet},
      max_retries: 1,
      retry_delay: 500
    )
    |> case do
      {:ok, result} -> result
      {:error, error} -> {:error, error}
    end
  end
end
