-- ==========================================================================
-- Agencia PSI — Migracao: Heartbeat de conexao WhatsApp (Grupo 6.1)
-- ==========================================================================
-- Criado por: Leonardo Nohama
-- Data: 2026-04-23 · Sao Carlos/SP — Brasil
--
-- Contexto: notification_channels ja tem connection_status e last_health_check,
-- mas nao ha tracking de incidents (quando conexao caiu, quanto tempo ficou
-- fora, se ja alertou o admin). Criamos tabela de incidents + helpers RPC.
--
-- Modelo:
--   - notification_channels.connection_status (ja existe) = estado atual
--   - notification_channels.last_health_check (ja existe) = ultima check
--   - notification_channels.metadata (ja existe) = config (threshold, toggles)
--
--   Novo:
--   - whatsapp_connection_incidents = eventos de degradacao (open/close)
--   - RPC open_incident (idempotente — ignora se ja tem aberto)
--   - RPC resolve_open_incidents (fecha todos abertos pro channel)
--
-- Fluxo esperado (edge function whatsapp-heartbeat-check):
--   1. Lista channels evolution_api ativos
--   2. Pra cada: GET /instance/connectionState/{instance}
--   3. Atualiza connection_status + last_health_check
--   4. Se state != 'open' por > threshold minutos → open_incident (se nao tem)
--      e dispara notificacao pros admins do tenant
--   5. Se state == 'open' → resolve_open_incidents (se tiver aberto)
-- ==========================================================================

