defmodule Mv.Authorization.Checks.MemberGroupReadLinkedForOwnData do
  @moduledoc """
  Policy check for MemberGroup read: true only when actor has permission set "own_data"
  AND record.member_id == actor.member_id.

  Used in a bypass so that own_data gets the linked filter (via auto_filter for list queries),
  while admin with member_id does not match and gets :all from HasPermission.

  - With a record (e.g. get by id): returns true only when own_data and member_id match.
  - Without a record (list query): strict_check returns false; auto_filter adds filter when own_data.
  """
  use Ash.Policy.Check

  alias Mv.Authorization.Checks.ActorPermissionSetIs

  @impl true
  def type, do: :filter

  @impl true
  def describe(_opts),
    do: "own_data can read only member_groups where member_id == actor.member_id"

  @impl true
  def strict_check(actor, authorizer, _opts) do
    record = get_record_from_authorizer(authorizer)
    is_own_data = ActorPermissionSetIs.match?(actor, authorizer, permission_set_name: "own_data")

    cond do
      # List query + own_data: return :unknown so authorizer applies auto_filter (keyword list)
      is_nil(record) and is_own_data ->
        {:ok, :unknown}

      is_nil(record) ->
        {:ok, false}

      not is_own_data ->
        {:ok, false}

      record.member_id == actor.member_id ->
        {:ok, true}

      true ->
        {:ok, false}
    end
  end

  @impl true
  def auto_filter(actor, _authorizer, _opts) do
    if ActorPermissionSetIs.match?(actor, nil, permission_set_name: "own_data") &&
         Map.get(actor, :member_id) do
      [member_id: actor.member_id]
    else
      []
    end
  end

  defp get_record_from_authorizer(authorizer) do
    case authorizer.subject do
      %{data: data} when not is_nil(data) -> data
      _ -> nil
    end
  end
end
