select create_newtype_text('public.usr_tag');

select create_newtype_text('public.usr_tag_or_email');

create function usr_tag_or_email_to_email(toe public.usr_tag_or_email)
  returns public.email
  language plpgsql
  immutable as $$
begin
  if position('@' in usr_tag_or_email_to_string(toe)) > 0 then
    return email_of_string(usr_tag_or_email_to_string(toe));
  else
    return null;
  end if;
end;
$$;

create function usr_tag_or_email_to_tag(toe public.usr_tag_or_email)
  returns public.usr_tag
  language plpgsql
  immutable as $$
begin
  if usr_tag_or_email_to_email(toe) is null then
    return usr_tag_of_string(usr_tag_or_email_to_string(toe));
  else
    return null;
  end if;
end;
$$;

create table public.usr
  ( id       int                 not null primary key generated always as identity
  , uid      uuid                not null default gen_random_uuid()
  , deleted  boolean             not null default false
  , tag      public.usr_tag      not null
  , discrim  int                 not null default 0
  , password public.hashed_text  not null
  , email    public.email        not null unique
  , unique (tag, discrim)
  );

create function public.do_insert_usr_default_discrim()
returns trigger
stable
language plpgsql
as $$
begin
  if new.discrim is null then
    new.discrim := (select count(*) from public.usr u where u.tag = tag);
  end if;

  return new;
end;
$$;

create trigger insert_usr_default_discrim
before insert on public.usr
for each row execute function public.do_insert_usr_default_discrim();

insert into public.usr
  (tag, password, email)
values
  (usr_tag_of_string('root'), hashed_text_of_string(''), email_of_string(''));

create function public.do_check_usr_tag()
returns trigger
stable
language plpgsql
as $$
begin
  if new.tag = usr_tag_of_string('root') then
    raise exception 'invalid tag';
  else
    return new;
  end if;
end;
$$;

create trigger check_usr_tag
before insert on public.usr
for each row execute function public.do_check_usr_tag();

select audit( 'public'
            , 'usr'
            , array[ row('tag', 'public.usr_tag')
                   , row('password', 'public.hashed_text')
                   , row('email', 'public.email')
                   ] :: audited_column[]
            , soft_delete => true
            );

select immutable( 'public'
                , 'usr'
                , array[ 'id'
                       , 'uid'
                       ]
                );