-- ---------------------------------------------------------------------------
-- Tabela: whatsapp_connection_incidents
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.whatsapp_connection_incidents (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
    channel_id UUID NOT NULL REFERENCES public.notification_channels(id) ON DELETE CASCADE,
    provider TEXT NOT NULL CHECK (provider IN ('evolution_api', 'twilio')),

    -- Tipo do incident (snapshot do connection_status que abriu)
    kind TEXT NOT NULL CHECK (kind IN ('disconnected', 'error', 'qr_pending', 'connecting', 'unknown')),

    -- Snapshots pra auditoria
    last_state TEXT,              -- estado quando abriu
    details JSONB,                -- payload bruto do provider

    started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    resolved_at TIMESTAMPTZ,      -- NULL enquanto aberto

    -- Tempo caido (preenchido ao resolver)
    duration_seconds INT,

    -- Controle de notificacao (anti-spam)
    notified_at TIMESTAMPTZ,      -- quando enviou notificacao pros admins
    notification_count INT NOT NULL DEFAULT 0,

    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Trigger updated_at
DROP TRIGGER IF EXISTS trg_wa_incidents_updated_at ON public.whatsapp_connection_incidents;
CREATE TRIGGER trg_wa_incidents_updated_at
    BEFORE UPDATE ON public.whatsapp_connection_incidents
    FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();

-- Apenas 1 incident aberto por channel (constraint de negocio)
CREATE UNIQUE INDEX IF NOT EXISTS uq_wa_incidents_open_per_channel
    ON public.whatsapp_connection_incidents (channel_id)
    WHERE resolved_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_wa_incidents_tenant_started
    ON public.whatsapp_connection_incidents (tenant_id, started_at DESC);

CREATE INDEX IF NOT EXISTS idx_wa_incidents_open
    ON public.whatsapp_connection_incidents (resolved_at)
    WHERE resolved_at IS NULL;

COMMENT ON TABLE public.whatsapp_connection_incidents IS
    'Eventos de degradacao de conexao WhatsApp (evolution/twilio). Max 1 aberto por channel (UNIQUE parcial).';


-- ---------------------------------------------------------------------------
-- RPC: whatsapp_heartbeat_open_incident
-- Idempotente — se ja tem incident aberto no channel, retorna o existente.
-- ---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION public.whatsapp_heartbeat_open_incident(
    p_channel_id UUID,
    p_kind TEXT,
    p_last_state TEXT DEFAULT NULL,
    p_details JSONB DEFAULT NULL
)
RETURNS UUID
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_tenant_id UUID;
    v_provider TEXT;
    v_existing_id UUID;
    v_new_id UUID;
BEGIN
    -- Busca tenant/provider do channel
    SELECT tenant_id, provider INTO v_tenant_id, v_provider
        FROM public.notification_channels
        WHERE id = p_channel_id
          AND deleted_at IS NULL;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'channel_not_found';
    END IF;

    IF p_kind NOT IN ('disconnected', 'error', 'qr_pending', 'connecting', 'unknown') THEN
        RAISE EXCEPTION 'invalid_kind: %', p_kind;
    END IF;

    -- Ja tem aberto? Retorna o mesmo id (idempotente)
    SELECT id INTO v_existing_id
        FROM public.whatsapp_connection_incidents
        WHERE channel_id = p_channel_id
          AND resolved_at IS NULL;

    IF FOUND THEN
        -- Atualiza o incident existente com detalhes frescos
        UPDATE public.whatsapp_connection_incidents
            SET last_state = COALESCE(p_last_state, last_state),
                details = COALESCE(p_details, details),
                kind = p_kind  -- pode mudar de qr_pending → disconnected, por ex
            WHERE id = v_existing_id;
        RETURN v_existing_id;
    END IF;

    -- Abre novo
    INSERT INTO public.whatsapp_connection_incidents
        (tenant_id, channel_id, provider, kind, last_state, details)
    VALUES
        (v_tenant_id, p_channel_id, v_provider, p_kind, p_last_state, p_details)
    RETURNING id INTO v_new_id;

    RETURN v_new_id;
END;
$$;

REVOKE ALL ON FUNCTION public.whatsapp_heartbeat_open_incident(UUID, TEXT, TEXT, JSONB) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.whatsapp_heartbeat_open_incident(UUID, TEXT, TEXT, JSONB) TO service_role;


-- ---------------------------------------------------------------------------
-- RPC: whatsapp_heartbeat_resolve_open_incidents
-- Fecha todos os incidents abertos de um channel. Retorna quantos fechou.
-- ---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION public.whatsapp_heartbeat_resolve_open_incidents(
    p_channel_id UUID
)
RETURNS INT
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_count INT := 0;
BEGIN
    UPDATE public.whatsapp_connection_incidents
        SET resolved_at = now(),
            duration_seconds = EXTRACT(EPOCH FROM (now() - started_at))::INT
        WHERE channel_id = p_channel_id
          AND resolved_at IS NULL;

    GET DIAGNOSTICS v_count = ROW_COUNT;
    RETURN v_count;
END;
$$;

REVOKE ALL ON FUNCTION public.whatsapp_heartbeat_resolve_open_incidents(UUID) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.whatsapp_heartbeat_resolve_open_incidents(UUID) TO service_role;


-- ---------------------------------------------------------------------------
-- RPC: whatsapp_heartbeat_mark_notified
-- Marca incident como notificado (anti-spam de alertas).
-- ---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION public.whatsapp_heartbeat_mark_notified(
    p_incident_id UUID
)
RETURNS VOID
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
BEGIN
    UPDATE public.whatsapp_connection_incidents
        SET notified_at = now(),
            notification_count = notification_count + 1
        WHERE id = p_incident_id;
END;
$$;

REVOKE ALL ON FUNCTION public.whatsapp_heartbeat_mark_notified(UUID) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.whatsapp_heartbeat_mark_notified(UUID) TO service_role;


-- ---------------------------------------------------------------------------
-- RLS
-- ---------------------------------------------------------------------------
ALTER TABLE public.whatsapp_connection_incidents ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS "wa_incidents: select membros/admin" ON public.whatsapp_connection_incidents;
CREATE POLICY "wa_incidents: select membros/admin"
    ON public.whatsapp_connection_incidents
    FOR SELECT
    TO authenticated
    USING (
        public.is_saas_admin()
        OR EXISTS (
            SELECT 1 FROM public.tenant_members tm
            WHERE tm.tenant_id = whatsapp_connection_incidents.tenant_id
              AND tm.user_id = auth.uid()
              AND tm.status = 'active'
        )
    );

-- Write apenas via service_role (edge function cron)
DROP POLICY IF EXISTS "wa_incidents: write service_role" ON public.whatsapp_connection_incidents;
CREATE POLICY "wa_incidents: write service_role"
    ON public.whatsapp_connection_incidents
    FOR ALL
    TO service_role
    USING (true)
    WITH CHECK (true);


-- ---------------------------------------------------------------------------
-- Expandir notifications.type pra aceitar 'system_alert' (usado por heartbeat)
-- ---------------------------------------------------------------------------
ALTER TABLE public.notifications
    DROP CONSTRAINT IF EXISTS notifications_type_check;

ALTER TABLE public.notifications
    ADD CONSTRAINT notifications_type_check
    CHECK (type = ANY (ARRAY[
        'new_scheduling'::text,
        'new_patient'::text,
        'recurrence_alert'::text,
        'session_status'::text,
        'inbound_message'::text,
        'system_alert'::text
    ]));


-- ---------------------------------------------------------------------------
-- Cron job (TEMPLATE — descomentar pra ativar)
-- ---------------------------------------------------------------------------
-- Checa heartbeat de todos os tenants Evolution ativos a cada 2 minutos.
-- Threshold de minutos fora do ar antes de abrir incident fica em
-- notification_channels.metadata.heartbeat_threshold_minutes (default 5).
--
-- SELECT cron.schedule(
--     'whatsapp-heartbeat-every-2min',
--     '*/2 * * * *',
--     $$
--     SELECT net.http_post(
--         url := current_setting('app.settings.supabase_url') || '/functions/v1/whatsapp-heartbeat-check',
--         headers := jsonb_build_object(
--             'Authorization', 'Bearer ' || current_setting('app.settings.service_role_key'),
--             'Content-Type', 'application/json'
--         ),
--         body := '{}'::jsonb
--     );
--     $$
-- );
--
-- Desativar: SELECT cron.unschedule('whatsapp-heartbeat-every-2min');
