drop function if exists "public"."usr_session_touch"(session usr_session_key);

set check_function_bodies = off;

CREATE OR REPLACE FUNCTION public.usr_groups(of_usr human_uuid.huid)
 RETURNS SETOF grp
 LANGUAGE plpgsql
 STABLE
AS $function$
begin
  return query select g.*
               from public.usr u
               inner join public.grp_usr gu on gu.usr = u.id
               inner join public.grp g on g.id = gu.grp
               where u.uid = of_usr;
end;
$function$
;

CREATE OR REPLACE FUNCTION public.usr_session_touch(touch_key usr_session_key)
 RETURNS usr
 LANGUAGE plpgsql
AS $function$
declare
  session     public.usr_session;
  session_usr public.usr;
  new_exp     timestamp;
begin
  select s.*
  from public.usr_session s
  where s.key = touch_key
  into session;

  if session is null then
    raise exception 'usr_session_invalid';
  end if;

  if session.expires_at <= now() then
    raise exception 'usr_session_expired';
  end if;

  if session.remembered then
    new_exp := now() + interval '1 week';
  else
    new_exp := now() + interval '1 hour';
  end if;

  update public.usr_session as s
  set expires_at = new_exp
  where s.id = session.id;

  select u.*
  from public.usr u
  where u.id = session.usr
  into session_usr;

  return session_usr;
end;
$function$
;

CREATE OR REPLACE FUNCTION public.do_insert_grp_perm()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
begin
  insert into public.perm
    (path, owner_user, owner_group, owner_user_mode, owner_group_mode, everyone_mode)
  values
    ('/groups/' || human_uuid.huid_to_string(NEW.uid) || '/members', (public.get_acting_usr()).id, NEW.id, 'w', 'w', '-')
  , ('/groups/' || human_uuid.huid_to_string(NEW.uid) || '/name', (public.get_acting_usr()).id, NEW.id, 'w', 'w', 'r')
  ;

  return new;
end;
$function$
;

CREATE OR REPLACE FUNCTION public.do_usr_create_default_grp()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
declare
  name    public.grp_tag := grp_tag_of_string('usr_' || new.uid);
  new_grp public.grp;
begin
  insert into public.grp
    (tag)
  values
    (name)
  returning * into new_grp;

  perform public.grp_add_member(to_grp => new_grp.id, add_usr => new.id);

  update public.perm
  set owner_user = NEW.id
    , owner_group = (public.grp_admins()).id
    , owner_user_mode = 'r' :: public.perm_mode
  where path = '/groups/' || (new_grp.uid :: text) || '/members'
     or path = '/groups/' || (new_grp.uid :: text) || '/name';

  return null;
end;
$function$
;

CREATE OR REPLACE FUNCTION public.do_usr_session_immutable_columns()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
begin
  if OLD.id <> NEW.id then
    raise exception 'immutable_field'
    using detail = 'public.usr_session.id is immutable',
          errcode = 'restrict_violation';
  elsif OLD.key <> NEW.key then
    raise exception 'immutable_field'
    using detail = 'public.usr_session.key is immutable',
          errcode = 'restrict_violation';
  elsif OLD.usr <> NEW.usr then
    raise exception 'immutable_field'
    using detail = 'public.usr_session.usr is immutable',
          errcode = 'restrict_violation';
  elsif OLD.location <> NEW.location then
    raise exception 'immutable_field'
    using detail = 'public.usr_session.location is immutable',
          errcode = 'restrict_violation';
  elsif OLD.device <> NEW.device then
    raise exception 'immutable_field'
    using detail = 'public.usr_session.device is immutable',
          errcode = 'restrict_violation';
  elsif OLD.ip <> NEW.ip then
    raise exception 'immutable_field'
    using detail = 'public.usr_session.ip is immutable',
          errcode = 'restrict_violation';
  end if;

  return NEW;
end;
$function$
;

CREATE OR REPLACE FUNCTION public.usr_session_login(tag_or_email usr_tag_or_email, password text, remember boolean DEFAULT false, location text DEFAULT NULL::text, device usr_session_device DEFAULT NULL::usr_session_device, ip inet DEFAULT NULL::inet)
 RETURNS usr_session_key
 LANGUAGE plpgsql
AS $function$
declare
  usr        public.usr;
  key        public.usr_session_key := usr_session_key_gen();
  expires_at timestamp;
begin
  usr := public.usr_session_login_validate(tag_or_email, password);

  if remember then
    expires_at := now() + interval '1 week';
  else
    expires_at := now() + interval '1 hour';
  end if;

  insert into public.usr_session
    (key, expires_at, usr, remembered, location, device, ip)
  values
    (key, expires_at, usr.id, coalesce(remember, false), location, device, ip);

  return key;
end;
$function$
;
