-- =============================================================================
-- F6.2 Lote C — split de notifications (tenant-local vs SaaS cross-tenant)
--
-- ⚠️ APLICAR COMO supabase_admin (CREATE OR REPLACE de funções owned por
--    postgres E supabase_admin; superuser preserva o owner):
--   docker exec -i -e PGPASSWORD=postgres supabase_db_agenciapsi-primesakai \
--     psql -U supabase_admin -h 127.0.0.1 -d postgres -v ON_ERROR_STOP=1 \
--     < database-novo/manual/f6_2c_notifications_split.supabase_admin.sql
--
-- Neste projeto, TODAS as notificações atuais (inbound_message, session_status,
-- system_alert, new_patient) são tenant-LOCAIS (avisos cross-tenant do SaaS
-- vivem em global_notices). Então:
--   * notifications continua tenant-local → já vive no schema do tenant (F6.1)
--   * public.notifications_sistema é criado como o canal SaaS→tenant / dev
--     cross-tenant (vazio hoje; pronto pro futuro: suporte, billing, etc.)
-- Triggers de notif reescritos schema-aware; os que disparam em tabelas PUBLIC
-- (notify_on_intake, notify_on_scheduling) roteiam pro schema via EXECUTE format.
-- =============================================================================

BEGIN;

-- ───────────────────────────────────────────────────────────────────────────
-- 1) notifications_sistema (GLOBAL, cross-tenant)
-- ───────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.notifications_sistema (
    id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    owner_id    uuid NOT NULL,                       -- destinatário (user do tenant OU dev)
    tenant_id   uuid REFERENCES public.tenants(id) ON DELETE CASCADE,  -- contexto (nullable: alerta global)
    type        text NOT NULL,
    ref_id      uuid,
    ref_table   text,
    payload     jsonb,
    read_at     timestamptz,
    archived    boolean NOT NULL DEFAULT false,
    created_at  timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS notif_sistema_owner_idx ON public.notifications_sistema (owner_id, created_at DESC) WHERE archived = false;

ALTER TABLE public.notifications_sistema ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS notif_sistema_owner ON public.notifications_sistema;
CREATE POLICY notif_sistema_owner ON public.notifications_sistema
    FOR ALL TO authenticated USING (owner_id = auth.uid()) WITH CHECK (owner_id = auth.uid());

-- realtime
DO $$ BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_publication_tables WHERE pubname='supabase_realtime' AND schemaname='public' AND tablename='notifications_sistema') THEN
        ALTER PUBLICATION supabase_realtime ADD TABLE public.notifications_sistema;
    END IF;
END $$;

-- helper pro futuro: emite notificação cross-tenant (dev/SaaS -> destinatário)
CREATE OR REPLACE FUNCTION public.notify_user_sistema(
    p_owner_id uuid, p_type text, p_payload jsonb,
    p_tenant_id uuid DEFAULT NULL, p_ref_id uuid DEFAULT NULL, p_ref_table text DEFAULT NULL)
RETURNS uuid LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE v_id uuid;
BEGIN
    INSERT INTO public.notifications_sistema (owner_id, tenant_id, type, ref_id, ref_table, payload)
    VALUES (p_owner_id, p_tenant_id, p_type, p_ref_id, p_ref_table, p_payload)
    RETURNING id INTO v_id;
    RETURN v_id;
END $$;

-- ───────────────────────────────────────────────────────────────────────────
-- 2) Rewrites dos triggers de notif (tenant-local) — schema-aware
-- ───────────────────────────────────────────────────────────────────────────

CREATE OR REPLACE FUNCTION public.notify_on_session_status()
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE v_nome text;
BEGIN
    IF NEW.status IN ('faltou','cancelado') AND OLD.status IS DISTINCT FROM NEW.status THEN
        PERFORM set_config('search_path', TG_TABLE_SCHEMA || ',public,pg_temp', true);
        SELECT nome_completo INTO v_nome FROM patients WHERE id = NEW.patient_id LIMIT 1;
        INSERT INTO notifications (owner_id, type, ref_id, ref_table, payload)
        VALUES (NEW.owner_id, 'session_status', NEW.id, 'agenda_eventos',
            jsonb_build_object(
                'title', CASE WHEN NEW.status='faltou' THEN 'Paciente faltou' ELSE 'Sessão cancelada' END,
                'detail', COALESCE(v_nome,'Paciente') || ' — ' || to_char(NEW.inicio_em,'DD/MM HH24:MI'),
                'deeplink', '/therapist/agenda',
                'avatar_initials', upper(left(COALESCE(v_nome,'?'),2))));
    END IF;
    RETURN NEW;
END $$;

CREATE OR REPLACE FUNCTION public.fanout_inbound_message_to_notifications()
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE
    v_target_user uuid; v_title text; v_detail text; v_initials text; v_deeplink text;
    v_patient_name text; v_payload jsonb; v_tenant uuid;
