defmodule Berrypod.Orders.AbandonedCartTest do
  use Berrypod.DataCase, async: true

  alias Berrypod.Orders

  import Berrypod.OrdersFixtures

  # ---------------------------------------------------------------------------
  # Abandoned carts
  # ---------------------------------------------------------------------------

  describe "create_abandoned_cart/1" do
    test "creates a record with required fields" do
      {:ok, cart} =
        Orders.create_abandoned_cart(%{
          customer_email: "test@example.com",
          stripe_session_id: "cs_test_abc",
          expired_at: DateTime.utc_now() |> DateTime.truncate(:second)
        })

      assert cart.customer_email == "test@example.com"
      assert cart.stripe_session_id == "cs_test_abc"
      assert is_nil(cart.emailed_at)
    end

    test "links to an existing order" do
      order = order_fixture()

      {:ok, cart} =
        Orders.create_abandoned_cart(%{
          customer_email: "buyer@example.com",
          stripe_session_id: "cs_test_xyz",
          order_id: order.id,
          cart_total: order.total,
          expired_at: DateTime.utc_now() |> DateTime.truncate(:second)
        })

      assert cart.order_id == order.id
    end

    test "enforces unique stripe_session_id" do
      attrs = %{
        customer_email: "test@example.com",
        stripe_session_id: "cs_test_dup",
        expired_at: DateTime.utc_now() |> DateTime.truncate(:second)
      }

      {:ok, _} = Orders.create_abandoned_cart(attrs)
      {:error, changeset} = Orders.create_abandoned_cart(attrs)
      assert "has already been taken" in errors_on(changeset).stripe_session_id
    end

    test "requires customer_email" do
      {:error, changeset} =
        Orders.create_abandoned_cart(%{
          stripe_session_id: "cs_test_noemail",
          expired_at: DateTime.utc_now() |> DateTime.truncate(:second)
        })

      assert "can't be blank" in errors_on(changeset).customer_email
    end
  end

  describe "get_abandoned_cart_by_session/1" do
    test "returns the cart for a known session" do
      {:ok, cart} =
        Orders.create_abandoned_cart(%{
          customer_email: "a@example.com",
          stripe_session_id: "cs_find_me",
          expired_at: DateTime.utc_now() |> DateTime.truncate(:second)
        })

      found = Orders.get_abandoned_cart_by_session("cs_find_me")
      assert found.id == cart.id
    end

    test "returns nil for unknown session" do
      assert is_nil(Orders.get_abandoned_cart_by_session("cs_nonexistent"))
    end
  end

  describe "mark_abandoned_cart_emailed/1" do
    test "sets emailed_at timestamp" do
      {:ok, cart} =
        Orders.create_abandoned_cart(%{
          customer_email: "b@example.com",
          stripe_session_id: "cs_mark_test",
          expired_at: DateTime.utc_now() |> DateTime.truncate(:second)
        })

      assert is_nil(cart.emailed_at)

      {:ok, updated} = Orders.mark_abandoned_cart_emailed(cart)
      assert not is_nil(updated.emailed_at)
    end
  end

  describe "has_recent_paid_order?/2" do
    test "returns true when email has a paid order within the window" do
      order_fixture(%{customer_email: "buyer@example.com", payment_status: "paid"})

      assert Orders.has_recent_paid_order?("buyer@example.com", 48)
    end

    test "returns false when no paid order exists" do
      refute Orders.has_recent_paid_order?("nobody@example.com", 48)
    end

    test "returns false when paid order is outside the window" do
      order = order_fixture(%{customer_email: "old@example.com", payment_status: "paid"})

      # Backdating: force inserted_at to be 3 days ago
      Berrypod.Repo.update_all(
        Ecto.Query.from(o in Berrypod.Orders.Order, where: o.id == ^order.id),
        set: [inserted_at: DateTime.add(DateTime.utc_now(), -3, :day)]
      )

      refute Orders.has_recent_paid_order?("old@example.com", 48)
    end

    test "is case-insensitive" do
      order_fixture(%{customer_email: "Buyer@Example.COM", payment_status: "paid"})

      assert Orders.has_recent_paid_order?("buyer@example.com")
    end
  end

  # ---------------------------------------------------------------------------
  # Email suppression
  # ---------------------------------------------------------------------------

  describe "check_suppression/1" do
    test "returns :ok for a non-suppressed email" do
      assert :ok == Orders.check_suppression("fresh@example.com")
    end

    test "returns :suppressed after adding suppression" do
      Orders.add_suppression("blocked@example.com", "unsubscribed")
      assert :suppressed == Orders.check_suppression("blocked@example.com")
    end

    test "is case-insensitive" do
      Orders.add_suppression("upper@example.com", "unsubscribed")
      assert :suppressed == Orders.check_suppression("UPPER@EXAMPLE.COM")
    end
  end

  describe "add_suppression/2" do
    test "creates a suppression record" do
      {:ok, sup} = Orders.add_suppression("new@example.com", "unsubscribed")
      assert sup.email == "new@example.com"
      assert sup.reason == "unsubscribed"
    end

    test "is idempotent — second insert is a no-op" do
      {:ok, _} = Orders.add_suppression("once@example.com", "unsubscribed")
      {:ok, _} = Orders.add_suppression("once@example.com", "unsubscribed")

      assert :suppressed == Orders.check_suppression("once@example.com")
    end

    test "normalises email to lowercase" do
      Orders.add_suppression("Mixed@Example.COM", "test")
      assert :suppressed == Orders.check_suppression("mixed@example.com")
    end
  end
end
