-- =============================================================================
-- AgenciaPsi — Functions — Pacientes
-- =============================================================================
-- can_delete_patient, safe_delete_patient, create_patient_intake_request,
-- create_patient_intake_request_v2, rotate_patient_invite_token,
-- patients_validate_member_consistency, prevent_system_group_changes,
-- seed_default_patient_groups
-- =============================================================================

CREATE FUNCTION public.can_delete_patient(p_patient_id uuid) RETURNS boolean
    LANGUAGE sql STABLE SECURITY DEFINER
    AS $$
  SELECT NOT EXISTS (
    SELECT 1 FROM public.agenda_eventos    WHERE patient_id = p_patient_id
    UNION ALL
    SELECT 1 FROM public.recurrence_rules  WHERE patient_id = p_patient_id
    UNION ALL
    SELECT 1 FROM public.billing_contracts WHERE patient_id = p_patient_id
  );
$$;


ALTER FUNCTION public.can_delete_patient(p_patient_id uuid) OWNER TO supabase_admin;

--
-- Name: cancel_notifications_on_opt_out(); Type: FUNCTION; Schema: public; Owner: supabase_admin
--

CREATE FUNCTION public.cancel_notifications_on_opt_out() RETURNS trigger
CREATE FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text DEFAULT NULL::text, p_phone text DEFAULT NULL::text, p_notes text DEFAULT NULL::text, p_consent boolean DEFAULT false) RETURNS uuid
    LANGUAGE plpgsql SECURITY DEFINER
    SET search_path TO 'public'
    AS $$
declare
  v_owner uuid;
  v_active boolean;
  v_expires timestamptz;
  v_max_uses int;
  v_uses int;
  v_id uuid;
begin
  select owner_id, active, expires_at, max_uses, uses
    into v_owner, v_active, v_expires, v_max_uses, v_uses
  from public.patient_invites
  where token = p_token
  limit 1;

  if v_owner is null then
    raise exception 'Token inválido';
  end if;

  if v_active is not true then
    raise exception 'Link desativado';
  end if;

  if v_expires is not null and now() > v_expires then
    raise exception 'Link expirado';
  end if;

  if v_max_uses is not null and v_uses >= v_max_uses then
    raise exception 'Limite de uso atingido';
  end if;

  if p_name is null or length(trim(p_name)) = 0 then
    raise exception 'Nome é obrigatório';
  end if;

  insert into public.patient_intake_requests
    (owner_id, token, name, email, phone, notes, consent, status)
  values
    (v_owner, p_token, trim(p_name),
     nullif(lower(trim(p_email)), ''),
     nullif(trim(p_phone), ''),
     nullif(trim(p_notes), ''),
     coalesce(p_consent, false),
     'new')
  returning id into v_id;

  update public.patient_invites
    set uses = uses + 1
  where token = p_token;

  return v_id;
end;
$$;


ALTER FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text, p_phone text, p_notes text, p_consent boolean) OWNER TO supabase_admin;

--
-- Name: create_patient_intake_request_v2(text, jsonb); Type: FUNCTION; Schema: public; Owner: supabase_admin
--

CREATE FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) RETURNS uuid
    LANGUAGE plpgsql SECURITY DEFINER
    SET search_path TO 'public'
    AS $_$
declare
  v_owner_id uuid;
  v_intake_id uuid;
  v_birth_raw text;
  v_birth date;
