defmodule WhoNeedHelp.Email.UnisenderGoAdapterTest do
  use ExUnit.Case, async: false

  import Swoosh.Email

  alias WhoNeedHelp.Email.UnisenderGoAdapter

  setup do
    previous_client = Application.get_env(:swoosh, :api_client)
    Application.put_env(:swoosh, :api_client, Swoosh.ApiClient.Req)

    on_exit(fn ->
      if previous_client do
        Application.put_env(:swoosh, :api_client, previous_client)
      else
        Application.delete_env(:swoosh, :api_client)
      end
    end)

    :ok
  end

  test "delivers a Swoosh email through the documented HTTPS JSON contract" do
    owner = self()

    plug = fn conn ->
      {:ok, request_body, conn} = Plug.Conn.read_body(conn)
      send(owner, {:request, conn, Jason.decode!(request_body)})

      response =
        Jason.encode!(%{
          "status" => "success",
          "job_id" => "job-123",
          "emails" => ["helper@example.com"]
        })

      conn
      |> Plug.Conn.put_resp_content_type("application/json")
      |> Plug.Conn.send_resp(200, response)
    end

    email =
      new()
      |> from({"Who Need Help", "contact@whoneedhelp.com"})
      |> to("helper@example.com")
      |> reply_to({"Support", "support@whoneedhelp.com"})
      |> subject("Confirmation instructions")
      |> text_body("Open the confirmation link")
      |> html_body("<p>Open the confirmation link</p>")
      |> header("X-WNH-Message-Type", "account-confirmation")
      |> header("List-Unsubscribe", "must-not-be-forwarded")
      |> put_provider_option(:idempotence_key, "stable-delivery-key")
      |> put_private(:client_options, plug: plug, retry: false)

    assert {:ok, %{provider: :unisender_go, job_id: "job-123", accepted_count: 1}} =
             UnisenderGoAdapter.deliver(email,
               api_key: "secret-api-key",
               base_url: "https://goapi.example.test/v1/"
             )

    assert_receive {:request, conn, payload}
    assert conn.method == "POST"
    assert conn.request_path == "/v1/email/send.json"
    assert Plug.Conn.get_req_header(conn, "x-api-key") == ["secret-api-key"]

    assert payload == %{
             "message" => %{
               "body" => %{
                 "html" => "<p>Open the confirmation link</p>",
                 "plaintext" => "Open the confirmation link"
               },
               "from_email" => "contact@whoneedhelp.com",
               "from_name" => "Who Need Help",
               "headers" => %{"X-WNH-Message-Type" => "account-confirmation"},
               "idempotence_key" => "stable-delivery-key",
               "recipients" => [%{"email" => "helper@example.com"}],
               "reply_to" => "support@whoneedhelp.com",
               "reply_to_name" => "Support",
               "subject" => "Confirmation instructions",
               "track_links" => 0,
               "track_read" => 0
             }
           }
  end

  test "returns a redacted recipient rejection without exposing addresses" do
    plug = fn conn ->
      response =
        Jason.encode!(%{
          "status" => "success",
          "job_id" => "job-123",
          "failed_emails" => %{"private@example.com" => "unsubscribed"}
        })

      Plug.Conn.send_resp(conn, 200, response)
    end

    assert {:error, {:unisender_go_recipient_rejected, %{"unsubscribed" => 1}}} =
             UnisenderGoAdapter.deliver(email(plug), api_key: "secret-api-key")
  end

  test "adds skip_unsubscribe only after the provider capability is explicitly enabled" do
    owner = self()

    plug = fn conn ->
      {:ok, request_body, conn} = Plug.Conn.read_body(conn)
      send(owner, {:request, Jason.decode!(request_body)})

      Plug.Conn.send_resp(
        conn,
        200,
        Jason.encode!(%{
          "status" => "success",
          "job_id" => "job-123",
          "emails" => ["helper@example.com"]
        })
      )
    end

    assert {:ok, _response} =
             UnisenderGoAdapter.deliver(email(plug),
               api_key: "secret-api-key",
               skip_unsubscribe: true
             )

    assert_receive {:request, %{"message" => %{"skip_unsubscribe" => 1}}}
  end

  test "returns structured API and invalid-response failures" do
    error_plug = fn conn ->
      Plug.Conn.send_resp(
        conn,
        401,
        Jason.encode!(%{"status" => "error", "code" => 101, "message" => "invalid API key"})
      )
    end

    assert {:error, {:unisender_go_api_error, 401, 101}} =
             UnisenderGoAdapter.deliver(email(error_plug), api_key: "secret-api-key")

    invalid_plug = fn conn -> Plug.Conn.send_resp(conn, 502, "not-json") end

    assert {:error, {:invalid_unisender_go_response, 502}} =
             UnisenderGoAdapter.deliver(email(invalid_plug), api_key: "secret-api-key")
  end

  test "rejects unsupported fields before making a request" do
    email =
      new()
      |> from("contact@whoneedhelp.com")
      |> to("helper@example.com")
      |> cc("operator@example.com")
      |> subject("Test")
      |> text_body("body")

    assert {:error, {:unsupported_email_feature, :cc}} =
             UnisenderGoAdapter.deliver(email, api_key: "secret-api-key")
  end

  test "rejects malformed or provider-incompatible messages before making a request" do
    assert {:error, :from_not_set} =
             new()
             |> to("helper@example.com")
             |> subject("Test")
             |> text_body("Body")
             |> UnisenderGoAdapter.deliver(api_key: "secret-api-key")

    assert {:error, :subject_not_set} =
             new()
             |> from("contact@whoneedhelp.com")
             |> to("helper@example.com")
             |> text_body("Body")
             |> UnisenderGoAdapter.deliver(api_key: "secret-api-key")

    too_many_recipients = List.duplicate("helper@example.com", 501)

    assert {:error, {:too_many_recipients, 500}} =
             new()
             |> from("contact@whoneedhelp.com")
             |> to(too_many_recipients)
             |> subject("Test")
             |> text_body("Body")
             |> UnisenderGoAdapter.deliver(api_key: "secret-api-key")

    assert {:error, {:idempotence_key_too_long, 64}} =
             email(fn conn -> Plug.Conn.send_resp(conn, 500, "not reached") end)
             |> put_provider_option(:idempotence_key, String.duplicate("x", 65))
             |> UnisenderGoAdapter.deliver(api_key: "secret-api-key")
  end

  test "validates the API key without including the adapter config in the exception" do
    assert_raise ArgumentError, "UniSender Go API key is missing", fn ->
      UnisenderGoAdapter.validate_config(api_key: "")
    end

    assert_raise ArgumentError,
                 "UniSender Go base URL must be an HTTPS URL without credentials",
                 fn ->
                   UnisenderGoAdapter.validate_config(
                     api_key: "secret-api-key",
                     base_url: "http://secret@example.test/api?key=secret"
                   )
                 end

    assert_raise ArgumentError, "UniSender Go skip_unsubscribe must be a boolean", fn ->
      UnisenderGoAdapter.validate_config(
        api_key: "secret-api-key",
        skip_unsubscribe: "true"
      )
    end
  end

  defp email(plug) do
    new()
    |> from({"Who Need Help", "contact@whoneedhelp.com"})
    |> to("helper@example.com")
    |> subject("Test")
    |> text_body("Body")
    |> put_private(:client_options, plug: plug, retry: false)
  end
end
