defmodule Mv.MembershipFees.ForeignKeyTest do
  @moduledoc """
  Tests for foreign key behaviors (CASCADE and RESTRICT).
  """
  use Mv.DataCase, async: true

  alias Mv.MembershipFees.MembershipFeeCycle
  alias Mv.MembershipFees.MembershipFeeType
  alias Mv.Membership.Member

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

  describe "CASCADE behavior" do
    test "deleting member deletes associated membership_fee_cycles", %{actor: actor} do
      # Create member
      {:ok, member} =
        Ash.create(
          Member,
          %{
            first_name: "Cascade",
            last_name: "Test",
            email: "cascade.test.#{System.unique_integer([:positive])}@example.com"
          },
          actor: actor
        )

      # Create fee type
      {:ok, fee_type} =
        Ash.create(
          MembershipFeeType,
          %{
            name: "Cascade Test Fee #{System.unique_integer([:positive])}",
            amount: Decimal.new("100.00"),
            interval: :monthly
          },
          actor: actor
        )

      # Create multiple cycles for this member
      {:ok, cycle1} =
        Ash.create(
          MembershipFeeCycle,
          %{
            cycle_start: ~D[2025-01-01],
            amount: Decimal.new("100.00"),
            member_id: member.id,
            membership_fee_type_id: fee_type.id
          },
          actor: actor
        )

      {:ok, cycle2} =
        Ash.create(
          MembershipFeeCycle,
          %{
            cycle_start: ~D[2025-02-01],
            amount: Decimal.new("100.00"),
            member_id: member.id,
            membership_fee_type_id: fee_type.id
          },
          actor: actor
        )

      # Verify cycles exist
      assert {:ok, _} = Ash.get(MembershipFeeCycle, cycle1.id, actor: actor)
      assert {:ok, _} = Ash.get(MembershipFeeCycle, cycle2.id, actor: actor)

      # Delete member
      assert :ok = Ash.destroy(member, actor: actor)

      # Verify cycles are also deleted (CASCADE)
      # NotFound is wrapped in Ash.Error.Invalid
      assert {:error, %Ash.Error.Invalid{}} = Ash.get(MembershipFeeCycle, cycle1.id, actor: actor)
      assert {:error, %Ash.Error.Invalid{}} = Ash.get(MembershipFeeCycle, cycle2.id, actor: actor)
    end
  end

  describe "RESTRICT behavior" do
    test "cannot delete membership_fee_type if cycles reference it", %{actor: actor} do
      # Create member
      {:ok, member} =
        Ash.create(
          Member,
          %{
            first_name: "Restrict",
            last_name: "Test",
            email: "restrict.test.#{System.unique_integer([:positive])}@example.com"
          },
          actor: actor
        )

      # Create fee type
      {:ok, fee_type} =
        Ash.create(
          MembershipFeeType,
          %{
            name: "Restrict Test Fee #{System.unique_integer([:positive])}",
            amount: Decimal.new("100.00"),
            interval: :monthly
          },
          actor: actor
        )

      # Create a cycle referencing this fee type
      {:ok, _cycle} =
        Ash.create(
          MembershipFeeCycle,
          %{
            cycle_start: ~D[2025-01-01],
            amount: Decimal.new("100.00"),
            member_id: member.id,
            membership_fee_type_id: fee_type.id
          },
          actor: actor
        )

      # Try to delete fee type - should fail due to RESTRICT
      assert {:error, error} = Ash.destroy(fee_type, actor: actor)

      # Check that it's a foreign key violation error
      assert is_struct(error, Ash.Error.Invalid) or is_struct(error, Ash.Error.Unknown)
    end

    test "can delete membership_fee_type if no cycles reference it", %{actor: actor} do
      # Create fee type without any cycles
      {:ok, fee_type} =
        Ash.create(
          MembershipFeeType,
          %{
            name: "Deletable Fee #{System.unique_integer([:positive])}",
            amount: Decimal.new("100.00"),
            interval: :monthly
          },
          actor: actor
        )

      # Should be able to delete
      assert :ok = Ash.destroy(fee_type, actor: actor)

      # Verify it's gone (NotFound is wrapped in Ash.Error.Invalid)
      assert {:error, %Ash.Error.Invalid{}} =
               Ash.get(MembershipFeeType, fee_type.id, actor: actor)
    end

    test "cannot delete membership_fee_type if members reference it", %{actor: actor} do
      # Create fee type
      {:ok, fee_type} =
        Ash.create(
          MembershipFeeType,
          %{
            name: "Member Ref Fee #{System.unique_integer([:positive])}",
            amount: Decimal.new("100.00"),
            interval: :monthly
          },
          actor: actor
        )

      # Create member with this fee type
      {:ok, _member} =
        Ash.create(
          Member,
          %{
            first_name: "FeeType",
            last_name: "Reference",
            email: "feetype.ref.#{System.unique_integer([:positive])}@example.com",
            membership_fee_type_id: fee_type.id
          },
          actor: actor
        )

      # Try to delete fee type - should fail due to RESTRICT
      assert {:error, error} = Ash.destroy(fee_type, actor: actor)
      assert is_struct(error, Ash.Error.Invalid) or is_struct(error, Ash.Error.Unknown)
    end
  end

  describe "member extensions" do
    test "member can be created with membership_fee_type_id", %{actor: actor} do
      # Create fee type first
      {:ok, fee_type} =
        Ash.create(
          MembershipFeeType,
          %{
            name: "Create Test Fee #{System.unique_integer([:positive])}",
            amount: Decimal.new("100.00"),
            interval: :yearly
          },
          actor: actor
        )

      # Create member with fee type
      {:ok, member} =
        Ash.create(
          Member,
          %{
            first_name: "With",
            last_name: "FeeType",
            email: "with.feetype.#{System.unique_integer([:positive])}@example.com",
            membership_fee_type_id: fee_type.id
          },
          actor: actor
        )

      assert member.membership_fee_type_id == fee_type.id
    end

    test "member can be created with membership_fee_start_date", %{actor: actor} do
      {:ok, member} =
        Ash.create(
          Member,
          %{
            first_name: "With",
            last_name: "StartDate",
            email: "with.startdate.#{System.unique_integer([:positive])}@example.com",
            membership_fee_start_date: ~D[2025-01-01]
          },
          actor: actor
        )

      assert member.membership_fee_start_date == ~D[2025-01-01]
    end

    test "member can be created without membership fee fields", %{actor: actor} do
      {:ok, member} =
        Ash.create(
          Member,
          %{
            first_name: "No",
            last_name: "FeeFields",
            email: "no.feefields.#{System.unique_integer([:positive])}@example.com"
          },
          actor: actor
        )

      assert member.membership_fee_type_id == nil
      assert member.membership_fee_start_date == nil
    end

    test "member can be updated with membership_fee_type_id", %{actor: actor} do
      # Create fee type
      {:ok, fee_type} =
        Ash.create(
          MembershipFeeType,
          %{
            name: "Update Test Fee #{System.unique_integer([:positive])}",
            amount: Decimal.new("100.00"),
            interval: :yearly
          },
          actor: actor
        )

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

      assert member.membership_fee_type_id == nil

      # Update member with fee type
      {:ok, updated_member} =
        Ash.update(member, %{membership_fee_type_id: fee_type.id}, actor: actor)

      assert updated_member.membership_fee_type_id == fee_type.id
    end

    test "member can be updated with membership_fee_start_date", %{actor: actor} do
      {:ok, member} =
        Ash.create(
          Member,
          %{
            first_name: "Start",
            last_name: "Date",
            email: "start.date.#{System.unique_integer([:positive])}@example.com"
          },
          actor: actor
        )

      assert member.membership_fee_start_date == nil

      {:ok, updated_member} =
        Ash.update(member, %{membership_fee_start_date: ~D[2025-06-01]}, actor: actor)

      assert updated_member.membership_fee_start_date == ~D[2025-06-01]
    end
  end
end
