defmodule Mv.Membership.CustomFieldValueValidationTest do
  @moduledoc """
  Tests for CustomFieldValue validation constraints.

  Tests cover:
  - String value length validation (max 10,000 characters)
  - String value trimming
  - Email value validation (via Email type)
  - Optional values (nil allowed)
  """
  use Mv.DataCase, async: true

  alias Mv.Membership.{CustomField, CustomFieldValue}

  setup do
    system_actor = Mv.Helpers.SystemActor.get_system_actor()

    # Create a test member
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "Test",
          last_name: "User",
          email: "test.validation@example.com"
        },
        actor: system_actor
      )

    # Create custom fields for different types
    {:ok, string_field} =
      CustomField
      |> Ash.Changeset.for_create(:create, %{
        name: "string_field",
        value_type: :string
      })
      |> Ash.create(actor: system_actor)

    {:ok, integer_field} =
      CustomField
      |> Ash.Changeset.for_create(:create, %{
        name: "integer_field",
        value_type: :integer
      })
      |> Ash.create(actor: system_actor)

    {:ok, email_field} =
      CustomField
      |> Ash.Changeset.for_create(:create, %{
        name: "email_field",
        value_type: :email
      })
      |> Ash.create(actor: system_actor)

    %{
      actor: system_actor,
      member: member,
      string_field: string_field,
      integer_field: integer_field,
      email_field: email_field
    }
  end

  describe "string value length validation" do
    test "accepts string value with exactly 10,000 characters", %{
      actor: system_actor,
      member: member,
      string_field: string_field
    } do
      value_string = String.duplicate("a", 10_000)

      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: string_field.id,
                 value: %{
                   "_union_type" => "string",
                   "_union_value" => value_string
                 }
               })
               |> Ash.create(actor: system_actor)

      assert custom_field_value.value.value == value_string
      assert String.length(custom_field_value.value.value) == 10_000
    end

    test "rejects string value with 10,001 characters", %{
      actor: system_actor,
      member: member,
      string_field: string_field
    } do
      value_string = String.duplicate("a", 10_001)

      assert {:error, changeset} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: string_field.id,
                 value: %{"_union_type" => "string", "_union_value" => value_string}
               })
               |> Ash.create(actor: system_actor)

      assert Enum.any?(changeset.errors, fn error ->
               error.field == :value and (error.message =~ "max" or error.message =~ "length")
             end)
    end

    test "trims whitespace from string value", %{
      actor: system_actor,
      member: member,
      string_field: string_field
    } do
      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: string_field.id,
                 value: %{"_union_type" => "string", "_union_value" => "  test value  "}
               })
               |> Ash.create(actor: system_actor)

      assert custom_field_value.value.value == "test value"
    end

    test "accepts empty string value", %{
      actor: system_actor,
      member: member,
      string_field: string_field
    } do
      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: string_field.id,
                 value: %{"_union_type" => "string", "_union_value" => ""}
               })
               |> Ash.create(actor: system_actor)

      # Empty strings after trimming become nil
      assert custom_field_value.value.value == nil
    end

    test "accepts string with special characters", %{
      actor: system_actor,
      member: member,
      string_field: string_field
    } do
      special_string = "Hello 世界! 🎉 @#$%^&*()"

      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: string_field.id,
                 value: %{"_union_type" => "string", "_union_value" => special_string}
               })
               |> Ash.create(actor: system_actor)

      assert custom_field_value.value.value == special_string
    end
  end

  describe "integer value validation" do
    test "accepts valid integer value", %{
      actor: system_actor,
      member: member,
      integer_field: integer_field
    } do
      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: integer_field.id,
                 value: %{"_union_type" => "integer", "_union_value" => 42}
               })
               |> Ash.create(actor: system_actor)

      assert custom_field_value.value.value == 42
    end

    test "accepts negative integer", %{
      actor: system_actor,
      member: member,
      integer_field: integer_field
    } do
      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: integer_field.id,
                 value: %{"_union_type" => "integer", "_union_value" => -100}
               })
               |> Ash.create(actor: system_actor)

      assert custom_field_value.value.value == -100
    end

    test "accepts zero", %{actor: system_actor, member: member, integer_field: integer_field} do
      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: integer_field.id,
                 value: %{"_union_type" => "integer", "_union_value" => 0}
               })
               |> Ash.create(actor: system_actor)

      assert custom_field_value.value.value == 0
    end
  end

  describe "email value validation" do
    test "accepts nil value (optional field)", %{
      actor: system_actor,
      member: member,
      email_field: email_field
    } do
      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: email_field.id,
                 value: %{"_union_type" => "email", "_union_value" => nil}
               })
               |> Ash.create(actor: system_actor)

      assert custom_field_value.value.value == nil
    end

    test "accepts empty string (becomes nil after trim)", %{
      actor: system_actor,
      member: member,
      email_field: email_field
    } do
      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: email_field.id,
                 value: %{"_union_type" => "email", "_union_value" => ""}
               })
               |> Ash.create(actor: system_actor)

      # Empty string after trim should become nil
      assert custom_field_value.value.value == nil
    end

    test "accepts valid email", %{actor: system_actor, member: member, email_field: email_field} do
      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: email_field.id,
                 value: %{"_union_type" => "email", "_union_value" => "test@example.com"}
               })
               |> Ash.create(actor: system_actor)

      assert custom_field_value.value.value == "test@example.com"
    end

    test "rejects invalid email format", %{
      actor: system_actor,
      member: member,
      email_field: email_field
    } do
      assert {:error, changeset} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: email_field.id,
                 value: %{"_union_type" => "email", "_union_value" => "not-an-email"}
               })
               |> Ash.create(actor: system_actor)

      assert Enum.any?(changeset.errors, fn error -> error.field == :value end)
    end

    test "rejects email longer than 254 characters", %{
      actor: system_actor,
      member: member,
      email_field: email_field
    } do
      # Create an email with >254 chars (243 + 12 = 255)
      long_email = String.duplicate("a", 243) <> "@example.com"

      assert {:error, changeset} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: email_field.id,
                 value: %{"_union_type" => "email", "_union_value" => long_email}
               })
               |> Ash.create(actor: system_actor)

      assert Enum.any?(changeset.errors, fn error -> error.field == :value end)
    end

    test "trims whitespace from email", %{
      actor: system_actor,
      member: member,
      email_field: email_field
    } do
      assert {:ok, custom_field_value} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: email_field.id,
                 value: %{"_union_type" => "email", "_union_value" => "  test@example.com  "}
               })
               |> Ash.create(actor: system_actor)

      assert custom_field_value.value.value == "test@example.com"
    end
  end

  describe "uniqueness constraint" do
    test "rejects duplicate custom_field_id per member", %{
      actor: system_actor,
      member: member,
      string_field: string_field
    } do
      # Create first custom field value
      assert {:ok, _} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: string_field.id,
                 value: %{"_union_type" => "string", "_union_value" => "first value"}
               })
               |> Ash.create(actor: system_actor)

      # Try to create second custom field value with same custom_field_id for same member
      assert {:error, changeset} =
               CustomFieldValue
               |> Ash.Changeset.for_create(:create, %{
                 member_id: member.id,
                 custom_field_id: string_field.id,
                 value: %{"_union_type" => "string", "_union_value" => "second value"}
               })
               |> Ash.create(actor: system_actor)

      # Should have uniqueness error
      assert Enum.any?(changeset.errors, fn error ->
               error.message =~ "unique" or error.message =~ "already exists" or
                 error.message =~ "has already been taken"
             end)
    end
  end
end
