defmodule Tarakan.Billing.StripeStub do
  @moduledoc """
  Test double for `Tarakan.Billing.Stripe`.

  Serves deterministic default responses for the endpoints the market flow
  uses. Tests can override behaviour per process:

      Tarakan.Billing.StripeStub.respond_with({:error, :card_declined})
      Tarakan.Billing.StripeStub.respond_with(fn :post, "/v1/refunds", _body -> ... end)
      Tarakan.Billing.StripeStub.reset()
  """

  @behaviour Tarakan.Billing.Stripe

  @impl true
  def request(method, path, body_or_params \\ nil) do
    case Process.get(:stripe_stub_response) do
      nil -> default_response(method, path, body_or_params)
      fun when is_function(fun, 3) -> fun.(method, path, body_or_params)
      response -> response
    end
  end

  @doc "Overrides the stubbed response for the current test process."
  def respond_with(response_or_fun), do: Process.put(:stripe_stub_response, response_or_fun)

  @doc "Restores the default responses for the current test process."
  def reset, do: Process.delete(:stripe_stub_response)

  defp default_response(:post, "/v1/checkout/sessions", _body) do
    id = "cs_test_#{System.unique_integer([:positive])}"

    {:ok,
     %{
       "id" => id,
       "url" => "https://checkout.stripe.com/c/pay/#{id}",
       "payment_intent" => "pi_#{id}",
       "status" => "open"
     }}
  end

  defp default_response(:get, "/v1/checkout/sessions/" <> id, _params) do
    {:ok, %{"id" => id, "payment_intent" => "pi_#{id}", "status" => "complete"}}
  end

  defp default_response(:post, "/v1/refunds", body) do
    payment_intent = body[:payment_intent] || body["payment_intent"]

    {:ok,
     %{
       "id" => "re_test_#{System.unique_integer([:positive])}",
       "payment_intent" => payment_intent,
       "status" => "succeeded"
     }}
  end

  defp default_response(:post, "/v1/billing_portal/sessions", body) do
    customer =
      case body do
        map when is_map(map) ->
          map[:customer] || map["customer"]

        list when is_list(list) ->
          Enum.find_value(list, fn
            {:customer, value} -> value
            {"customer", value} -> value
            _other -> nil
          end)

        _other ->
          nil
      end

    {:ok,
     %{
       "id" => "bps_test_#{System.unique_integer([:positive])}",
       "customer" => customer,
       "url" => "https://billing.stripe.com/p/session/test_#{System.unique_integer([:positive])}"
     }}
  end

  defp default_response(_method, _path, _body), do: {:error, :unexpected_request}
end
