defmodule Mv.MembershipFees.MembershipFeeCycleTest do
  @moduledoc """
  Tests for MembershipFeeCycle resource, focusing on status management actions.
  """
  use Mv.DataCase, async: true

  alias Mv.MembershipFees.MembershipFeeCycle
  alias Mv.MembershipFees.MembershipFeeType

  setup do
    system_actor = Mv.Helpers.SystemActor.get_system_actor()
    %{actor: system_actor}
  end

  # Helper to create a membership fee type
  defp create_fee_type(attrs, actor) do
    default_attrs = %{
      name: "Test Fee Type #{System.unique_integer([:positive])}",
      amount: Decimal.new("50.00"),
      interval: :yearly
    }

    attrs = Map.merge(default_attrs, attrs)

    MembershipFeeType
    |> Ash.Changeset.for_create(:create, attrs)
    |> Ash.create!(actor: actor)
  end

  # Helper to create a member
  defp create_member(attrs, actor) do
    default_attrs = %{
      first_name: "Test",
      last_name: "Member",
      email: "test.member.#{System.unique_integer([:positive])}@example.com"
    }

    attrs = Map.merge(default_attrs, attrs)
    {:ok, member} = Mv.Membership.create_member(attrs, actor: actor)
    member
  end

  # Helper to create a cycle
  defp create_cycle(member, fee_type, attrs, actor) do
    default_attrs = %{
      cycle_start: ~D[2024-01-01],
      amount: Decimal.new("50.00"),
      member_id: member.id,
      membership_fee_type_id: fee_type.id
    }

    attrs = Map.merge(default_attrs, attrs)

    MembershipFeeCycle
    |> Ash.Changeset.for_create(:create, attrs)
    |> Ash.create!(actor: actor)
  end

  describe "status defaults" do
    test "status defaults to :unpaid when creating a cycle", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)

      cycle =
        MembershipFeeCycle
        |> Ash.Changeset.for_create(:create, %{
          cycle_start: ~D[2024-01-01],
          amount: Decimal.new("50.00"),
          member_id: member.id,
          membership_fee_type_id: fee_type.id
        })
        |> Ash.create!(actor: actor)

      assert cycle.status == :unpaid
    end
  end

  describe "mark_as_paid" do
    test "sets status to :paid", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)
      cycle = create_cycle(member, fee_type, %{status: :unpaid}, actor)

      assert {:ok, updated} = Ash.update(cycle, %{}, actor: actor, action: :mark_as_paid)
      assert updated.status == :paid
    end

    test "can set notes when marking as paid", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)
      cycle = create_cycle(member, fee_type, %{status: :unpaid}, actor)

      assert {:ok, updated} =
               Ash.update(cycle, %{notes: "Payment received via bank transfer"},
                 actor: actor,
                 action: :mark_as_paid
               )

      assert updated.status == :paid
      assert updated.notes == "Payment received via bank transfer"
    end

    test "can change from suspended to paid", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)
      cycle = create_cycle(member, fee_type, %{status: :suspended}, actor)

      assert {:ok, updated} = Ash.update(cycle, %{}, actor: actor, action: :mark_as_paid)
      assert updated.status == :paid
    end
  end

  describe "mark_as_suspended" do
    test "sets status to :suspended", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)
      cycle = create_cycle(member, fee_type, %{status: :unpaid}, actor)

      assert {:ok, updated} = Ash.update(cycle, %{}, actor: actor, action: :mark_as_suspended)
      assert updated.status == :suspended
    end

    test "can set notes when marking as suspended", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)
      cycle = create_cycle(member, fee_type, %{status: :unpaid}, actor)

      assert {:ok, updated} =
               Ash.update(cycle, %{notes: "Waived due to special circumstances"},
                 actor: actor,
                 action: :mark_as_suspended
               )

      assert updated.status == :suspended
      assert updated.notes == "Waived due to special circumstances"
    end

    test "can change from paid to suspended", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)
      cycle = create_cycle(member, fee_type, %{status: :paid}, actor)

      assert {:ok, updated} = Ash.update(cycle, %{}, actor: actor, action: :mark_as_suspended)
      assert updated.status == :suspended
    end
  end

  describe "mark_as_unpaid" do
    test "sets status to :unpaid", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)
      cycle = create_cycle(member, fee_type, %{status: :paid}, actor)

      assert {:ok, updated} = Ash.update(cycle, %{}, actor: actor, action: :mark_as_unpaid)
      assert updated.status == :unpaid
    end

    test "can set notes when marking as unpaid", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)
      cycle = create_cycle(member, fee_type, %{status: :paid}, actor)

      assert {:ok, updated} =
               Ash.update(cycle, %{notes: "Payment was reversed"},
                 actor: actor,
                 action: :mark_as_unpaid
               )

      assert updated.status == :unpaid
      assert updated.notes == "Payment was reversed"
    end

    test "can change from suspended to unpaid", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)
      cycle = create_cycle(member, fee_type, %{status: :suspended}, actor)

      assert {:ok, updated} = Ash.update(cycle, %{}, actor: actor, action: :mark_as_unpaid)
      assert updated.status == :unpaid
    end
  end

  describe "status transitions" do
    test "all status transitions are allowed", %{actor: actor} do
      fee_type = create_fee_type(%{interval: :yearly}, actor)
      member = create_member(%{membership_fee_type_id: fee_type.id}, actor)

      # unpaid -> paid
      cycle1 = create_cycle(member, fee_type, %{status: :unpaid}, actor)
      assert {:ok, c1} = Ash.update(cycle1, %{}, actor: actor, action: :mark_as_paid)
      assert c1.status == :paid

      # paid -> suspended
      assert {:ok, c2} = Ash.update(c1, %{}, actor: actor, action: :mark_as_suspended)
      assert c2.status == :suspended

      # suspended -> unpaid
      assert {:ok, c3} = Ash.update(c2, %{}, actor: actor, action: :mark_as_unpaid)
      assert c3.status == :unpaid

      # unpaid -> suspended
      assert {:ok, c4} = Ash.update(c3, %{}, actor: actor, action: :mark_as_suspended)
      assert c4.status == :suspended

      # suspended -> paid
      assert {:ok, c5} = Ash.update(c4, %{}, actor: actor, action: :mark_as_paid)
      assert c5.status == :paid

      # paid -> unpaid
      assert {:ok, c6} = Ash.update(c5, %{}, actor: actor, action: :mark_as_unpaid)
      assert c6.status == :unpaid
    end
  end
end
