defmodule Plausible.Workers.AcceptTrafficUntilTest do
  use Plausible.DataCase, async: true
  use Bamboo.Test

  alias Plausible.Workers.AcceptTrafficUntil

  @moduletag :ee_only

  test "does not send any notifications when sites have no stats" do
    today = Date.utc_today()
    next_week = today |> Date.add(+7)
    tomorrow = today |> Date.add(+1)

    user1 = insert(:user, accept_traffic_until: next_week)

    user2 = insert(:user, accept_traffic_until: tomorrow)

    _site1 = insert(:site, members: [user1])

    _site2 = insert(:site, members: [user2])

    {:ok, 2} = AcceptTrafficUntil.perform(nil)

    refute_notifications(user1.email)
    refute_notifications(user2.email)
  end

  test "does not send any notifications when site has stats older than 2d" do
    today = Date.utc_today()
    next_week = today |> Date.add(+7)

    user = insert(:user, accept_traffic_until: next_week)

    :site
    |> insert(members: [user])
    |> populate_stats([build(:pageview, timestamp: Date.add(today, -3))])

    {:ok, 1} = AcceptTrafficUntil.perform(nil)

    refute_notifications(user.email)
  end

  test "does send notification when last stat is 2d old" do
    today = Date.utc_today()
    next_week = today |> Date.add(+7)

    user =
      insert(:user, accept_traffic_until: next_week)

    :site
    |> insert(members: [user])
    |> populate_stats([build(:pageview, timestamp: Date.add(today, -2))])

    {:ok, 1} = AcceptTrafficUntil.perform(nil)

    assert_weekly_notification(user.email)
  end

  test "tomorrow: sends one e-mail" do
    tomorrow = Date.utc_today() |> Date.add(+1)
    user = insert(:user, accept_traffic_until: tomorrow)

    :site |> insert(members: [user]) |> populate_stats([build(:pageview)])

    {:ok, 1} = AcceptTrafficUntil.perform(nil)
    assert_final_notification(user.email)
  end

  test "next week: sends one e-mail" do
    next_week = Date.utc_today() |> Date.add(+7)
    user = insert(:user, accept_traffic_until: next_week)

    :site |> insert(members: [user]) |> populate_stats([build(:pageview)])

    {:ok, 1} = AcceptTrafficUntil.perform(nil)
    assert_weekly_notification(user.email)
  end

  test "sends combined warnings in one shot" do
    today = Date.utc_today()
    next_week = today |> Date.add(+7)
    tomorrow = today |> Date.add(+1)
    in_8_days = today |> Date.add(+8)

    user1 = insert(:user, accept_traffic_until: next_week)
    user2 = insert(:user, accept_traffic_until: tomorrow)
    user3 = insert(:user, accept_traffic_until: in_8_days, email: "nope@example.com")
    user4 = insert(:user, accept_traffic_until: today, email: "nope2@example.com")

    :site |> insert(members: [user1]) |> populate_stats([build(:pageview)])
    :site |> insert(members: [user2]) |> populate_stats([build(:pageview)])
    :site |> insert(members: [user3]) |> populate_stats([build(:pageview)])
    :site |> insert(members: [user4]) |> populate_stats([build(:pageview)])

    {:ok, 2} = AcceptTrafficUntil.perform(nil)
    assert_weekly_notification(user1.email)
    assert_final_notification(user2.email)
    refute_notifications("nope@example.com")
    refute_notifications("nope2@example.com")
  end

  test "sends multiple notifications per recipient as the time passes" do
    today = Date.utc_today()
    email = "cycle@example.com"
    user = insert(:user, email: email) |> Plausible.Auth.User.start_trial() |> Repo.update!()
    site = insert(:site, members: [user])

    populate_stats(site, [
      build(:pageview, timestamp: today),
      build(:pageview, timestamp: Date.add(user.trial_expiry_date, 7)),
      build(:pageview, timestamp: Date.add(user.trial_expiry_date, 8)),
      build(:pageview, timestamp: Date.add(user.trial_expiry_date, 13))
    ])

    # today's worker is no-op
    {:ok, 0} = AcceptTrafficUntil.perform(nil, today)
    refute_notifications(email)

    # trial_expiry + 7 days worker => we'll stop counting next week
    {:ok, 1} = AcceptTrafficUntil.perform(nil, Date.add(user.trial_expiry_date, 7))
    assert_weekly_notification(email)

    # another call on the same day == no-op, e-mail already sent
    {:ok, 0} = AcceptTrafficUntil.perform(nil, Date.add(user.trial_expiry_date, 7))
    refute_notifications(email)

    # another call on another day == no-op, no notification type applies
    {:ok, 0} = AcceptTrafficUntil.perform(nil, Date.add(user.trial_expiry_date, 8))
    refute_notifications(email)

    # day before accept_stats_until (trial_expiry_date + 14 - 1)
    {:ok, 1} = AcceptTrafficUntil.perform(nil, Date.add(user.trial_expiry_date, 13))
    assert_final_notification(email)

    # another one on the day before accept_stats_until (trial_expiry_date + 14 - 1) == no-op
    {:ok, 0} = AcceptTrafficUntil.perform(nil, Date.add(user.trial_expiry_date, 13))
    refute_notifications(email)
  end

  defp assert_weekly_notification(email) when is_binary(email) do
    assert_email_delivered_with(
      html_body: ~r/Hey Jane,/,
      to: [nil: email],
      subject:
        PlausibleWeb.Email.approaching_accept_traffic_until(%{name: "", email: email}).subject
    )
  end

  defp assert_final_notification(email) when is_binary(email) do
    assert_email_delivered_with(
      html_body: ~r/Hey Jane,/,
      to: [nil: email],
      subject:
        PlausibleWeb.Email.approaching_accept_traffic_until_tomorrow(%{name: "", email: email}).subject
    )
  end

  defp refute_notifications(email) when is_binary(email) do
    refute_email_delivered_with(to: [nil: email])
  end
end
