defmodule MvWeb.Helpers.MembershipFeeHelpersTest do
  @moduledoc """
  Tests for MembershipFeeHelpers module.
  """
  use Mv.DataCase, async: true

  require Ash.Query

  alias MvWeb.Helpers.MembershipFeeHelpers
  alias Mv.MembershipFees.CalendarCycles

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

  describe "format_currency/1" do
    test "formats decimal amount correctly" do
      assert MembershipFeeHelpers.format_currency(Decimal.new("60.00")) == "60,00 €"
      assert MembershipFeeHelpers.format_currency(Decimal.new("5.5")) == "5,50 €"
      assert MembershipFeeHelpers.format_currency(Decimal.new("100")) == "100,00 €"
      assert MembershipFeeHelpers.format_currency(Decimal.new("0.99")) == "0,99 €"
    end
  end

  describe "format_interval/1" do
    test "formats all interval types correctly" do
      assert MembershipFeeHelpers.format_interval(:monthly) == "Monthly"
      assert MembershipFeeHelpers.format_interval(:quarterly) == "Quarterly"
      assert MembershipFeeHelpers.format_interval(:half_yearly) == "Half-yearly"
      assert MembershipFeeHelpers.format_interval(:yearly) == "Yearly"
    end
  end

  describe "format_cycle_range/2" do
    test "formats yearly cycle range correctly" do
      cycle_start = ~D[2024-01-01]
      interval = :yearly
      _cycle_end = CalendarCycles.calculate_cycle_end(cycle_start, interval)

      result = MembershipFeeHelpers.format_cycle_range(cycle_start, interval)
      assert result =~ "2024"
      assert result =~ "01.01"
      assert result =~ "31.12"
    end

    test "formats quarterly cycle range correctly" do
      cycle_start = ~D[2024-01-01]
      interval = :quarterly
      _cycle_end = CalendarCycles.calculate_cycle_end(cycle_start, interval)

      result = MembershipFeeHelpers.format_cycle_range(cycle_start, interval)
      assert result =~ "2024"
      assert result =~ "01.01"
      assert result =~ "31.03"
    end

    test "formats monthly cycle range correctly" do
      cycle_start = ~D[2024-03-01]
      interval = :monthly
      _cycle_end = CalendarCycles.calculate_cycle_end(cycle_start, interval)

      result = MembershipFeeHelpers.format_cycle_range(cycle_start, interval)
      assert result =~ "2024"
      assert result =~ "01.03"
      assert result =~ "31.03"
    end
  end

  describe "get_last_completed_cycle/2" do
    test "returns nil when member is nil" do
      assert MembershipFeeHelpers.get_last_completed_cycle(nil) == nil
      assert MembershipFeeHelpers.get_last_completed_cycle(nil, Date.utc_today()) == nil
    end

    test "returns last completed cycle for member", %{actor: actor} do
      # Create test data
      fee_type =
        Mv.MembershipFees.MembershipFeeType
        |> Ash.Changeset.for_create(:create, %{
          name: "Test Type",
          amount: Decimal.new("50.00"),
          interval: :yearly
        })
        |> Ash.create!(actor: actor)

      # Create member without fee type first to avoid auto-generation
      {:ok, member} =
        Mv.Membership.create_member(
          %{
            first_name: "Test",
            last_name: "Member",
            email: "test#{System.unique_integer([:positive])}@example.com",
            join_date: ~D[2022-01-01]
          },
          actor: actor
        )

      # Assign fee type after member creation (this may generate cycles, but we'll create our own)
      {:ok, member} =
        Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)

      # Delete any auto-generated cycles first
      cycles =
        Mv.MembershipFees.MembershipFeeCycle
        |> Ash.Query.filter(member_id == ^member.id)
        |> Ash.read!(actor: actor)

      Enum.each(cycles, fn cycle -> Ash.destroy!(cycle, actor: actor) end)

      # Create cycles manually
      _cycle_2022 =
        Mv.MembershipFees.MembershipFeeCycle
        |> Ash.Changeset.for_create(:create, %{
          cycle_start: ~D[2022-01-01],
          amount: Decimal.new("50.00"),
          member_id: member.id,
          membership_fee_type_id: fee_type.id,
          status: :paid
        })
        |> Ash.create!(actor: actor)

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

      # Load cycles with membership_fee_type relationship
      member =
        member
        |> Ash.load!([membership_fee_cycles: [:membership_fee_type]], actor: actor)
        |> Ash.load!(:membership_fee_type, actor: actor)

      # Use a fixed date in 2024 to ensure 2023 is last completed
      today = ~D[2024-06-15]
      last_cycle = MembershipFeeHelpers.get_last_completed_cycle(member, today)

      assert last_cycle.id == cycle_2023.id
    end

    test "returns nil if no cycles exist", %{actor: actor} do
      fee_type =
        Mv.MembershipFees.MembershipFeeType
        |> Ash.Changeset.for_create(:create, %{
          name: "Test Type",
          amount: Decimal.new("50.00"),
          interval: :yearly
        })
        |> Ash.create!(actor: actor)

      # Create member without fee type first
      {:ok, member} =
        Mv.Membership.create_member(
          %{
            first_name: "Test",
            last_name: "Member",
            email: "test#{System.unique_integer([:positive])}@example.com"
          },
          actor: actor
        )

      # Assign fee type
      {:ok, member} =
        Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)

      # Delete any auto-generated cycles
      cycles =
        Mv.MembershipFees.MembershipFeeCycle
        |> Ash.Query.filter(member_id == ^member.id)
        |> Ash.read!(actor: actor)

      Enum.each(cycles, fn cycle -> Ash.destroy!(cycle, actor: actor) end)

      # Load cycles and fee type (will be empty)
      member =
        member
        |> Ash.load!([membership_fee_cycles: [:membership_fee_type]], actor: actor)
        |> Ash.load!(:membership_fee_type, actor: actor)

      last_cycle = MembershipFeeHelpers.get_last_completed_cycle(member, Date.utc_today())
      assert last_cycle == nil
    end
  end

  describe "get_current_cycle/2" do
    test "returns nil when member is nil" do
      assert MembershipFeeHelpers.get_current_cycle(nil) == nil
      assert MembershipFeeHelpers.get_current_cycle(nil, Date.utc_today()) == nil
    end

    test "returns current cycle for member", %{actor: actor} do
      fee_type =
        Mv.MembershipFees.MembershipFeeType
        |> Ash.Changeset.for_create(:create, %{
          name: "Test Type",
          amount: Decimal.new("50.00"),
          interval: :yearly
        })
        |> Ash.create!(actor: actor)

      # Create member without fee type first
      {:ok, member} =
        Mv.Membership.create_member(
          %{
            first_name: "Test",
            last_name: "Member",
            email: "test#{System.unique_integer([:positive])}@example.com",
            join_date: ~D[2023-01-01]
          },
          actor: actor
        )

      # Assign fee type
      {:ok, member} =
        Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)

      # Delete any auto-generated cycles
      cycles =
        Mv.MembershipFees.MembershipFeeCycle
        |> Ash.Query.filter(member_id == ^member.id)
        |> Ash.read!(actor: actor)

      Enum.each(cycles, fn cycle -> Ash.destroy!(cycle, actor: actor) end)

      today = Date.utc_today()
      current_year_start = %{today | month: 1, day: 1}

      current_cycle =
        Mv.MembershipFees.MembershipFeeCycle
        |> Ash.Changeset.for_create(:create, %{
          cycle_start: current_year_start,
          amount: Decimal.new("50.00"),
          member_id: member.id,
          membership_fee_type_id: fee_type.id,
          status: :unpaid
        })
        |> Ash.create!(actor: actor)

      # Load cycles with membership_fee_type relationship
      member =
        member
        |> Ash.load!([membership_fee_cycles: [:membership_fee_type]], actor: actor)
        |> Ash.load!(:membership_fee_type, actor: actor)

      result = MembershipFeeHelpers.get_current_cycle(member, today)

      assert result.id == current_cycle.id
    end
  end

  describe "status_color/1" do
    test "returns correct color classes for statuses" do
      assert MembershipFeeHelpers.status_color(:paid) == "badge-success"
      assert MembershipFeeHelpers.status_color(:unpaid) == "badge-error"
      assert MembershipFeeHelpers.status_color(:suspended) == "badge-ghost"
    end
  end

  describe "status_icon/1" do
    test "returns correct icon names for statuses" do
      assert MembershipFeeHelpers.status_icon(:paid) == "hero-check-circle"
      assert MembershipFeeHelpers.status_icon(:unpaid) == "hero-x-circle"
      assert MembershipFeeHelpers.status_icon(:suspended) == "hero-pause-circle"
    end
  end
end
