-- =============================================================================
-- F6.4 — RPCs SaaS-admin: defaults do sistema (template + fan-out) e views
--        cross-tenant (fan-out). Destrava o F6.3 (páginas SaaS deixam de ler
--        public.<tabela_tenant>).
--
-- ⚠️ APLICAR COMO supabase_admin.
--
-- Defaults (feriados nacionais, notification_templates is_default): editados
-- pelo SaaS no _tenant_template (fonte da verdade, propaga p/ tenants NOVOS no
-- clone) E fan-out pros schemas EXISTENTES. Só toca cópias-default (owner_id
-- NULL / is_default), preserva overrides do tenant (owner_id próprio).
-- Todas gated por is_saas_admin().
-- =============================================================================

BEGIN;

CREATE OR REPLACE FUNCTION public._assert_saas_admin()
RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$ BEGIN
    IF NOT public.is_saas_admin() THEN RAISE EXCEPTION 'Apenas SaaS admin' USING ERRCODE='42501'; END IF;
END $$;

-- ── FERIADOS (defaults nacionais) ───────────────────────────────────────────
CREATE OR REPLACE FUNCTION public.saas_list_default_feriados(p_ano integer)
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE v jsonb;
BEGIN
    PERFORM public._assert_saas_admin();
    SELECT COALESCE(jsonb_agg(jsonb_build_object('id',id,'data',data,'nome',nome,'tipo',tipo,'bloqueia_sessoes',bloqueia_sessoes) ORDER BY data),'[]'::jsonb)
    INTO v FROM _tenant_template.feriados WHERE EXTRACT(YEAR FROM data) = p_ano;
    RETURN v;
END $$;

CREATE OR REPLACE FUNCTION public.saas_add_default_feriado(p_data date, p_nome text, p_tipo text DEFAULT 'municipal')
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE t record; v_owner uuid := auth.uid();
BEGIN
    PERFORM public._assert_saas_admin();
    INSERT INTO _tenant_template.feriados (owner_id, tipo, nome, data, bloqueia_sessoes)
    VALUES (v_owner, p_tipo, p_nome, p_data, false) ON CONFLICT (data, nome) DO NOTHING;
    FOR t IN SELECT schema_name FROM public.tenant_schemas LOOP
        EXECUTE format('INSERT INTO %I.feriados (owner_id, tipo, nome, data, bloqueia_sessoes) VALUES ($1,$2,$3,$4,false) ON CONFLICT (data, nome) DO NOTHING', t.schema_name)
        USING v_owner, p_tipo, p_nome, p_data;
    END LOOP;
    RETURN jsonb_build_object('data', p_data, 'nome', p_nome, 'tipo', p_tipo);
END $$;

CREATE OR REPLACE FUNCTION public.saas_remove_default_feriado(p_data date, p_nome text)
RETURNS integer LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE t record; v_n int := 0;
BEGIN
    PERFORM public._assert_saas_admin();
    DELETE FROM _tenant_template.feriados WHERE data = p_data AND nome = p_nome;
    FOR t IN SELECT schema_name FROM public.tenant_schemas LOOP
        EXECUTE format('DELETE FROM %I.feriados WHERE data = $1 AND nome = $2', t.schema_name) USING p_data, p_nome;
        v_n := v_n + 1;
    END LOOP;
    RETURN v_n;
END $$;

-- ── NOTIFICATION_TEMPLATES (defaults) ───────────────────────────────────────
CREATE OR REPLACE FUNCTION public.saas_list_default_notif_templates()
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE v jsonb;
BEGIN
    PERFORM public._assert_saas_admin();
    SELECT COALESCE(jsonb_agg(to_jsonb(nt) ORDER BY nt.domain, nt.event_type),'[]'::jsonb)
    INTO v FROM _tenant_template.notification_templates nt WHERE nt.is_default = true AND nt.deleted_at IS NULL;
    RETURN v;
END $$;

-- upsert por key (defaults têm owner_id NULL). Cria/atualiza no template + schemas.
CREATE OR REPLACE FUNCTION public.saas_upsert_default_notif_template(p_payload jsonb)
RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE t record; v_key text := p_payload->>'key'; v_exists boolean;
        v_domain text := p_payload->>'domain'; v_channel text := p_payload->>'channel';
        v_event text := p_payload->>'event_type'; v_body text := p_payload->>'body_text';
        v_vars jsonb := COALESCE(p_payload->'variables','[]'::jsonb);
        v_active boolean := COALESCE((p_payload->>'is_active')::boolean, true);
