defmodule MvWeb.MemberLive.ShowMembershipFeesTest do
  @moduledoc """
  Tests for membership fees section in member detail view.
  """
  use MvWeb.ConnCase, async: false

  import Phoenix.LiveViewTest

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

  require Ash.Query

  # Helper to create a membership fee type
  defp create_fee_type(attrs) do
    system_actor = Mv.Helpers.SystemActor.get_system_actor()

    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: system_actor)
  end

  # Helper to create a cycle
  defp create_cycle(member, fee_type, attrs) do
    system_actor = Mv.Helpers.SystemActor.get_system_actor()

    # Delete any auto-generated cycles first to avoid conflicts
    existing_cycles =
      MembershipFeeCycle
      |> Ash.Query.filter(member_id == ^member.id)
      |> Ash.read!(actor: system_actor)

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

    default_attrs = %{
      cycle_start: ~D[2023-01-01],
      amount: Decimal.new("50.00"),
      member_id: member.id,
      membership_fee_type_id: fee_type.id,
      status: :unpaid
    }

    attrs = Map.merge(default_attrs, attrs)

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

  describe "cycles table display" do
    test "displays all cycles for member", %{conn: conn} do
      fee_type = create_fee_type(%{interval: :yearly})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})

      _cycle1 = create_cycle(member, fee_type, %{cycle_start: ~D[2022-01-01], status: :paid})
      _cycle2 = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})

      {:ok, view, _html} = live(conn, "/members/#{member.id}")

      # Switch to membership fees tab
      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      html = render(view)

      # Should show cycles table
      assert html =~ "Membership Fees" || html =~ "Mitgliedsbeiträge"
      # Check for formatted cycle dates (e.g., "01.01.2022" or "2022")
      assert html =~ "2022" || html =~ "2023" || html =~ "01.01.2022" || html =~ "01.01.2023"
    end

    test "table columns show correct data", %{conn: conn} do
      fee_type = create_fee_type(%{interval: :yearly, amount: Decimal.new("60.00")})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})

      create_cycle(member, fee_type, %{
        cycle_start: ~D[2023-01-01],
        amount: Decimal.new("60.00"),
        status: :paid
      })

      {:ok, view, _html} = live(conn, "/members/#{member.id}")

      # Switch to membership fees tab
      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      html = render(view)

      # Should show interval, amount, status
      assert html =~ "Yearly" || html =~ "Jährlich"
      assert html =~ "60" || html =~ "60,00"
      assert html =~ "paid" || html =~ "bezahlt"
    end
  end

  describe "membership fee type display" do
    test "shows assigned membership fee type", %{conn: conn} do
      yearly_type = create_fee_type(%{interval: :yearly, name: "Yearly Type"})
      _monthly_type = create_fee_type(%{interval: :monthly, name: "Monthly Type"})

      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: yearly_type.id})

      {:ok, _view, html} = live(conn, "/members/#{member.id}")

      # Should show yearly type name
      assert html =~ "Yearly Type"
    end

    test "shows no type message when no type assigned and Regenerate Cycles button is hidden", %{
      conn: conn
    } do
      member = Mv.Fixtures.member_fixture(%{})

      {:ok, view, html} = live(conn, "/members/#{member.id}")

      # Should show message about no type assigned
      assert html =~ "No membership fee type assigned" || html =~ "No type"

      # Switch to membership fees tab: message and no Regenerate Cycles button
      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      refute has_element?(view, "button[phx-click='regenerate_cycles']"),
             "Regenerate Cycles should be hidden when no membership fee type is assigned"
    end
  end

  describe "status change actions" do
    test "mark as paid works", %{conn: conn} do
      fee_type = create_fee_type(%{interval: :yearly})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})

      cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})

      {:ok, view, _html} = live(conn, "/members/#{member.id}")

      # Switch to membership fees tab
      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      # Mark as paid
      view
      |> element(
        "button[phx-click='mark_cycle_status'][phx-value-cycle_id='#{cycle.id}'][phx-value-status='paid']"
      )
      |> render_click()

      # Verify cycle is now paid
      system_actor = Mv.Helpers.SystemActor.get_system_actor()

      updated_cycle =
        Ash.read_one!(MembershipFeeCycle |> Ash.Query.filter(id == ^cycle.id),
          actor: system_actor
        )

      assert updated_cycle.status == :paid
    end

    test "mark as suspended works", %{conn: conn} do
      fee_type = create_fee_type(%{interval: :yearly})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})

      cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})

      {:ok, view, _html} = live(conn, "/members/#{member.id}")

      # Switch to membership fees tab
      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      # Mark as suspended
      view
      |> element(
        "button[phx-click='mark_cycle_status'][phx-value-cycle_id='#{cycle.id}'][phx-value-status='suspended']"
      )
      |> render_click()

      # Verify cycle is now suspended
      system_actor = Mv.Helpers.SystemActor.get_system_actor()

      updated_cycle =
        Ash.read_one!(MembershipFeeCycle |> Ash.Query.filter(id == ^cycle.id),
          actor: system_actor
        )

      assert updated_cycle.status == :suspended
    end

    test "mark as unpaid works", %{conn: conn} do
      fee_type = create_fee_type(%{interval: :yearly})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})

      cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :paid})

      {:ok, view, _html} = live(conn, "/members/#{member.id}")

      # Switch to membership fees tab
      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      # Mark as unpaid
      view
      |> element(
        "button[phx-click='mark_cycle_status'][phx-value-cycle_id='#{cycle.id}'][phx-value-status='unpaid']"
      )
      |> render_click()

      # Verify cycle is now unpaid
      system_actor = Mv.Helpers.SystemActor.get_system_actor()

      updated_cycle =
        Ash.read_one!(MembershipFeeCycle |> Ash.Query.filter(id == ^cycle.id),
          actor: system_actor
        )

      assert updated_cycle.status == :unpaid
    end
  end

  describe "cycle regeneration" do
    test "manual regeneration button exists and can be clicked", %{conn: conn} do
      fee_type = create_fee_type(%{interval: :yearly})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})

      {:ok, view, _html} = live(conn, "/members/#{member.id}")

      # Switch to membership fees tab
      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      # Verify regenerate button exists
      assert has_element?(view, "button[phx-click='regenerate_cycles']")

      # Trigger regeneration (just verify it doesn't crash)
      view
      |> element("button[phx-click='regenerate_cycles']")
      |> render_click()

      # Verify the action completed without error
      # (The actual cycle generation depends on many factors, so we just test the UI works)
      assert render(view) =~ "Membership Fees" || render(view) =~ "Mitgliedsbeiträge"
    end
  end

  describe "edge cases" do
    test "handles members without membership fee type gracefully", %{conn: conn} do
      # No fee type
      member = Mv.Fixtures.member_fixture(%{})

      {:ok, _view, html} = live(conn, "/members/#{member.id}")

      # Should not crash
      assert html =~ member.first_name
    end
  end

  describe "read_only user (Vorstand/Buchhaltung) - no cycle action buttons" do
    @tag role: :read_only
    test "read_only does not see Regenerate Cycles, Delete All Cycles, or Create Cycle buttons",
         %{
           conn: conn
         } do
      fee_type = create_fee_type(%{interval: :yearly})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})
      _cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})

      {:ok, view, _html} = live(conn, "/members/#{member.id}")

      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      refute has_element?(view, "button[phx-click='regenerate_cycles']")
      refute has_element?(view, "button[phx-click='delete_all_cycles']")
      refute has_element?(view, "button[phx-click='open_create_cycle_modal']")
    end

    @tag role: :read_only
    test "read_only does not see Paid, Unpaid, Suspended, or Delete buttons in cycles table", %{
      conn: conn
    } do
      fee_type = create_fee_type(%{interval: :yearly})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})
      cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})

      {:ok, view, _html} = live(conn, "/members/#{member.id}")

      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      # Row action buttons must not be present for read_only
      refute has_element?(view, "button[phx-click='mark_cycle_status']")
      refute has_element?(view, "button[phx-click='delete_cycle']")
      # Sanity: cycle row is present (read is allowed)
      assert has_element?(view, "tr[id='cycle-#{cycle.id}']")
    end
  end

  describe "read_only cannot delete all cycles (policy enforced via Ash.destroy)" do
    @tag role: :read_only
    test "Ash.destroy returns Forbidden for read_only so handler would reject", %{
      current_user: read_only_user
    } do
      # The handler uses Ash.destroy per cycle, so if the handler were triggered
      # (e.g. via dev tools), the server would enforce policy and show an error.
      # This test verifies that Ash.destroy(cycle, actor: read_only_user) returns Forbidden.
      fee_type = create_fee_type(%{interval: :yearly})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})
      cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})

      assert {:error, %Ash.Error.Forbidden{}} =
               Ash.destroy(cycle, domain: Mv.MembershipFees, actor: read_only_user)
    end
  end

  describe "read_only cannot trigger regenerate_cycles (handler enforces can?)" do
    @tag role: :read_only
    test "read_only cannot create MembershipFeeCycle so regenerate_cycles handler would show flash error",
         %{current_user: read_only_user} do
      # The regenerate_cycles handler checks can?(actor, :create, MembershipFeeCycle) before
      # calling the generator. If a read_only user triggered the event (e.g. via DevTools),
      # the handler returns flash error and no new cycles are created.
      # This test verifies the condition the handler uses.
      refute MvWeb.Authorization.can?(read_only_user, :create, MembershipFeeCycle),
             "read_only must not be allowed to create MembershipFeeCycle so handler rejects regenerate_cycles"
    end
  end

  describe "confirm_delete_all_cycles handler (policy enforced)" do
    @tag role: :admin
    test "admin can delete all cycles via UI and cycles are removed", %{conn: conn} do
      # Use English locale so confirmation "Yes" matches gettext("Yes")
      conn = put_session(conn, :locale, "en")

      fee_type = create_fee_type(%{interval: :yearly})
      member = Mv.Fixtures.member_fixture(%{membership_fee_type_id: fee_type.id})
      _c1 = create_cycle(member, fee_type, %{cycle_start: ~D[2022-01-01], status: :paid})
      _c2 = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})

      {:ok, view, _html} = live(conn, "/members/#{member.id}")

      view
      |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']")
      |> render_click()

      view
      |> element("button[phx-click='delete_all_cycles']")
      |> render_click()

      view
      |> element("input[phx-keyup='update_delete_all_confirmation']")
      |> render_keyup(%{"value" => "Yes"})

      view
      |> element("button[phx-click='confirm_delete_all_cycles']")
      |> render_click()

      _html = render(view)

      system_actor = Mv.Helpers.SystemActor.get_system_actor()

      remaining =
        Mv.MembershipFees.MembershipFeeCycle
        |> Ash.Query.filter(member_id == ^member.id)
        |> Ash.read!(actor: system_actor)

      assert remaining == [],
             "Expected all cycles to be deleted (handler enforces policy via Ash.destroy)"
    end
  end
end
