defmodule Plausible.Billing.QuotaTest do
  use Plausible.DataCase, async: true
  use Plausible
  alias Plausible.Billing.{Quota, Plans}
  alias Plausible.Billing.Feature.{Goals, Props, StatsAPI}

  on_ee do
    alias Plausible.Billing.Feature.Funnels
    alias Plausible.Billing.Feature.RevenueGoals
  end

  @legacy_plan_id "558746"
  @v1_plan_id "558018"
  @v2_plan_id "654177"
  @v3_plan_id "749342"
  @v3_business_plan_id "857481"
  @v4_1m_plan_id "857101"

  describe "site_limit/1" do
    @describetag :ee_only

    test "returns 50 when user is on an old plan" do
      user_on_v1 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))
      user_on_v2 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id))
      user_on_v3 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_plan_id))

      assert 50 == Quota.site_limit(user_on_v1)
      assert 50 == Quota.site_limit(user_on_v2)
      assert 50 == Quota.site_limit(user_on_v3)
    end

    test "returns 50 when user is on free_10k plan" do
      user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k"))
      assert 50 == Quota.site_limit(user)
    end

    test "returns the configured site limit for enterprise plan" do
      user = insert(:user)

      enterprise_plan = insert(:enterprise_plan, user_id: user.id, site_limit: 500)
      insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)

      assert enterprise_plan.site_limit == Quota.site_limit(user)
    end

    test "returns 10 when user in on trial" do
      user =
        insert(:user,
          trial_expiry_date: Timex.shift(Timex.now(), days: 7)
        )

      assert 10 == Quota.site_limit(user)
    end

    test "returns the subscription limit for enterprise users who have not paid yet" do
      user =
        insert(:user,
          enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
          subscription: build(:subscription, paddle_plan_id: @v1_plan_id)
        )

      assert 50 == Quota.site_limit(user)
    end

    test "returns 10 for enterprise users who have not upgraded yet and are on trial" do
      user =
        insert(:user,
          enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
          subscription: nil
        )

      assert 10 == Quota.site_limit(user)
    end
  end

  test "site_usage/1 returns the amount of sites the user owns" do
    user = insert(:user)
    insert_list(3, :site, memberships: [build(:site_membership, user: user, role: :owner)])
    insert(:site, memberships: [build(:site_membership, user: user, role: :admin)])
    insert(:site, memberships: [build(:site_membership, user: user, role: :viewer)])

    assert Quota.site_usage(user) == 3
  end

  describe "below_limit?/2" do
    test "returns true when quota is not exceeded" do
      assert Quota.below_limit?(3, 5)
    end

    test "returns true when limit is :unlimited" do
      assert Quota.below_limit?(10_000, :unlimited)
    end

    test "returns false when usage is at limit" do
      refute Quota.below_limit?(3, 3)
    end

    test "returns false when usage exceeds the limit" do
      refute Quota.below_limit?(10, 3)
    end
  end

  describe "ensure_within_plan_limits/2" do
    test "returns :ok when site and team member limits are reached but not exceeded" do
      usage = %{
        monthly_pageviews: %{last_30_days: %{total: 1}},
        team_members: 3,
        sites: 10
      }

      plan = Plans.find(@v4_1m_plan_id)

      assert Quota.ensure_within_plan_limits(usage, plan) == :ok
    end

    test "returns all exceeded limits" do
      usage = %{
        monthly_pageviews: %{last_30_days: %{total: 1_150_001}},
        team_members: 4,
        sites: 11
      }

      plan = Plans.find(@v4_1m_plan_id)

      {:error, {:over_plan_limits, exceeded_limits}} =
        Quota.ensure_within_plan_limits(usage, plan)

      assert :monthly_pageview_limit in exceeded_limits
      assert :team_member_limit in exceeded_limits
      assert :site_limit in exceeded_limits
    end

    test "can skip checking the pageview limit" do
      usage = %{
        monthly_pageviews: %{last_30_days: %{total: 1_150_001}},
        team_members: 2,
        sites: 8
      }

      plan = Plans.find(@v4_1m_plan_id)

      assert :ok = Quota.ensure_within_plan_limits(usage, plan, ignore_pageview_limit: true)
    end

    test "by the last 30 days usage, pageview limit is exceeded when more than 10% over the limit" do
      usage_within_pageview_limit = %{
        monthly_pageviews: %{last_30_days: %{total: 1_100_000}},
        team_members: 1,
        sites: 1
      }

      usage_over_pageview_limit = %{
        monthly_pageviews: %{last_30_days: %{total: 1_100_001}},
        team_members: 1,
        sites: 1
      }

      plan = Plans.find(@v4_1m_plan_id)

      assert Quota.ensure_within_plan_limits(usage_within_pageview_limit, plan) == :ok

      assert Quota.ensure_within_plan_limits(usage_over_pageview_limit, plan) ==
               {:error, {:over_plan_limits, [:monthly_pageview_limit]}}
    end

    test "by billing cycles usage, pageview limit is exceeded when last two billing cycles exceed by 10%" do
      usage_within_pageview_limit = %{
        monthly_pageviews: %{penultimate_cycle: %{total: 11_000}, last_cycle: %{total: 10_999}},
        team_members: 1,
        sites: 1
      }

      usage_over_pageview_limit = %{
        monthly_pageviews: %{penultimate_cycle: %{total: 11_000}, last_cycle: %{total: 11_000}},
        team_members: 1,
        sites: 1
      }

      plan = Plans.find(@v3_plan_id)

      assert Quota.ensure_within_plan_limits(usage_within_pageview_limit, plan) == :ok

      assert Quota.ensure_within_plan_limits(usage_over_pageview_limit, plan) ==
               {:error, {:over_plan_limits, [:monthly_pageview_limit]}}
    end

    test "returns error with exceeded limits for enterprise plans" do
      user = insert(:user)

      usage = %{
        monthly_pageviews: %{penultimate_cycle: %{total: 1}, last_cycle: %{total: 1}},
        team_members: 1,
        sites: 2
      }

      enterprise_plan =
        insert(:enterprise_plan,
          user: user,
          paddle_plan_id: "whatever",
          site_limit: 1
        )

      assert Quota.ensure_within_plan_limits(usage, enterprise_plan) ==
               {:error, {:over_plan_limits, [:site_limit]}}
    end
  end

  describe "monthly_pageview_limit/1" do
    test "is based on the plan if user is on a legacy plan" do
      user = insert(:user, subscription: build(:subscription, paddle_plan_id: @legacy_plan_id))

      assert Quota.monthly_pageview_limit(user.subscription) == 1_000_000
    end

    test "is based on the plan if user is on a standard plan" do
      user = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))

      assert Quota.monthly_pageview_limit(user.subscription) == 10_000
    end

    test "free_10k has 10k monthly_pageview_limit" do
      user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k"))

      assert Quota.monthly_pageview_limit(user.subscription) == 10_000
    end

    test "is based on the enterprise plan if user is on an enterprise plan" do
      user = insert(:user)

      enterprise_plan =
        insert(:enterprise_plan, user_id: user.id, monthly_pageview_limit: 100_000)

      subscription =
        insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)

      assert Quota.monthly_pageview_limit(subscription) == 100_000
    end

    test "does not limit pageviews when user has a pending enterprise plan" do
      user = insert(:user)
      subscription = insert(:subscription, user_id: user.id, paddle_plan_id: "pending-enterprise")

      assert Quota.monthly_pageview_limit(subscription) == :unlimited
    end
  end

  describe "team_member_usage/1" do
    test "returns the number of members in all of the sites the user owns" do
      me = insert(:user)

      _site_i_own_1 =
        insert(:site,
          memberships: [
            build(:site_membership, user: me, role: :owner),
            build(:site_membership, user: build(:user), role: :viewer)
          ]
        )

      _site_i_own_2 =
        insert(:site,
          memberships: [
            build(:site_membership, user: me, role: :owner),
            build(:site_membership, user: build(:user), role: :admin),
            build(:site_membership, user: build(:user), role: :viewer)
          ]
        )

      _site_i_own_3 =
        insert(:site,
          memberships: [
            build(:site_membership, user: me, role: :owner)
          ]
        )

      _site_i_have_access =
        insert(:site,
          memberships: [
            build(:site_membership, user: me, role: :viewer),
            build(:site_membership, user: build(:user), role: :viewer),
            build(:site_membership, user: build(:user), role: :viewer),
            build(:site_membership, user: build(:user), role: :viewer)
          ]
        )

      assert Quota.team_member_usage(me) == 3
    end

    test "counts the same email address as one team member" do
      me = insert(:user)
      joe = insert(:user, email: "joe@plausible.test")

      _site_i_own_1 =
        insert(:site,
          memberships: [
            build(:site_membership, user: me, role: :owner),
            build(:site_membership, user: joe, role: :viewer)
          ]
        )

      _site_i_own_2 =
        insert(:site,
          memberships: [
            build(:site_membership, user: me, role: :owner),
            build(:site_membership, user: build(:user), role: :admin),
            build(:site_membership, user: joe, role: :viewer)
          ]
        )

      site_i_own_3 = insert(:site, memberships: [build(:site_membership, user: me, role: :owner)])

      insert(:invitation, site: site_i_own_3, inviter: me, email: "joe@plausible.test")

      assert Quota.team_member_usage(me) == 2
    end

    test "counts pending invitations as team members" do
      me = insert(:user)
      member = insert(:user)

      site_i_own =
        insert(:site,
          memberships: [
            build(:site_membership, user: me, role: :owner),
            build(:site_membership, user: member, role: :admin)
          ]
        )

      site_i_have_access =
        insert(:site, memberships: [build(:site_membership, user: me, role: :admin)])

      insert(:invitation, site: site_i_own, inviter: me)
      insert(:invitation, site: site_i_own, inviter: member)
      insert(:invitation, site: site_i_have_access, inviter: me)

      assert Quota.team_member_usage(me) == 3
    end

    test "does not count ownership transfer as a team member" do
      me = insert(:user)
      site_i_own = insert(:site, memberships: [build(:site_membership, user: me, role: :owner)])

      insert(:invitation, site: site_i_own, inviter: me, role: :owner)

      assert Quota.team_member_usage(me) == 0
    end

    test "returns zero when user does not have any site" do
      me = insert(:user)
      assert Quota.team_member_usage(me) == 0
    end

    test "does not count email report recipients as team members" do
      me = insert(:user)
      site = insert(:site, memberships: [build(:site_membership, user: me, role: :owner)])

      insert(:weekly_report,
        site: site,
        recipients: ["adam@plausible.test", "vini@plausible.test"]
      )

      assert Quota.team_member_usage(me) == 0
    end

    test "excludes specific emails from limit calculation" do
      me = insert(:user)
      member = insert(:user)

      site_i_own =
        insert(:site,
          memberships: [
            build(:site_membership, user: me, role: :owner),
            build(:site_membership, user: member, role: :admin)
          ]
        )

      insert(:invitation, site: site_i_own, inviter: me)
      insert(:invitation, site: site_i_own, inviter: member)
      invitation = insert(:invitation, site: site_i_own, inviter: me, email: "foo@example.com")

      assert Quota.team_member_usage(me) == 4
      assert Quota.team_member_usage(me, exclude_emails: "arbitrary@example.com") == 4
      assert Quota.team_member_usage(me, exclude_emails: member.email) == 3
      assert Quota.team_member_usage(me, exclude_emails: invitation.email) == 3
      assert Quota.team_member_usage(me, exclude_emails: [member.email, invitation.email]) == 2
    end
  end

  describe "team_member_limit/1" do
    @describetag :ee_only
    test "returns unlimited when user is on an old plan" do
      user_on_v1 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))
      user_on_v2 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id))
      user_on_v3 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_plan_id))

      assert :unlimited == Quota.team_member_limit(user_on_v1)
      assert :unlimited == Quota.team_member_limit(user_on_v2)
      assert :unlimited == Quota.team_member_limit(user_on_v3)
    end

    test "returns unlimited when user is on free_10k plan" do
      user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k"))
      assert :unlimited == Quota.team_member_limit(user)
    end

    test "returns 5 when user in on trial" do
      user =
        insert(:user,
          trial_expiry_date: Timex.shift(Timex.now(), days: 7)
        )

      assert 3 == Quota.team_member_limit(user)
    end

    test "returns the enterprise plan limit" do
      user =
        insert(:user,
          enterprise_plan:
            build(:enterprise_plan, paddle_plan_id: "123321", team_member_limit: 27),
          subscription: build(:subscription, paddle_plan_id: "123321")
        )

      assert 27 == Quota.team_member_limit(user)
    end

    test "reads from json file when the user is on a v4 plan" do
      user_on_growth = insert(:user, subscription: build(:growth_subscription))

      user_on_business = insert(:user, subscription: build(:business_subscription))

      assert 3 == Quota.team_member_limit(user_on_growth)
      assert 10 == Quota.team_member_limit(user_on_business)
    end

    test "returns unlimited when user is on a v3 business plan" do
      user =
        insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_business_plan_id))

      assert :unlimited == Quota.team_member_limit(user)
    end
  end

  describe "features_usage/1" do
    test "returns an empty list for a user/site who does not use any feature" do
      assert [] == Quota.features_usage(insert(:user))
      assert [] == Quota.features_usage(insert(:site))
    end

    test "returns [Props] when user/site uses custom props" do
      user = insert(:user)

      site =
        insert(:site,
          allowed_event_props: ["dummy"],
          memberships: [build(:site_membership, user: user, role: :owner)]
        )

      assert [Props] == Quota.features_usage(site)
      assert [Props] == Quota.features_usage(user)
    end

    on_ee do
      test "returns [Funnels] when user/site uses funnels" do
        user = insert(:user)
        site = insert(:site, memberships: [build(:site_membership, user: user, role: :owner)])

        goals = insert_list(3, :goal, site: site, event_name: fn -> Ecto.UUID.generate() end)
        steps = Enum.map(goals, &%{"goal_id" => &1.id})
        Plausible.Funnels.create(site, "dummy", steps)

        assert [Funnels] == Quota.features_usage(site)
        assert [Funnels] == Quota.features_usage(user)
      end

      test "returns [RevenueGoals] when user/site uses revenue goals" do
        user = insert(:user)
        site = insert(:site, memberships: [build(:site_membership, user: user, role: :owner)])
        insert(:goal, currency: :USD, site: site, event_name: "Purchase")

        assert [RevenueGoals] == Quota.features_usage(site)
        assert [RevenueGoals] == Quota.features_usage(user)
      end
    end

    test "returns [StatsAPI] when user has a stats api key" do
      user = insert(:user)
      insert(:api_key, user: user)

      assert [StatsAPI] == Quota.features_usage(user)
    end

    on_ee do
      test "returns multiple features" do
        user = insert(:user)

        site =
          insert(:site,
            allowed_event_props: ["dummy"],
            memberships: [build(:site_membership, user: user, role: :owner)]
          )

        insert(:goal, currency: :USD, site: site, event_name: "Purchase")

        goals = insert_list(3, :goal, site: site, event_name: fn -> Ecto.UUID.generate() end)
        steps = Enum.map(goals, &%{"goal_id" => &1.id})
        Plausible.Funnels.create(site, "dummy", steps)

        assert [Props, Funnels, RevenueGoals] == Quota.features_usage(site)
        assert [Props, Funnels, RevenueGoals] == Quota.features_usage(user)
      end
    end

    test "accounts only for sites the user owns" do
      user = insert(:user)

      insert(:site,
        allowed_event_props: ["dummy"],
        memberships: [build(:site_membership, user: user, role: :admin)]
      )

      assert [] == Quota.features_usage(user)
    end
  end

  describe "allowed_features_for/1" do
    on_ee do
      test "users with expired trials have no access to subscription features" do
        user = insert(:user, trial_expiry_date: ~D[2023-01-01])
        assert [Goals] == Quota.allowed_features_for(user)
      end
    end

    test "returns all grandfathered features when user is on an old plan" do
      user_on_v1 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))
      user_on_v2 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id))
      user_on_v3 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_plan_id))

      assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user_on_v1)
      assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user_on_v2)
      assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user_on_v3)
    end

    test "returns [Goals, Props, StatsAPI] when user is on free_10k plan" do
      user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k"))
      assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user)
    end

    on_ee do
      test "returns the enterprise plan features" do
        user = insert(:user)

        enterprise_plan =
          insert(:enterprise_plan,
            user_id: user.id,
            monthly_pageview_limit: 100_000,
            site_limit: 500,
            features: [Plausible.Billing.Feature.StatsAPI, Plausible.Billing.Feature.Funnels]
          )

        _subscription =
          insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)

        assert [Plausible.Billing.Feature.StatsAPI, Plausible.Billing.Feature.Funnels] ==
                 Quota.allowed_features_for(user)
      end
    end

    test "returns all features when user in on trial" do
      user = insert(:user, trial_expiry_date: Timex.shift(Timex.now(), days: 7))

      assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user)
    end

    test "returns previous plan limits for enterprise users who have not paid yet" do
      user =
        insert(:user,
          enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
          subscription: build(:subscription, paddle_plan_id: @v1_plan_id)
        )

      assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user)
    end

    test "returns all features for enterprise users who have not upgraded yet and are on trial" do
      user =
        insert(:user,
          enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
          subscription: nil
        )

      assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user)
    end

    test "returns old plan features for enterprise customers who are due to change a plan" do
      user =
        insert(:user,
          enterprise_plan:
            build(:enterprise_plan,
              paddle_plan_id: "old-paddle-plan-id",
              features: [Plausible.Billing.Feature.StatsAPI]
            ),
          subscription: build(:subscription, paddle_plan_id: "old-paddle-plan-id")
        )

      insert(:enterprise_plan, user_id: user.id, paddle_plan_id: "new-paddle-plan-id")
      assert [Plausible.Billing.Feature.StatsAPI] == Quota.allowed_features_for(user)
    end
  end

  describe "monthly_pageview_usage/2" do
    test "returns empty usage for user without subscription and without any sites" do
      user =
        insert(:user)
        |> Plausible.Users.with_subscription()

      assert %{
               last_30_days: %{
                 total: 0,
                 custom_events: 0,
                 pageviews: 0,
                 date_range: date_range
               }
             } = Quota.monthly_pageview_usage(user)

      assert date_range.last == Date.utc_today()
      assert Date.compare(date_range.first, date_range.last) == :lt
    end

    test "returns usage for user without subscription with a site" do
      user =
        insert(:user)
        |> Plausible.Users.with_subscription()

      site = insert(:site, members: [user])

      now = NaiveDateTime.utc_now()

      populate_stats(site, [
        build(:event, timestamp: Timex.shift(now, days: -40), name: "custom"),
        build(:event, timestamp: Timex.shift(now, days: -10), name: "custom"),
        build(:event, timestamp: Timex.shift(now, days: -9), name: "pageview"),
        build(:event, timestamp: Timex.shift(now, days: -8), name: "pageview"),
        build(:event, timestamp: Timex.shift(now, days: -7), name: "pageview"),
        build(:event, timestamp: Timex.shift(now, days: -6), name: "custom")
      ])

      assert %{
               last_30_days: %{
                 total: 5,
                 custom_events: 2,
                 pageviews: 3,
                 date_range: %{}
               }
             } = Quota.monthly_pageview_usage(user)
    end

    test "returns usage for user with subscription and a site" do
      today = Date.utc_today()

      user =
        insert(:user,
          subscription: build(:subscription, last_bill_date: Timex.shift(today, days: -8))
        )

      site = insert(:site, members: [user])

      now = NaiveDateTime.utc_now()

      populate_stats(site, [
        build(:event, timestamp: Timex.shift(now, days: -40), name: "custom"),
        build(:event, timestamp: Timex.shift(now, days: -10), name: "custom"),
        build(:event, timestamp: Timex.shift(now, days: -9), name: "pageview"),
        build(:event, timestamp: Timex.shift(now, days: -8), name: "pageview"),
        build(:event, timestamp: Timex.shift(now, days: -7), name: "pageview"),
        build(:event, timestamp: Timex.shift(now, days: -6), name: "custom")
      ])

      assert %{
               current_cycle: %{
                 total: 3,
                 custom_events: 1,
                 pageviews: 2,
                 date_range: %{}
               },
               last_cycle: %{
                 total: 2,
                 custom_events: 1,
                 pageviews: 1,
                 date_range: %{}
               },
               penultimate_cycle: %{
                 total: 1,
                 custom_events: 1,
                 pageviews: 0,
                 date_range: %{}
               }
             } = Quota.monthly_pageview_usage(user)
    end

    test "returns usage for only a subset of site IDs" do
      today = Date.utc_today()

      user =
        insert(:user,
          subscription: build(:subscription, last_bill_date: Timex.shift(today, days: -8))
        )

      site1 = insert(:site, members: [user])
      site2 = insert(:site, members: [user])
      site3 = insert(:site, members: [user])

      now = NaiveDateTime.utc_now()

      for site <- [site1, site2, site3] do
        populate_stats(site, [
          build(:event, timestamp: Timex.shift(now, days: -40), name: "custom"),
          build(:event, timestamp: Timex.shift(now, days: -10), name: "custom"),
          build(:event, timestamp: Timex.shift(now, days: -9), name: "pageview"),
          build(:event, timestamp: Timex.shift(now, days: -8), name: "pageview"),
          build(:event, timestamp: Timex.shift(now, days: -7), name: "pageview"),
          build(:event, timestamp: Timex.shift(now, days: -6), name: "custom")
        ])
      end

      assert %{
               current_cycle: %{
                 total: 6,
                 custom_events: 2,
                 pageviews: 4,
                 date_range: %{}
               },
               last_cycle: %{
                 total: 4,
                 custom_events: 2,
                 pageviews: 2,
                 date_range: %{}
               },
               penultimate_cycle: %{
                 total: 2,
                 custom_events: 2,
                 pageviews: 0,
                 date_range: %{}
               }
             } = Quota.monthly_pageview_usage(user, [site1.id, site3.id])
    end
  end

  describe "usage_cycle/1" do
    setup do
      user = insert(:user)
      site = insert(:site, members: [user])

      populate_stats(site, [
        build(:event, timestamp: ~N[2023-04-01 00:00:00], name: "custom"),
        build(:event, timestamp: ~N[2023-04-02 00:00:00], name: "custom"),
        build(:event, timestamp: ~N[2023-04-03 00:00:00], name: "custom"),
        build(:event, timestamp: ~N[2023-04-04 00:00:00], name: "custom"),
        build(:event, timestamp: ~N[2023-04-05 00:00:00], name: "custom"),
        build(:event, timestamp: ~N[2023-05-01 00:00:00], name: "pageview"),
        build(:event, timestamp: ~N[2023-05-02 00:00:00], name: "pageview"),
        build(:event, timestamp: ~N[2023-05-03 00:00:00], name: "pageview"),
        build(:event, timestamp: ~N[2023-05-04 00:00:00], name: "pageview"),
        build(:event, timestamp: ~N[2023-05-05 00:00:00], name: "pageview"),
        build(:event, timestamp: ~N[2023-06-01 00:00:00], name: "custom"),
        build(:event, timestamp: ~N[2023-06-02 00:00:00], name: "custom"),
        build(:event, timestamp: ~N[2023-06-03 00:00:00], name: "custom"),
        build(:event, timestamp: ~N[2023-06-04 00:00:00], name: "custom"),
        build(:event, timestamp: ~N[2023-06-05 00:00:00], name: "custom")
      ])

      {:ok, %{user: user}}
    end

    test "returns usage and date_range for the given billing month", %{user: user} do
      last_bill_date = ~D[2023-06-03]
      today = ~D[2023-06-05]

      insert(:subscription, user_id: user.id, last_bill_date: last_bill_date)

      assert %{date_range: penultimate_cycle, pageviews: 2, custom_events: 3, total: 5} =
               Quota.usage_cycle(user, :penultimate_cycle, nil, today)

      assert %{date_range: last_cycle, pageviews: 3, custom_events: 2, total: 5} =
               Quota.usage_cycle(user, :last_cycle, nil, today)

      assert %{date_range: current_cycle, pageviews: 0, custom_events: 3, total: 3} =
               Quota.usage_cycle(user, :current_cycle, nil, today)

      assert penultimate_cycle == Date.range(~D[2023-04-03], ~D[2023-05-02])
      assert last_cycle == Date.range(~D[2023-05-03], ~D[2023-06-02])
      assert current_cycle == Date.range(~D[2023-06-03], ~D[2023-07-02])
    end

    test "returns usage and date_range for the last 30 days", %{user: user} do
      today = ~D[2023-06-01]

      assert %{date_range: last_30_days, pageviews: 4, custom_events: 1, total: 5} =
               Quota.usage_cycle(user, :last_30_days, nil, today)

      assert last_30_days == Date.range(~D[2023-05-02], ~D[2023-06-01])
    end

    test "only considers sites that the user owns", %{user: user} do
      different_site =
        insert(:site,
          memberships: [
            build(:site_membership, user: user, role: :admin)
          ]
        )

      populate_stats(different_site, [
        build(:event, timestamp: ~N[2023-05-05 00:00:00], name: "custom")
      ])

      last_bill_date = ~D[2023-06-03]
      today = ~D[2023-06-05]

      insert(:subscription, user_id: user.id, last_bill_date: last_bill_date)

      assert %{date_range: last_cycle, pageviews: 3, custom_events: 2, total: 5} =
               Quota.usage_cycle(user, :last_cycle, nil, today)

      assert last_cycle == Date.range(~D[2023-05-03], ~D[2023-06-02])
    end

    test "in case of yearly billing, cycles are normalized as if they were paying monthly" do
      last_bill_date = ~D[2020-09-01]
      today = ~D[2021-02-02]

      user = insert(:user, subscription: build(:subscription, last_bill_date: last_bill_date))

      assert %{date_range: penultimate_cycle} =
               Quota.usage_cycle(user, :penultimate_cycle, nil, today)

      assert %{date_range: last_cycle} =
               Quota.usage_cycle(user, :last_cycle, nil, today)

      assert %{date_range: current_cycle} =
               Quota.usage_cycle(user, :current_cycle, nil, today)

      assert penultimate_cycle == Date.range(~D[2020-12-01], ~D[2020-12-31])
      assert last_cycle == Date.range(~D[2021-01-01], ~D[2021-01-31])
      assert current_cycle == Date.range(~D[2021-02-01], ~D[2021-02-28])
    end

    test "returns correct billing months when last_bill_date is the first day of the year" do
      last_bill_date = ~D[2021-01-01]
      today = ~D[2021-01-02]

      user = insert(:user, subscription: build(:subscription, last_bill_date: last_bill_date))

      assert %{date_range: penultimate_cycle, total: 0} =
               Quota.usage_cycle(user, :penultimate_cycle, nil, today)

      assert %{date_range: last_cycle, total: 0} =
               Quota.usage_cycle(user, :last_cycle, nil, today)

      assert %{date_range: current_cycle, total: 0} =
               Quota.usage_cycle(user, :current_cycle, nil, today)

      assert penultimate_cycle == Date.range(~D[2020-11-01], ~D[2020-11-30])
      assert last_cycle == Date.range(~D[2020-12-01], ~D[2020-12-31])
      assert current_cycle == Date.range(~D[2021-01-01], ~D[2021-01-31])
    end
  end
end
