begin;

do $$
declare
  v_active_owner_count integer;
  v_candidate_count integer;
  v_candidate_id uuid;
  v_owner_role_id uuid;
begin
  -- This migration also belongs to the identity-only db migration ledger. When
  -- the Supabase RBAC schema is not part of that migration flow, record a safe
  -- no-op without referencing any missing relation or trigger function.
  if to_regclass('public.admin_users') is null
    or to_regclass('public.admin_roles') is null
    or to_regclass('public.admin_user_roles') is null
    or to_regclass('identity.users') is null
    or to_regclass('auth.users') is null
    or to_regprocedure('public.admin_permission_keys(uuid)') is null
    or to_regprocedure('public.assert_active_admin_owner_exists()') is null then
    return;
  end if;

  -- Serialize recovery with the last-Owner protection used by the RBAC functions.
  perform pg_catalog.pg_advisory_xact_lock(1096040772, 1);

  select count(*)
  into v_active_owner_count
  from public.admin_users au
  join public.admin_user_roles aur on aur.admin_user_id = au.user_id
  join public.admin_roles ar on ar.id = aur.role_id
  where au.revoked_at is null
    and ar.code = 'owner';

  -- Once an active Owner exists, role management remains the only authority.
  if v_active_owner_count > 0 then
    return;
  end if;

  -- Keep the candidate set stable while recovery decides and writes.
  lock table identity.users, auth.users in share mode;

  -- A pristine database has no account to recover yet. Any populated system
  -- without an Owner must have exactly one active identity-admin candidate.
  if not exists (select 1 from identity.users)
    and not exists (select 1 from auth.users) then
    return;
  end if;

  select count(*), (array_agg(u.id order by u.id))[1]
  into v_candidate_count, v_candidate_id
  from identity.users u
  join auth.users a on a.id = u.id
  left join public.admin_users existing_admin on existing_admin.user_id = u.id
  where (existing_admin.user_id is null or existing_admin.revoked_at is null)
    and (not u.banned or (u.ban_expires is not null and u.ban_expires <= clock_timestamp()))
    and exists (
      select 1
      from unnest(string_to_array(u.role, ',')) as role_part(value)
      where btrim(role_part.value) = 'admin'
    );

  if v_candidate_count <> 1 then
    raise exception 'admin_owner_recovery_requires_exactly_one_active_identity_admin: found %',
      v_candidate_count
      using errcode = '23514';
  end if;

  select id
  into v_owner_role_id
  from public.admin_roles
  where code = 'owner';

  if v_owner_role_id is null then
    raise exception 'admin_owner_recovery_owner_role_missing'
      using errcode = '23514';
  end if;

  insert into public.admin_users (user_id, created_by)
  values (v_candidate_id, v_candidate_id)
  on conflict on constraint admin_users_pkey do nothing;

  insert into public.admin_user_roles (admin_user_id, role_id, assigned_by)
  values (v_candidate_id, v_owner_role_id, v_candidate_id)
  on conflict do nothing;

  if not exists (
    select 1
    from public.admin_users au
    join public.admin_user_roles aur on aur.admin_user_id = au.user_id
    where au.user_id = v_candidate_id
      and au.revoked_at is null
      and aur.role_id = v_owner_role_id
  ) then
    raise exception 'admin_owner_recovery_failed'
      using errcode = '23514';
  end if;
end;
$$;

commit;