begin
  select owner_id
    into v_owner_id
  from public.patient_invites
  where token = p_token;

  if v_owner_id is null then
    raise exception 'Token inválido ou expirado';
  end if;

  v_birth_raw := nullif(trim(coalesce(
    p_payload->>'data_nascimento',
    ''
  )), '');

  v_birth := case
    when v_birth_raw is null then null
    when v_birth_raw ~ '^\d{4}-\d{2}-\d{2}$' then v_birth_raw::date
    when v_birth_raw ~ '^\d{2}-\d{2}-\d{4}$' then to_date(v_birth_raw, 'DD-MM-YYYY')
    else null
  end;

  insert into public.patient_intake_requests (
    owner_id,
    token,
    status,
    consent,

    nome_completo,
    email_principal,
    telefone,

    avatar_url,  -- 🔥 AQUI

    data_nascimento,
    cpf,
    rg,
    genero,
    estado_civil,
    profissao,
    escolaridade,
    nacionalidade,
    naturalidade,

    cep,
    pais,
    cidade,
    estado,
    endereco,
    numero,
    complemento,
    bairro,

    observacoes,
    notas_internas,

    encaminhado_por,
    onde_nos_conheceu
  )
  values (
    v_owner_id,
    p_token,
    'new',
    coalesce((p_payload->>'consent')::boolean, false),

    nullif(trim(p_payload->>'nome_completo'), ''),
    nullif(trim(p_payload->>'email_principal'), ''),
    nullif(regexp_replace(coalesce(p_payload->>'telefone',''), '\D', '', 'g'), ''),

    nullif(trim(p_payload->>'avatar_url'), ''), -- 🔥 AQUI

    v_birth,
    nullif(regexp_replace(coalesce(p_payload->>'cpf',''), '\D', '', 'g'), ''),
    nullif(trim(p_payload->>'rg'), ''),
    nullif(trim(p_payload->>'genero'), ''),
    nullif(trim(p_payload->>'estado_civil'), ''),
    nullif(trim(p_payload->>'profissao'), ''),
    nullif(trim(p_payload->>'escolaridade'), ''),
    nullif(trim(p_payload->>'nacionalidade'), ''),
    nullif(trim(p_payload->>'naturalidade'), ''),

    nullif(regexp_replace(coalesce(p_payload->>'cep',''), '\D', '', 'g'), ''),
    nullif(trim(p_payload->>'pais'), ''),
    nullif(trim(p_payload->>'cidade'), ''),
    nullif(trim(p_payload->>'estado'), ''),
    nullif(trim(p_payload->>'endereco'), ''),
    nullif(trim(p_payload->>'numero'), ''),
    nullif(trim(p_payload->>'complemento'), ''),
    nullif(trim(p_payload->>'bairro'), ''),

    nullif(trim(p_payload->>'observacoes'), ''),
    nullif(trim(p_payload->>'notas_internas'), ''),

    nullif(trim(p_payload->>'encaminhado_por'), ''),
    nullif(trim(p_payload->>'onde_nos_conheceu'), '')
  )
  returning id into v_intake_id;

  return v_intake_id;
end;
$_$;


ALTER FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) OWNER TO supabase_admin;

--
-- Name: create_support_session(uuid, integer); Type: FUNCTION; Schema: public; Owner: supabase_admin
--

CREATE FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer DEFAULT 60) RETURNS json
CREATE FUNCTION public.patients_validate_member_consistency() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
DECLARE
  v_tenant_responsible uuid;
  v_tenant_therapist uuid;
BEGIN
  -- responsible_member sempre deve existir e ser do tenant
  SELECT tenant_id INTO v_tenant_responsible
  FROM public.tenant_members
  WHERE id = NEW.responsible_member_id;

  IF v_tenant_responsible IS NULL THEN
    RAISE EXCEPTION 'Responsible member not found';
  END IF;

  IF NEW.tenant_id IS NULL THEN
    RAISE EXCEPTION 'tenant_id is required';
  END IF;

  IF v_tenant_responsible <> NEW.tenant_id THEN
    RAISE EXCEPTION 'Responsible member must belong to the same tenant';
  END IF;

  -- therapist scope: therapist_member_id deve existir e ser do mesmo tenant
  IF NEW.patient_scope = 'therapist' THEN
    IF NEW.therapist_member_id IS NULL THEN
      RAISE EXCEPTION 'therapist_member_id is required when patient_scope=therapist';
    END IF;

    SELECT tenant_id INTO v_tenant_therapist
    FROM public.tenant_members
    WHERE id = NEW.therapist_member_id;

    IF v_tenant_therapist IS NULL THEN
      RAISE EXCEPTION 'Therapist member not found';
    END IF;

    IF v_tenant_therapist <> NEW.tenant_id THEN
      RAISE EXCEPTION 'Therapist member must belong to the same tenant';
    END IF;
  END IF;

  RETURN NEW;
END;
$$;


ALTER FUNCTION public.patients_validate_member_consistency() OWNER TO supabase_admin;

--
-- Name: patients_validate_responsible_member_tenant(); Type: FUNCTION; Schema: public; Owner: supabase_admin
--

CREATE FUNCTION public.patients_validate_responsible_member_tenant() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
declare
  m_tenant uuid;
begin
  select tenant_id into m_tenant
  from public.tenant_members
  where id = new.responsible_member_id;

  if m_tenant is null then
    raise exception 'Responsible member not found';
  end if;

  if new.tenant_id is null then
    raise exception 'tenant_id is required';
  end if;

  if m_tenant <> new.tenant_id then
    raise exception 'Responsible member must belong to the same tenant';
  end if;

  return new;
