begin;

create or replace function public.append_consultation_question(
  p_user_id uuid,
  p_request_id text,
  p_session_id uuid,
  p_question_message jsonb
)
returns table(success boolean, error_code text)
language plpgsql
security definer
set search_path = ''
as $$
declare
  v_session public.chat_sessions%rowtype;
  v_message jsonb;
  v_text text;
  v_request_id text;
  v_count integer;
  v_chars integer;
  v_new_chars integer;
begin
  v_request_id := btrim(coalesce(p_request_id, ''));
  if p_user_id is null or p_session_id is null or v_request_id = '' then
    return query select false, 'invalid_request'::text;
    return;
  end if;
  if jsonb_typeof(p_question_message) <> 'object'
    or p_question_message->>'role' <> 'user' then
    return query select false, 'invalid_question_message'::text;
    return;
  end if;
  v_text := btrim(coalesce(p_question_message->>'text', ''));
  if v_text = '' or char_length(v_text) > 16000 then
    return query select false, 'invalid_question_message'::text;
    return;
  end if;

  perform pg_advisory_xact_lock(hashtextextended(p_user_id::text || ':' || v_request_id, 0));

  select session.* into v_session
  from public.chat_sessions as session
  where session.id = p_session_id
    and session.user_id = p_user_id
    and session.session_type = 'consultation'
  for update;

  if not found then
    return query select false, 'session_missing'::text;
    return;
  end if;

  if exists (
    select 1
    from jsonb_array_elements(coalesce(v_session.messages, '[]'::jsonb)) as elem
    where elem->>'requestId' = v_request_id
  ) then
    return query select true, null::text;
    return;
  end if;

  v_count := jsonb_array_length(coalesce(v_session.messages, '[]'::jsonb));
  select coalesce(sum(
    length(coalesce(elem->>'text', ''))
    + length(coalesce(elem->>'thinkingText', ''))
    + case
      when elem ? 'thinkingSections' then length((elem->'thinkingSections')::text)
      else 0
    end
  ), 0)
  into v_chars
  from jsonb_array_elements(coalesce(v_session.messages, '[]'::jsonb)) as elem;

  v_new_chars := length(v_text);
  if v_count >= 200 or (v_chars + v_new_chars) > 200000 then
    return query select false, 'session_full'::text;
    return;
  end if;

  v_message := p_question_message || jsonb_build_object('requestId', v_request_id);

  update public.chat_sessions as session
  set messages = coalesce(session.messages, '[]'::jsonb) || jsonb_build_array(v_message),
      title = case
        when coalesce(btrim(session.title), '') in ('', '新对话') then
          case
            when char_length(v_text) > 14 then left(v_text, 14) || '…'
            else v_text
          end
        else session.title
      end,
      updated_at = clock_timestamp()
  where session.id = p_session_id
    and session.user_id = p_user_id
    and session.session_type = 'consultation';
  if not found then
    return query select false, 'session_missing'::text;
    return;
  end if;

  return query select true, null::text;
end;
$$;

revoke all on function public.append_consultation_question(uuid, text, uuid, jsonb)
  from public, anon, authenticated;
grant execute on function public.append_consultation_question(uuid, text, uuid, jsonb)
  to service_role;
do $$ begin
  if exists(select 1 from pg_roles where rolname = 'admin_runtime') then
    grant execute on function public.append_consultation_question(uuid, text, uuid, jsonb)
      to admin_runtime;
  end if;
end $$;

commit;
