defmodule Parrot.Sip.HandlerAdapter.ResponseHandler do
  @moduledoc """
  Functions for handling SIP responses.

  This module handles the processing of responses from user handlers,
  including formatting and sending responses through the UAS layer.
  """

  require Logger
  alias Parrot.Sip.UAS

  @doc """
  Processes a response from the user handler and sends it via the UAS.

  This function takes a response tuple from the user handler, constructs a proper
  SIP response message, and sends it through the UAS layer.

  ## Parameters

    * `response` - The response tuple from the user handler
    * `uas` - The UAS object
    * `req_sip_msg` - The original request SIP message

  ## Returns

  The return value of `UAS.response/2`
  """
  def process_user_response({:respond, status, reason, headers, body}, uas, req_sip_msg) do
    Logger.debug("Processing user response: #{status} #{reason}")

    resp = UAS.make_reply(status, reason, uas, req_sip_msg)
    resp_with_headers = add_headers(resp, headers)
    resp_with_body = Parrot.Sip.Message.set_body(resp_with_headers, body)
    UAS.response(resp_with_body, uas)
  end

  def process_user_response({:proxy, uri}, uas, req_sip_msg) do
    Logger.info("Proxying request to #{uri}")
    proxy_request(uri, req_sip_msg, uas)
  end

  def process_user_response({:b2bua, _uri}, _uas, _req_sip_msg) do
    Logger.warning("B2BUA functionality has been removed")
    {:respond, 501, "Not Implemented", %{}, "", %{}}
  end

  def process_user_response(:noreply, _uas, _req_sip_msg) do
    Logger.debug("User handler returned :noreply.")
    :ok
  end

  def process_user_response(other, _uas, _req_sip_msg) do
    Logger.error("Unknown user response format: #{inspect(other)}")
    :ok
  end

  # Private functions

  defp add_headers(sip_msg, headers) do
    alias Parrot.Sip.HandlerAdapter.HeaderHandler
    HeaderHandler.add_headers(sip_msg, headers)
  end

  @doc """
  Proxies a SIP request to another URI.

  This function implements SIP proxying by forwarding the request to another URI
  and relaying the responses back to the original sender.

  For INVITE requests, it sends a provisional 100 Trying response first.

  ## Parameters

    * `uri` - The target URI to proxy the request to
    * `req_sip_msg` - The original SIP request message
    * `uas_obj` - The UAS object representing the original transaction

  ## Returns

  `:ok`
  """
  def proxy_request(uri, req_sip_msg, uas_obj) do
    # Ensure Transport and UAC are accessible or passed if these become instance methods
    if req_sip_msg.method == :invite do
      trying_resp = UAS.make_reply(100, "Trying", uas_obj, req_sip_msg)
      UAS.response(trying_resp, uas_obj)
    end

    forward_sip_msg = prepare_request_for_forwarding(req_sip_msg, uri)

    Parrot.Sip.UAC.request(forward_sip_msg, fn response ->
      case response do
        {:message, resp_sip_msg} ->
          forwarded_resp = prepare_response_for_forwarding(resp_sip_msg, req_sip_msg)
          UAS.response(forwarded_resp, uas_obj)

        {:stop, reason} ->
          Logger.warning("Proxy request failed: #{inspect(reason)}")
          error_resp = UAS.make_reply(500, "Proxy Error", uas_obj, req_sip_msg)
          UAS.response(error_resp, uas_obj)
      end
    end)

    :ok
  end

  @doc """
  Prepares a SIP request for forwarding to another URI.

  This function modifies a SIP request to prepare it for forwarding to a different
  destination. It:

  1. Updates the Request-URI to the target URI
  2. Decrements the Max-Forwards header to prevent infinite loops
  3. For INVITE requests, adds a Record-Route header to ensure responses are routed back
     through this proxy

  ## Parameters

    * `req_sip_msg` - The original SIP request message
    * `target_uri` - The target URI to forward the request to

  ## Returns

  The modified SIP request message ready for forwarding
  """
  def prepare_request_for_forwarding(req_sip_msg, target_uri) do
    # Set the new request URI
    req1 = %{req_sip_msg | request_uri: target_uri}

    # Decrement Max-Forwards
    max_forwards_val =
      case Parrot.Sip.Message.get_header(req1, "max-forwards") do
        nil -> Parrot.Sip.Headers.MaxForwards.default()
        val -> val
      end

    new_max_forwards =
      case Parrot.Sip.Headers.MaxForwards.decrement(max_forwards_val) do
        nil -> 0
        v -> v
      end

    req2 = Parrot.Sip.Message.set_header(req1, "max-forwards", new_max_forwards)

    # Add Record-Route if INVITE
    if req2.method == :invite do
      local_uri = Parrot.Sip.Transport.local_uri()
      record_route_hdr = Parrot.Sip.Headers.RecordRoute.new(local_uri)

      existing_routes =
        case Parrot.Sip.Message.get_header(req2, "record-route") do
          nil -> []
          r -> List.wrap(r)
        end

      Parrot.Sip.Message.set_header(req2, "record-route", [record_route_hdr | existing_routes])
    else
      req2
    end
  end

  @doc """
  Prepares a SIP response for forwarding to the original requester.

  This function takes a response received from the forwarded request and
  prepares it to be sent back to the original requester. It:

  1. Creates a new response to the original request with the same status code
  2. Copies the body from the received response
  3. Copies essential headers (Contact, Content-Type, Record-Route) from the received response

  ## Parameters

    * `resp_sip_msg` - The response received from the forwarded request
    * `orig_req_sip_msg` - The original request from the initial requester

  ## Returns

  The modified SIP response message ready to send back to the original requester
  """
  def prepare_response_for_forwarding(resp_sip_msg, orig_req_sip_msg) do
    status = resp_sip_msg.status_code
    reason = resp_sip_msg.reason_phrase || Parrot.Sip.Message.default_reason_phrase(status)
    base_resp = Parrot.Sip.Message.reply(orig_req_sip_msg, status, reason)
    resp_with_body = Parrot.Sip.Message.set_body(base_resp, resp_sip_msg.body)

    # Add other necessary headers
    headers_to_copy = ["contact", "content-type", "record-route"]

    Enum.reduce(headers_to_copy, resp_with_body, fn header_key, acc_resp ->
      case Parrot.Sip.Message.get_header(resp_sip_msg, header_key) do
        nil -> acc_resp
        value -> Parrot.Sip.Message.set_header(acc_resp, header_key, value)
      end
    end)
  end
end