end;
$$;


ALTER FUNCTION public.patients_validate_responsible_member_tenant() OWNER TO supabase_admin;

--
-- Name: populate_notification_queue(); Type: FUNCTION; Schema: public; Owner: supabase_admin
--

CREATE FUNCTION public.populate_notification_queue() RETURNS void
CREATE FUNCTION public.prevent_system_group_changes() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
begin
  -- Se for grupo do sistema, regras rígidas:
  if old.is_system = true then

    -- nunca pode deletar
    if tg_op = 'DELETE' then
      raise exception 'Grupos padrão do sistema não podem ser alterados ou excluídos.';
    end if;

    if tg_op = 'UPDATE' then
      -- permite SOMENTE mudar tenant_id e/ou updated_at
      -- qualquer mudança de conteúdo permanece proibida
      if
        new.nome         is distinct from old.nome or
        new.descricao    is distinct from old.descricao or
        new.cor          is distinct from old.cor or
        new.is_active    is distinct from old.is_active or
        new.is_system    is distinct from old.is_system or
        new.owner_id     is distinct from old.owner_id or
        new.therapist_id is distinct from old.therapist_id or
        new.created_at   is distinct from old.created_at
      then
        raise exception 'Grupos padrão do sistema não podem ser alterados ou excluídos.';
      end if;

      -- chegou aqui: só tenant_id/updated_at mudaram -> ok
      return new;
    end if;

  end if;

  -- não-system: deixa passar
  if tg_op = 'DELETE' then
    return old;
  end if;

  return new;
end;
$$;


ALTER FUNCTION public.prevent_system_group_changes() OWNER TO supabase_admin;

--
-- Name: provision_account_tenant(uuid, text, text); Type: FUNCTION; Schema: public; Owner: supabase_admin
--

CREATE FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid
CREATE FUNCTION public.rotate_patient_invite_token(p_new_token text) RETURNS uuid
    LANGUAGE plpgsql SECURITY DEFINER
    SET search_path TO 'public'
    AS $$
declare
  v_uid uuid;
  v_id uuid;
begin
  -- pega o usuário logado
  v_uid := auth.uid();
  if v_uid is null then
    raise exception 'Usuário não autenticado';
  end if;

  -- desativa tokens antigos ativos do usuário
  update public.patient_invites
    set active = false
  where owner_id = v_uid
    and active = true;

  -- cria novo token
  insert into public.patient_invites (owner_id, token, active)
  values (v_uid, p_new_token, true)
  returning id into v_id;

  return v_id;
end;
$$;


ALTER FUNCTION public.rotate_patient_invite_token(p_new_token text) OWNER TO supabase_admin;

--
-- Name: saas_votar_doc(uuid, boolean); Type: FUNCTION; Schema: public; Owner: supabase_admin
--

CREATE FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) RETURNS jsonb
CREATE FUNCTION public.safe_delete_patient(p_patient_id uuid) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
BEGIN
  -- Bloqueia se houver histórico
  IF NOT public.can_delete_patient(p_patient_id) THEN
    RETURN jsonb_build_object(
      'ok',      false,
      'error',   'has_history',
      'message', 'Este paciente possui histórico clínico ou financeiro e não pode ser removido. Você pode desativar ou arquivar o paciente.'
    );
  END IF;

  -- Verifica ownership via RLS (owner_id ou responsible_member_id)
  IF NOT EXISTS (
    SELECT 1 FROM public.patients
     WHERE id = p_patient_id
       AND (
         owner_id = auth.uid()
         OR responsible_member_id IN (
           SELECT id FROM public.tenant_members WHERE user_id = auth.uid()
         )
       )
  ) THEN
    RETURN jsonb_build_object(
      'ok',      false,
      'error',   'forbidden',
      'message', 'Sem permissão para excluir este paciente.'
    );
  END IF;

  DELETE FROM public.patients WHERE id = p_patient_id;

  RETURN jsonb_build_object('ok', true);
END;
$$;


ALTER FUNCTION public.safe_delete_patient(p_patient_id uuid) OWNER TO supabase_admin;

--
-- Name: sanitize_phone_br(text); Type: FUNCTION; Schema: public; Owner: supabase_admin
--

CREATE FUNCTION public.sanitize_phone_br(raw_phone text) RETURNS text