BEGIN
    IF NEW.direction <> 'inbound' THEN RETURN NEW; END IF;
    v_tenant := public.tenant_id_for_schema(TG_TABLE_SCHEMA);
    PERFORM set_config('search_path', TG_TABLE_SCHEMA || ',public,pg_temp', true);

    IF NEW.patient_id IS NOT NULL THEN
        SELECT nome_completo INTO v_patient_name FROM patients WHERE id = NEW.patient_id;
    END IF;
    v_title := COALESCE(v_patient_name, NEW.from_number, 'Desconhecido');
    v_detail := COALESCE(left(NEW.body, 100), '[mensagem sem texto]');
    IF v_patient_name IS NOT NULL THEN
        v_initials := upper(left(v_patient_name,1)) || COALESCE(upper(left(split_part(v_patient_name,' ',2),1)),'');
    ELSE v_initials := '?'; END IF;
    v_deeplink := '/admin/conversas';
    v_payload := jsonb_build_object('title',v_title,'detail',v_detail,'avatar_initials',v_initials,
        'deeplink',v_deeplink,'channel',NEW.channel,'conversation_message_id',NEW.id,
        'patient_id',NEW.patient_id,'from_number',NEW.from_number);

    -- destinatário: responsável do paciente (tenant_members é GLOBAL)
    IF NEW.patient_id IS NOT NULL THEN
        SELECT tm.user_id INTO v_target_user
        FROM patients p JOIN public.tenant_members tm ON tm.id = p.responsible_member_id
        WHERE p.id = NEW.patient_id AND tm.status = 'active' LIMIT 1;
        IF v_target_user IS NOT NULL THEN
            INSERT INTO notifications (owner_id, type, ref_id, ref_table, payload)
            VALUES (v_target_user, 'inbound_message', NULL, 'conversation_messages', v_payload);
            RETURN NEW;
        END IF;
    END IF;
    -- fallback: fan-out pros admins/therapists ativos do tenant (global)
    INSERT INTO notifications (owner_id, type, ref_id, ref_table, payload)
    SELECT tm.user_id, 'inbound_message', NULL, 'conversation_messages', v_payload
    FROM public.tenant_members tm
    WHERE tm.tenant_id = v_tenant AND tm.status = 'active'
      AND tm.role IN ('clinic_admin','tenant_admin','therapist');
    RETURN NEW;
END $$;

-- helper de cancelamento: notification_queue é tenant; herda search_path do trigger chamador
CREATE OR REPLACE FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text DEFAULT NULL, p_evento_id uuid DEFAULT NULL)
RETURNS integer LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE v_canceled integer;
BEGIN
    UPDATE notification_queue SET status='cancelado', updated_at=now()
    WHERE patient_id = p_patient_id AND status IN ('pendente','processando')
      AND (p_channel IS NULL OR channel = p_channel)
      AND (p_evento_id IS NULL OR agenda_evento_id = p_evento_id);
    GET DIAGNOSTICS v_canceled = ROW_COUNT;
    RETURN v_canceled;
END $$;

CREATE OR REPLACE FUNCTION public.cancel_notifications_on_opt_out()
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
BEGIN
    PERFORM set_config('search_path', TG_TABLE_SCHEMA || ',public,pg_temp', true);
    IF OLD.whatsapp_opt_in = true AND NEW.whatsapp_opt_in = false THEN
        PERFORM public.cancel_patient_pending_notifications(NEW.patient_id, 'whatsapp');
    END IF;
    IF OLD.email_opt_in = true AND NEW.email_opt_in = false THEN
        PERFORM public.cancel_patient_pending_notifications(NEW.patient_id, 'email');
    END IF;
    IF OLD.sms_opt_in = true AND NEW.sms_opt_in = false THEN
        PERFORM public.cancel_patient_pending_notifications(NEW.patient_id, 'sms');
    END IF;
    RETURN NEW;
END $$;

CREATE OR REPLACE FUNCTION public.cancel_notifications_on_session_cancel()
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
BEGIN
    IF NEW.status = 'cancelado' AND OLD.status <> 'cancelado' THEN
        PERFORM set_config('search_path', TG_TABLE_SCHEMA || ',public,pg_temp', true);
        PERFORM public.cancel_patient_pending_notifications(NEW.patient_id, NULL, NEW.id);
    END IF;
    RETURN NEW;
END $$;

-- ───────────────────────────────────────────────────────────────────────────
-- 3) Triggers que disparam em tabelas PUBLIC (intake/scheduling, F1b) —
--    roteiam a notificação pro schema do tenant via EXECUTE format
-- ───────────────────────────────────────────────────────────────────────────

CREATE OR REPLACE FUNCTION public.notify_on_intake()
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE v_schema text;
BEGIN
    IF NEW.status = 'new' THEN
        v_schema := public.tenant_schema_for(NEW.tenant_id);
        IF v_schema IS NULL THEN RETURN NEW; END IF;
        EXECUTE format('INSERT INTO %I.notifications (owner_id, type, ref_id, ref_table, payload) VALUES ($1,$2,$3,$4,$5)', v_schema)
        USING NEW.owner_id, 'new_patient', NEW.id, 'patient_intake_requests',
            jsonb_build_object('title','Novo cadastro externo','detail',COALESCE(NEW.nome_completo,'Paciente'),
                'deeplink','/therapist/patients/cadastro/recebidos','avatar_initials',upper(left(COALESCE(NEW.nome_completo,'?'),2)));
    END IF;
    RETURN NEW;