BEGIN
    PERFORM public._assert_saas_admin();
    IF v_key IS NULL THEN RAISE EXCEPTION 'key obrigatório'; END IF;
    -- template
    SELECT EXISTS(SELECT 1 FROM _tenant_template.notification_templates WHERE key=v_key AND owner_id IS NULL AND is_default=true) INTO v_exists;
    IF v_exists THEN
        UPDATE _tenant_template.notification_templates SET body_text=v_body, domain=v_domain, channel=v_channel,
            event_type=v_event, variables=v_vars, is_active=v_active WHERE key=v_key AND owner_id IS NULL AND is_default=true;
    ELSE
        INSERT INTO _tenant_template.notification_templates (owner_id, key, domain, channel, event_type, body_text, variables, is_default, is_active)
        VALUES (NULL, v_key, v_domain, v_channel, v_event, v_body, v_vars, true, v_active);
    END IF;
    -- fan-out schemas (só a cópia-default; preserva overrides do tenant owner_id<>NULL)
    FOR t IN SELECT schema_name FROM public.tenant_schemas LOOP
        EXECUTE format(
            'INSERT INTO %I.notification_templates (owner_id, key, domain, channel, event_type, body_text, variables, is_default, is_active) '
            || 'VALUES (NULL,$1,$2,$3,$4,$5,$6,true,$7) '
            || 'ON CONFLICT (owner_id, key, deleted_at) DO UPDATE SET body_text=EXCLUDED.body_text, domain=EXCLUDED.domain, '
            || 'channel=EXCLUDED.channel, event_type=EXCLUDED.event_type, variables=EXCLUDED.variables, is_active=EXCLUDED.is_active',
            t.schema_name) USING v_key, v_domain, v_channel, v_event, v_body, v_vars, v_active;
    END LOOP;
END $$;

CREATE OR REPLACE FUNCTION public.saas_set_default_notif_template_active(p_key text, p_active boolean)
RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE t record;
BEGIN
    PERFORM public._assert_saas_admin();
    UPDATE _tenant_template.notification_templates SET is_active=p_active WHERE key=p_key AND owner_id IS NULL AND is_default=true;
    FOR t IN SELECT schema_name FROM public.tenant_schemas LOOP
        EXECUTE format('UPDATE %I.notification_templates SET is_active=$1 WHERE key=$2 AND owner_id IS NULL AND is_default=true', t.schema_name) USING p_active, p_key;
    END LOOP;
END $$;

CREATE OR REPLACE FUNCTION public.saas_delete_default_notif_template(p_key text)
RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE t record;
BEGIN
    PERFORM public._assert_saas_admin();
    UPDATE _tenant_template.notification_templates SET deleted_at=now() WHERE key=p_key AND owner_id IS NULL AND is_default=true;
    FOR t IN SELECT schema_name FROM public.tenant_schemas LOOP
        EXECUTE format('UPDATE %I.notification_templates SET deleted_at=now() WHERE key=$1 AND owner_id IS NULL AND is_default=true', t.schema_name) USING p_key;
    END LOOP;
END $$;

-- quantos tenants têm override (tenant_id<>NULL no modelo antigo = owner_id<>NULL aqui) por key
CREATE OR REPLACE FUNCTION public.saas_count_notif_template_overrides(p_key text)
RETURNS integer LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE t record; v_n int := 0; v_has int;
BEGIN
    PERFORM public._assert_saas_admin();
    FOR t IN SELECT schema_name FROM public.tenant_schemas LOOP
        EXECUTE format('SELECT count(*) FROM %I.notification_templates WHERE key=$1 AND owner_id IS NOT NULL AND is_active=true AND deleted_at IS NULL', t.schema_name) INTO v_has USING p_key;
        v_n := v_n + v_has;
    END LOOP;
    RETURN v_n;
END $$;

-- ── WHATSAPP admin (cross-tenant) — substitui v_twilio_whatsapp_overview ─────
CREATE OR REPLACE FUNCTION public.saas_list_all_whatsapp_channels()
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
AS $$
DECLARE t record; v_rows jsonb := '[]'::jsonb; v_part jsonb;
BEGIN
    PERFORM public._assert_saas_admin();
    FOR t IN SELECT ts.tenant_id, ts.schema_name, tn.name AS tenant_name
             FROM public.tenant_schemas ts JOIN public.tenants tn ON tn.id = ts.tenant_id LOOP
        EXECUTE format(
            'SELECT COALESCE(jsonb_agg(jsonb_build_object('
            || '''id'',nc.id, ''tenant_id'',$1::uuid, ''tenant_name'',$2::text, ''owner_id'',nc.owner_id,'
            || '''provider'',nc.provider, ''is_active'',nc.is_active, ''connection_status'',nc.connection_status,'
            || '''sender_address'',nc.sender_address, ''twilio_phone_number'',nc.twilio_phone_number,'
            || '''last_health_check'',nc.last_health_check,'
            || '''open_incident'',(SELECT i.kind FROM %1$I.whatsapp_connection_incidents i WHERE i.channel_id=nc.id AND i.resolved_at IS NULL LIMIT 1)'
            || ')),''[]''::jsonb) FROM %1$I.notification_channels nc WHERE nc.channel=''whatsapp'' AND nc.deleted_at IS NULL',
            t.schema_name) INTO v_part USING t.tenant_id, t.tenant_name;
        v_rows := v_rows || v_part;
    END LOOP;
    RETURN v_rows;
END $$;

-- grants: gated por is_saas_admin internamente, mas exposto a authenticated
DO $g$ DECLARE fn text; BEGIN
    FOREACH fn IN ARRAY ARRAY[
        'saas_list_default_feriados(integer)','saas_add_default_feriado(date,text,text)','saas_remove_default_feriado(date,text)',
        'saas_list_default_notif_templates()','saas_upsert_default_notif_template(jsonb)',
        'saas_set_default_notif_template_active(text,boolean)','saas_delete_default_notif_template(text)',
        'saas_count_notif_template_overrides(text)','saas_list_all_whatsapp_channels()'
    ] LOOP
        EXECUTE format('GRANT EXECUTE ON FUNCTION public.%s TO authenticated', fn);
    END LOOP;
END $g$;

COMMIT;
