defmodule Berrypod.Orders.OrderNotifier do
  @moduledoc """
  Sends transactional emails for orders.

  Order confirmation after payment, shipping notification when dispatched.
  """

  import Swoosh.Email

  alias Berrypod.{ActivityLog, Cart}
  alias Berrypod.Mailer

  require Logger

  @doc """
  Sends an order confirmation email after successful payment.

  Skips silently if the order has no customer email.
  """
  def deliver_order_confirmation(%{customer_email: nil}), do: {:ok, :no_email}
  def deliver_order_confirmation(%{customer_email: ""}), do: {:ok, :no_email}

  def deliver_order_confirmation(order) do
    subject = "Order confirmed - #{order.order_number}"

    body = """
    ==============================

    Thanks for your order!

    Order: #{order.order_number}

    #{format_items(order.items)}
    Total: #{Cart.format_price(order.total)}

    #{format_shipping_address(order.shipping_address)}
    We'll send you another email when your order ships.

    ==============================
    """

    result = deliver(order.customer_email, subject, body)

    case result do
      {:ok, _} ->
        ActivityLog.log_event(
          "order.email.confirmation_sent",
          "Order confirmation sent to #{order.customer_email}",
          order_id: order.id
        )

      _ ->
        :ok
    end

    result
  end

  @doc """
  Sends a magic link for the customer to view their orders.

  The link is time-limited (controlled by the caller's token expiry).
  """
  def deliver_order_lookup(email, link) do
    subject = "Your order lookup link"

    body = """
    ==============================

    Here's your link to view your orders:

    #{link}

    This link expires in 1 hour.

    If you didn't request this, you can ignore this email.

    ==============================
    """

    deliver(email, subject, body)
  end

  @doc """
  Sends a shipping notification with tracking info.

  Skips silently if the order has no customer email.
  """
  def deliver_shipping_notification(%{customer_email: nil}), do: {:ok, :no_email}
  def deliver_shipping_notification(%{customer_email: ""}), do: {:ok, :no_email}

  def deliver_shipping_notification(order) do
    subject = "Your order has shipped - #{order.order_number}"

    body = """
    ==============================

    Good news! Your order #{order.order_number} is on its way.

    #{format_tracking(order)}
    Thanks for shopping with us.

    ==============================
    """

    result = deliver(order.customer_email, subject, body)

    case result do
      {:ok, _} ->
        ActivityLog.log_event(
          "order.email.shipping_sent",
          "Shipping notification sent to #{order.customer_email}",
          order_id: order.id,
          payload: %{tracking_number: order.tracking_number}
        )

      _ ->
        :ok
    end

    result
  end

  @doc """
  Sends a cart recovery email to a customer who abandoned a Stripe checkout.

  Plain text, no tracking pixels, single send. Includes an unsubscribe link.
  """
  def deliver_cart_recovery(cart, order, unsubscribe_url) do
    base_url = BerrypodWeb.Endpoint.url()

    body = """
    ==============================

    You recently started a checkout but didn't complete it.

    Your cart had:
    #{format_cart_items(order.items, base_url)}
    Total: #{Cart.format_price(cart.cart_total)}

    We're only sending this once.

    Don't want to hear from us? Unsubscribe: #{unsubscribe_url}

    ==============================
    """

    result = deliver(cart.customer_email, "You left something behind", body)

    case result do
      {:ok, _} ->
        ActivityLog.log_event(
          "abandoned_cart.email_sent",
          "Recovery email sent to #{cart.customer_email}",
          payload: %{email: cart.customer_email}
        )

      _ ->
        :ok
    end

    result
  end

  # --- Private ---

  defp deliver(recipient, subject, body) do
    shop_name = Berrypod.Settings.get_setting("shop_name", "Berrypod")
    from_address = Berrypod.Settings.get_setting("email_from_address", "contact@example.com")

    email =
      new()
      |> to(recipient)
      |> from({shop_name, from_address})
      |> subject(subject)
      |> text_body(body)

    case Mailer.deliver(email) do
      {:ok, _metadata} = result ->
        result

      {:error, reason} = error ->
        Logger.warning("Failed to send email to #{recipient}: #{inspect(reason)}")
        error
    end
  end

  defp format_items(items) when is_list(items) do
    items
    |> Enum.map_join("\n", fn item ->
      price = Cart.format_price(item.unit_price * item.quantity)
      "  #{item.quantity}x #{item.product_name} (#{item.variant_title}) - #{price}"
    end)
  end

  defp format_items(_), do: ""

  defp format_cart_items(items, base_url) when is_list(items) do
    items
    |> Enum.map_join("\n", fn item ->
      price = Cart.format_price(item.unit_price * item.quantity)
      line = "  #{item.quantity}x #{item.product_name} (#{item.variant_title}) - #{price}"

      if item.product_id do
        line <> "\n  #{base_url}/products/#{item.product_id}"
      else
        line
      end
    end)
  end

  defp format_cart_items(_, _), do: ""

  defp format_shipping_address(address) when is_map(address) and map_size(address) > 0 do
    lines =
      [
        address["name"],
        address["line1"],
        address["line2"],
        [address["city"], address["postal_code"]] |> Enum.reject(&is_nil/1) |> Enum.join(" "),
        address["state"],
        address["country"]
      ]
      |> Enum.reject(&(is_nil(&1) or &1 == ""))
      |> Enum.map_join("\n", &"  #{&1}")

    "Shipping to:\n#{lines}\n\n"
  end

  defp format_shipping_address(_), do: ""

  defp format_tracking(order) do
    cond do
      order.tracking_url not in [nil, ""] and order.tracking_number not in [nil, ""] ->
        "Tracking: #{order.tracking_number}\n#{order.tracking_url}\n\n"

      order.tracking_number not in [nil, ""] ->
        "Tracking: #{order.tracking_number}\n\n"

      true ->
        "Tracking details will follow once the carrier updates.\n\n"
    end
  end
end