END $$;

CREATE OR REPLACE FUNCTION public.notify_on_scheduling()
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE v_schema text;
BEGIN
    IF NEW.status = 'pendente' THEN
        v_schema := public.tenant_schema_for(NEW.tenant_id);
        IF v_schema IS NULL THEN RETURN NEW; END IF;
        EXECUTE format('INSERT INTO %I.notifications (owner_id, type, ref_id, ref_table, payload) VALUES ($1,$2,$3,$4,$5)', v_schema)
        USING NEW.owner_id, 'new_scheduling', NEW.id, 'agendador_solicitacoes',
            jsonb_build_object('title','Nova solicitação de agendamento',
                'detail', COALESCE(NEW.paciente_nome,'Paciente') || ' ' || COALESCE(NEW.paciente_sobrenome,'') || ' — ' || COALESCE(NEW.tipo,''),
                'deeplink','/therapist/agendamentos-recebidos',
                'avatar_initials', upper(left(COALESCE(NEW.paciente_nome,'?'),1) || left(COALESCE(NEW.paciente_sobrenome,''),1)));
    END IF;
    RETURN NEW;
END $$;

-- ───────────────────────────────────────────────────────────────────────────
-- 4) Detach dos notif-triggers tenant de public + attach nos schemas (estende
--    attach_schema_aware_triggers com os 5 triggers de notif tenant)
-- ───────────────────────────────────────────────────────────────────────────
DO $$
DECLARE
    aware text[] := ARRAY['notify_on_session_status','fanout_inbound_message_to_notifications',
                          'cancel_notifications_on_opt_out','cancel_notifications_on_session_cancel'];
    r record;
BEGIN
    FOR r IN
        SELECT c.relname AS tab, t.tgname FROM pg_trigger t JOIN pg_class c ON c.oid=t.tgrelid
        JOIN pg_namespace n ON n.oid=c.relnamespace JOIN pg_proc p ON p.oid=t.tgfoid
        WHERE n.nspname='public' AND NOT t.tgisinternal AND p.proname = ANY(aware)
          AND c.relname IN (SELECT table_name FROM information_schema.tables WHERE table_schema='_tenant_template' AND table_type='BASE TABLE')
    LOOP
        EXECUTE format('DROP TRIGGER IF EXISTS %I ON public.%I', r.tgname, r.tab);
    END LOOP;
END $$;

CREATE OR REPLACE FUNCTION public.attach_notif_triggers(p_schema text)
RETURNS int LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE
    specs jsonb := jsonb_build_array(
        jsonb_build_object('tab','agenda_eventos','name','trg_notify_on_session_status','spec','AFTER UPDATE OF status ON __T__ FOR EACH ROW EXECUTE FUNCTION public.notify_on_session_status()'),
        jsonb_build_object('tab','agenda_eventos','name','trg_cancel_notifs_on_session_cancel','spec','AFTER UPDATE ON __T__ FOR EACH ROW WHEN (new.status IS DISTINCT FROM old.status) EXECUTE FUNCTION public.cancel_notifications_on_session_cancel()'),
        jsonb_build_object('tab','agenda_eventos','name','trg_agenda_status_notify','spec','AFTER UPDATE OF status ON __T__ FOR EACH ROW EXECUTE FUNCTION public.fn_notify_agenda_status_change()'),
        jsonb_build_object('tab','conversation_messages','name','trg_fanout_inbound_to_notifications','spec','AFTER INSERT ON __T__ FOR EACH ROW EXECUTE FUNCTION public.fanout_inbound_message_to_notifications()'),
        jsonb_build_object('tab','notification_preferences','name','trg_cancel_notifs_on_opt_out','spec','AFTER UPDATE ON __T__ FOR EACH ROW EXECUTE FUNCTION public.cancel_notifications_on_opt_out()')
    );
    el jsonb; v_count int := 0; v_target text;
BEGIN
    IF p_schema NOT LIKE 'tenant\_%' THEN RAISE EXCEPTION 'schema inválido %', p_schema; END IF;
    FOR el IN SELECT * FROM jsonb_array_elements(specs) LOOP
        IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=p_schema AND table_name=(el->>'tab')) THEN CONTINUE; END IF;
        v_target := format('%I.%I', p_schema, el->>'tab');
        EXECUTE format('DROP TRIGGER IF EXISTS %I ON %s', el->>'name', v_target);
        EXECUTE 'CREATE TRIGGER ' || quote_ident(el->>'name') || ' ' || replace(el->>'spec','__T__',v_target);
        v_count := v_count + 1;
    END LOOP;
    RETURN v_count;
END $$;

DO $$
DECLARE r record; v int;
BEGIN
    FOR r IN SELECT schema_name FROM public.tenant_schemas ORDER BY schema_name LOOP
        v := public.attach_notif_triggers(r.schema_name);
        RAISE NOTICE 'F6.2C %: % notif triggers', r.schema_name, v;
    END LOOP;
END $$;

COMMIT;
