defmodule Berrypod.MailerTest do
  use Berrypod.DataCase, async: false

  alias Berrypod.Mailer
  alias Berrypod.Settings

  setup do
    # Store original config to restore after each test
    original = Application.get_env(:berrypod, Mailer)
    on_exit(fn -> Application.put_env(:berrypod, Mailer, original) end)
    :ok
  end

  describe "email_configured?/0" do
    test "returns false with Local adapter" do
      Application.put_env(:berrypod, Mailer, adapter: Swoosh.Adapters.Local)
      refute Mailer.email_configured?()
    end

    test "returns true with a real adapter" do
      Application.put_env(:berrypod, Mailer, adapter: Swoosh.Adapters.Postmark, api_key: "test")
      assert Mailer.email_configured?()
    end
  end

  describe "load_config/0" do
    test "loads adapter config from settings" do
      Settings.put_setting("email_adapter", "brevo")
      Settings.put_secret("email_brevo_api_key", "xkeysib-test-key-123")

      Mailer.load_config()

      config = Application.get_env(:berrypod, Mailer)
      assert config[:adapter] == Swoosh.Adapters.Brevo
      assert config[:api_key] == "xkeysib-test-key-123"
    end

    test "loads SMTP config with multiple fields" do
      Settings.put_setting("email_adapter", "smtp")
      Settings.put_setting("email_smtp_relay", "smtp.example.com")
      Settings.put_setting("email_smtp_port", 465, "integer")
      Settings.put_setting("email_smtp_username", "user@example.com")
      Settings.put_secret("email_smtp_password", "secret123")

      Mailer.load_config()

      config = Application.get_env(:berrypod, Mailer)
      assert config[:adapter] == Swoosh.Adapters.SMTP
      assert config[:relay] == "smtp.example.com"
      assert config[:port] == 465
      assert config[:username] == "user@example.com"
      assert config[:password] == "secret123"
    end

    test "is a no-op when no email_adapter is set" do
      original = Application.get_env(:berrypod, Mailer)
      Mailer.load_config()
      assert Application.get_env(:berrypod, Mailer) == original
    end
  end

  describe "email_verified?/0" do
    test "returns false when adapter is Local" do
      Application.put_env(:berrypod, Mailer, adapter: Swoosh.Adapters.Local)
      Mailer.mark_email_verified()
      refute Mailer.email_verified?()
    end

    test "returns false when configured but not verified" do
      Application.put_env(:berrypod, Mailer, adapter: Swoosh.Adapters.Postmark, api_key: "test")
      Mailer.clear_email_verified()
      refute Mailer.email_verified?()
    end

    test "returns true when configured and verified" do
      Application.put_env(:berrypod, Mailer, adapter: Swoosh.Adapters.Postmark, api_key: "test")
      Mailer.mark_email_verified()
      assert Mailer.email_verified?()
    end

    test "clear_email_verified resets the flag" do
      Application.put_env(:berrypod, Mailer, adapter: Swoosh.Adapters.Postmark, api_key: "test")
      Mailer.mark_email_verified()
      assert Mailer.email_verified?()

      Mailer.clear_email_verified()
      refute Mailer.email_verified?()
    end
  end

  describe "friendly_error/1" do
    # API errors with status codes
    test "401 with message body" do
      error = {401, %{"code" => "unauthorized", "message" => "Key not found"}}
      result = Mailer.friendly_error(error)
      assert result =~ "API key was rejected"
      assert result =~ "key not found"
    end

    test "401 without message" do
      assert Mailer.friendly_error({401, ""}) =~ "API key was rejected"
    end

    test "403 with message" do
      error = {403, %{"message" => "Insufficient permissions"}}
      result = Mailer.friendly_error(error)
      assert result =~ "permission"
      assert result =~ "insufficient permissions"
    end

    test "422 with errors array" do
      error = {422, %{"errors" => [%{"message" => "Invalid sender address"}]}}
      result = Mailer.friendly_error(error)
      assert result =~ "rejected"
      assert result =~ "invalid sender address"
    end

    test "429 rate limit" do
      assert Mailer.friendly_error({429, %{}}) =~ "Rate limit"
    end

    test "500 server error" do
      assert Mailer.friendly_error({500, %{"message" => "Internal error"}}) =~ "having issues"
    end

    test "Postmark ErrorMessage format" do
      error = {422, %{"ErrorCode" => 300, "ErrorMessage" => "Invalid 'From' address"}}
      result = Mailer.friendly_error(error)
      assert result =~ "invalid 'From' address"
    end

    # SMTP errors
    test "permanent SMTP failure" do
      assert Mailer.friendly_error({:permanent_failure, "550 User not found"}) =~ "rejected"

      assert Mailer.friendly_error({:permanent_failure, "550 User not found"}) =~
               "550 User not found"
    end

    test "temporary SMTP failure" do
      assert Mailer.friendly_error({:temporary_failure, "421 Try again"}) =~
               "temporarily unavailable"
    end

    test "SMTP retries exceeded" do
      assert Mailer.friendly_error({:retries_exceeded, nil}) =~ "connection failed"
    end

    test "SMTP auth failed" do
      assert Mailer.friendly_error({:auth_failed, nil}) =~ "login failed"
    end

    # Network errors
    test "timeout" do
      assert Mailer.friendly_error(:timeout) =~ "timed out"
    end

    test "connection refused" do
      assert Mailer.friendly_error(:econnrefused) =~ "Connection refused"
    end

    test "nxdomain" do
      assert Mailer.friendly_error(:nxdomain) =~ "hostname not found"
    end

    # Catch-all
    test "unknown error falls back to inspect" do
      assert Mailer.friendly_error({:something, :weird}) =~ "Delivery failed"
    end

    test "string errors pass through" do
      assert Mailer.friendly_error("some error") == "some error"
    end
  end

  describe "retryable_error?/1" do
    test "401 is not retryable" do
      refute Mailer.retryable_error?({401, %{}})
    end

    test "403 is not retryable" do
      refute Mailer.retryable_error?({403, %{}})
    end

    test "429 is retryable" do
      assert Mailer.retryable_error?({429, %{}})
    end

    test "500 is retryable" do
      assert Mailer.retryable_error?({500, %{}})
    end

    test "timeout is retryable" do
      assert Mailer.retryable_error?(:timeout)
    end

    test "temporary SMTP failure is retryable" do
      assert Mailer.retryable_error?({:temporary_failure, "421 Try again"})
    end

    test "permanent SMTP failure is not retryable" do
      refute Mailer.retryable_error?({:permanent_failure, "550 Bad mailbox"})
    end

    test "auth failure is not retryable" do
      refute Mailer.retryable_error?({:auth_failed, nil})
    end
  end

  describe "save_config/3" do
    test "saves valid adapter config" do
      assert {:ok, _} =
               Mailer.save_config("brevo", %{"api_key" => "xkeysib-abc123def456"}, "a@b.com")

      assert Settings.get_setting("email_adapter") == "brevo"
      assert Settings.has_secret?("email_brevo_api_key")
    end

    test "returns error for missing required fields" do
      assert {:error, errors} = Mailer.save_config("brevo", %{"api_key" => ""}, "a@b.com")
      assert errors["api_key"] =~ "required"
    end

    test "returns error for invalid key format" do
      assert {:error, errors} =
               Mailer.save_config("sendgrid", %{"api_key" => "not-a-sendgrid-key"}, "a@b.com")

      assert errors["api_key"] =~ "SG."
    end

    test "returns error for invalid integer" do
      assert {:error, errors} =
               Mailer.save_config(
                 "smtp",
                 %{"relay" => "smtp.example.com", "port" => "abc"},
                 "a@b.com"
               )

      assert errors["port"] =~ "number"
    end

    test "returns error for unknown adapter" do
      assert {:error, errors} = Mailer.save_config("unknown", %{}, "a@b.com")
      assert errors["_base"] =~ "select"
    end

    test "auto-sets from address when not configured" do
      Settings.delete_setting("email_from_address")
      Mailer.save_config("brevo", %{"api_key" => "xkeysib-abc123def456"}, "admin@shop.com")
      assert Settings.get_setting("email_from_address") == "admin@shop.com"
    end

    test "clears email verified flag" do
      Application.put_env(:berrypod, Mailer, adapter: Swoosh.Adapters.Postmark, api_key: "test")
      Mailer.mark_email_verified()
      assert Mailer.email_verified?()

      Mailer.save_config("brevo", %{"api_key" => "xkeysib-abc123def456"}, "a@b.com")
      refute Mailer.email_verified?()
    end

    test "clears settings from other providers" do
      Settings.put_secret("email_mailjet_api_key", "old-key")
      Settings.put_secret("email_mailjet_secret", "old-secret")

      Mailer.save_config("brevo", %{"api_key" => "xkeysib-abc123def456"}, "a@b.com")

      refute Settings.has_secret?("email_mailjet_api_key")
      refute Settings.has_secret?("email_mailjet_secret")
    end

    test "trims whitespace from values" do
      Mailer.save_config("brevo", %{"api_key" => "  xkeysib-abc123def456  "}, "a@b.com")
      # Key should be saved trimmed (verified via successful save — no format error)
      assert Settings.get_setting("email_adapter") == "brevo"
    end
  end

  describe "current_config/0" do
    test "returns {nil, %{}} when no adapter configured" do
      Application.put_env(:berrypod, Mailer, adapter: Swoosh.Adapters.Local)
      assert {nil, %{}} = Mailer.current_config()
    end

    test "returns adapter key and config when configured from settings" do
      Settings.put_setting("email_adapter", "brevo")
      Settings.put_secret("email_brevo_api_key", "xkeysib-test-key-123")

      Mailer.load_config()

      {adapter_key, config} = Mailer.current_config()
      assert adapter_key == "brevo"
      assert config["api_key"] =~ "•••"
    end
  end
end
