defmodule Towerops.Organizations.InvitationTest do
  use Towerops.DataCase

  alias Towerops.Organizations.Invitation

  describe "changeset/2" do
    test "valid changeset with required fields" do
      attrs = %{
        email: "test@example.com",
        role: :member,
        organization_id: Ecto.UUID.generate(),
        invited_by_id: Ecto.UUID.generate()
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)

      assert changeset.valid?
      assert get_field(changeset, :email) == "test@example.com"
      assert get_field(changeset, :role) == :member
      # Token and expires_at should be auto-generated
      assert get_field(changeset, :token)
      assert get_field(changeset, :expires_at)
    end

    test "requires email" do
      attrs = %{
        role: :member,
        organization_id: Ecto.UUID.generate(),
        invited_by_id: Ecto.UUID.generate()
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)

      refute changeset.valid?
      assert %{email: ["can't be blank"]} = errors_on(changeset)
    end

    test "defaults role to :member when not provided" do
      attrs = %{
        email: "test@example.com",
        organization_id: Ecto.UUID.generate(),
        invited_by_id: Ecto.UUID.generate()
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)

      assert changeset.valid?
      assert Ecto.Changeset.get_field(changeset, :role) == :member
    end

    test "requires organization_id" do
      attrs = %{
        email: "test@example.com",
        role: :member,
        invited_by_id: Ecto.UUID.generate()
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)

      refute changeset.valid?
      assert %{organization_id: ["can't be blank"]} = errors_on(changeset)
    end

    test "requires invited_by_id" do
      attrs = %{
        email: "test@example.com",
        role: :member,
        organization_id: Ecto.UUID.generate()
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)

      refute changeset.valid?
      assert %{invited_by_id: ["can't be blank"]} = errors_on(changeset)
    end

    test "validates email format" do
      attrs = %{
        email: "invalid-email",
        role: :member,
        organization_id: Ecto.UUID.generate(),
        invited_by_id: Ecto.UUID.generate()
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)

      refute changeset.valid?
      assert %{email: ["has invalid format"]} = errors_on(changeset)
    end

    test "accepts valid email formats" do
      valid_emails = [
        "test@example.com",
        "user.name@domain.co.uk",
        "user+tag@example.org"
      ]

      for email <- valid_emails do
        attrs = %{
          email: email,
          role: :member,
          organization_id: Ecto.UUID.generate(),
          invited_by_id: Ecto.UUID.generate()
        }

        changeset = Invitation.changeset(%Invitation{}, attrs)
        assert changeset.valid?, "Expected #{email} to be valid"
      end
    end

    test "generates token automatically" do
      attrs = %{
        email: "test@example.com",
        role: :member,
        organization_id: Ecto.UUID.generate(),
        invited_by_id: Ecto.UUID.generate()
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)
      token = get_field(changeset, :token)

      assert token
      assert is_binary(token)
      assert String.length(token) > 0
    end

    test "does not override provided token" do
      attrs = %{
        email: "test@example.com",
        role: :member,
        organization_id: Ecto.UUID.generate(),
        invited_by_id: Ecto.UUID.generate(),
        token: "custom-token"
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)

      assert get_field(changeset, :token) == "custom-token"
    end

    test "generates expires_at automatically (7 days from now)" do
      attrs = %{
        email: "test@example.com",
        role: :member,
        organization_id: Ecto.UUID.generate(),
        invited_by_id: Ecto.UUID.generate()
      }

      now = DateTime.utc_now()
      changeset = Invitation.changeset(%Invitation{}, attrs)
      expires_at = get_field(changeset, :expires_at)

      assert expires_at

      # Should be approximately 7 days from now (within 1 minute tolerance)
      diff_seconds = DateTime.diff(expires_at, now, :second)
      expected_seconds = 7 * 24 * 60 * 60
      assert_in_delta diff_seconds, expected_seconds, 60
    end

    test "does not override provided expires_at" do
      custom_expires_at = DateTime.utc_now() |> DateTime.add(1, :day) |> DateTime.truncate(:second)

      attrs = %{
        email: "test@example.com",
        role: :member,
        organization_id: Ecto.UUID.generate(),
        invited_by_id: Ecto.UUID.generate(),
        expires_at: custom_expires_at
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)

      assert DateTime.compare(get_field(changeset, :expires_at), custom_expires_at) == :eq
    end

    test "accepts all valid role values" do
      roles = [:admin, :member, :viewer]

      for role <- roles do
        attrs = %{
          email: "test@example.com",
          role: role,
          organization_id: Ecto.UUID.generate(),
          invited_by_id: Ecto.UUID.generate()
        }

        changeset = Invitation.changeset(%Invitation{}, attrs)
        assert changeset.valid?, "Expected role #{role} to be valid"
      end
    end

    test "allows accepted_at to be set" do
      accepted_at = DateTime.truncate(DateTime.utc_now(), :second)

      attrs = %{
        email: "test@example.com",
        role: :member,
        organization_id: Ecto.UUID.generate(),
        invited_by_id: Ecto.UUID.generate(),
        accepted_at: accepted_at
      }

      changeset = Invitation.changeset(%Invitation{}, attrs)

      assert changeset.valid?
      assert DateTime.compare(get_field(changeset, :accepted_at), accepted_at) == :eq
    end
  end
end
