-- ==========================================================================
-- Agencia PSI — Migracao: Atribuicao de conversa a terapeuta (CRM Grupo 3.2)
-- ==========================================================================
-- Criado por: Leonardo Nohama
-- Data: 2026-04-21 · Sao Carlos/SP — Brasil
--
-- Uma linha por (tenant_id, thread_key) — UPSERT em cada reatribuicao.
-- Historico (quem atribuiu pra quem e quando) pode ser adicionado depois
-- via trigger INSERT em conversation_assignment_history se virar requisito.
--
-- thread_key segue o padrao de conversation_threads:
--   - '<uuid>'       → thread de paciente conhecido
--   - 'anon:<phone>' → thread de numero nao identificado
--
-- RLS:
--   - SELECT: qualquer membro ativo do tenant
--   - INSERT/UPDATE: qualquer membro ativo do tenant (self-assign ou delegar)
--   - DELETE: nao permitido (unassign = UPDATE assigned_to=NULL)
-- ==========================================================================

CREATE TABLE IF NOT EXISTS public.conversation_assignments (
    tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
    thread_key TEXT NOT NULL,

    patient_id UUID REFERENCES public.patients(id) ON DELETE SET NULL,
    contact_number TEXT,

    assigned_to UUID REFERENCES auth.users(id) ON DELETE SET NULL,
    assigned_by UUID NOT NULL REFERENCES auth.users(id) ON DELETE SET NULL,
    assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(),

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

    PRIMARY KEY (tenant_id, thread_key)
);

CREATE INDEX IF NOT EXISTS idx_conv_assign_tenant_user
    ON public.conversation_assignments (tenant_id, assigned_to)
    WHERE assigned_to IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_conv_assign_patient
    ON public.conversation_assignments (patient_id)
    WHERE patient_id IS NOT NULL;

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

COMMENT ON TABLE public.conversation_assignments IS
    'Atribuicao de threads de conversa a membros do tenant. Uma linha por (tenant_id, thread_key). assigned_to=NULL significa nao atribuida.';

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

-- SELECT: qualquer membro ativo do tenant OU saas_admin
DROP POLICY IF EXISTS "conv_assign: select tenant" ON public.conversation_assignments;
CREATE POLICY "conv_assign: select tenant"
    ON public.conversation_assignments
    FOR SELECT
    TO authenticated
    USING (
        public.is_saas_admin()
        OR EXISTS (
            SELECT 1 FROM public.tenant_members tm
            WHERE tm.user_id = auth.uid()
              AND tm.tenant_id = conversation_assignments.tenant_id
              AND tm.status = 'active'
        )
    );

-- INSERT: membro ativo do tenant. assigned_by deve ser o proprio user.
-- assigned_to deve ser membro ativo do mesmo tenant (ou NULL).
DROP POLICY IF EXISTS "conv_assign: insert tenant" ON public.conversation_assignments;
CREATE POLICY "conv_assign: insert tenant"
    ON public.conversation_assignments
    FOR INSERT
    TO authenticated
    WITH CHECK (
        assigned_by = auth.uid()
        AND EXISTS (
            SELECT 1 FROM public.tenant_members tm
            WHERE tm.user_id = auth.uid()
              AND tm.tenant_id = conversation_assignments.tenant_id
              AND tm.status = 'active'
        )
        AND (
            assigned_to IS NULL
            OR EXISTS (
                SELECT 1 FROM public.tenant_members tm2
                WHERE tm2.user_id = conversation_assignments.assigned_to
                  AND tm2.tenant_id = conversation_assignments.tenant_id
                  AND tm2.status = 'active'
            )
        )
    );

-- UPDATE: membro ativo do tenant pode reatribuir. Mesma validacao pra assigned_to.
DROP POLICY IF EXISTS "conv_assign: update tenant" ON public.conversation_assignments;
CREATE POLICY "conv_assign: update tenant"
    ON public.conversation_assignments
    FOR UPDATE
    TO authenticated
    USING (
        EXISTS (
            SELECT 1 FROM public.tenant_members tm
            WHERE tm.user_id = auth.uid()
              AND tm.tenant_id = conversation_assignments.tenant_id
              AND tm.status = 'active'
        )
    )
    WITH CHECK (
        assigned_by = auth.uid()
        AND (
            assigned_to IS NULL
            OR EXISTS (
                SELECT 1 FROM public.tenant_members tm2
                WHERE tm2.user_id = conversation_assignments.assigned_to
                  AND tm2.tenant_id = conversation_assignments.tenant_id
                  AND tm2.status = 'active'
            )
        )
    );

-- DELETE: bloqueado (unassign = UPDATE assigned_to=NULL)

-- --------------------------------------------------------------------------
-- Atualiza view conversation_threads pra incluir assignment
-- --------------------------------------------------------------------------
DROP VIEW IF EXISTS public.conversation_threads CASCADE;

CREATE VIEW public.conversation_threads
WITH (security_invoker = true)
AS
WITH base AS (
    SELECT
        cm.id,
        cm.tenant_id,
        cm.patient_id,
        cm.channel,
        cm.body,
        cm.direction,
        cm.kanban_status,
        cm.read_at,
        cm.created_at,
        CASE WHEN cm.direction = 'inbound' THEN cm.from_number ELSE cm.to_number END AS contact_number,
        COALESCE(cm.patient_id::text, 'anon:' || COALESCE(
            CASE WHEN cm.direction = 'inbound' THEN cm.from_number ELSE cm.to_number END,
            'unknown'
        )) AS thread_key
    FROM public.conversation_messages cm
),
latest AS (
    SELECT DISTINCT ON (tenant_id, thread_key)
        tenant_id, thread_key, patient_id, channel, contact_number,
        body AS last_message_body,
        direction AS last_message_direction,
        kanban_status,
        created_at AS last_message_at
    FROM base
    ORDER BY tenant_id, thread_key, created_at DESC
),
counts AS (
    SELECT
        tenant_id, thread_key,
        COUNT(*) AS message_count,
        COUNT(*) FILTER (WHERE direction = 'inbound' AND read_at IS NULL) AS unread_count
    FROM base
    GROUP BY tenant_id, thread_key
)
SELECT
    l.tenant_id,
    l.thread_key,
    l.patient_id,
    p.nome_completo AS patient_name,
    l.contact_number,
    l.channel,
    c.message_count,
    c.unread_count,
    l.last_message_at,
    l.last_message_body,
    l.last_message_direction,
    l.kanban_status,
    ca.assigned_to,
    ca.assigned_at
FROM latest l
JOIN counts c ON c.tenant_id = l.tenant_id AND c.thread_key = l.thread_key
LEFT JOIN public.patients p ON p.id = l.patient_id
LEFT JOIN public.conversation_assignments ca
    ON ca.tenant_id = l.tenant_id AND ca.thread_key = l.thread_key;

COMMENT ON VIEW public.conversation_threads IS
    'Agregado de conversas por paciente ou numero anonimo. Base do Kanban. Inclui assignment atual do thread.';

GRANT SELECT ON public.conversation_threads TO authenticated;

-- ==========================================================================
-- FIM DA MIGRACAO
-- ==========================================================================
