<!--
|--------------------------------------------------------------------------
| Agência PSI — GlobalInboundNotifier
|--------------------------------------------------------------------------
| Componente global que escuta INSERTs de conversation_messages (direction
| inbound) e mostra card flutuante no canto inferior direito, além de tocar
| som opcional. Mesmo estando em outra tela, o usuário é avisado da nova
| mensagem e pode clicar "Abrir" pra ir direto à conversa.
|
| Monta uma vez em AppLayout.
|--------------------------------------------------------------------------
-->
<script setup>
import { ref, watch, onMounted, onUnmounted } from 'vue';
import { supabase } from '@/lib/supabase/client';
import { tenantDb } from '@/lib/supabase/tenantClient';
import { useTenantStore } from '@/stores/tenantStore';
import { useConversationDrawerStore } from '@/stores/conversationDrawerStore';
import { logEvent, logError } from '@/support/supportLogger';

const LOG_SRC = 'GlobalInboundNotifier';

const tenantStore = useTenantStore();
const drawerStore = useConversationDrawerStore();

const activeNotifs = ref([]); // array de popups ativos
let channel = null;

const SOUND_KEY = 'agenciapsi.inbound_sound_enabled';
const soundEnabled = ref(true);

function loadSoundPref() {
    try {
        soundEnabled.value = localStorage.getItem(SOUND_KEY) !== 'false';
    } catch { soundEnabled.value = true; }
}

function toggleSound() {
    soundEnabled.value = !soundEnabled.value;
    try {
        localStorage.setItem(SOUND_KEY, soundEnabled.value ? 'true' : 'false');
    } catch {}
}

function playBeep() {
    if (!soundEnabled.value) return;
    try {
        // WebAudio API — beep sintético de 2 tons curtos
        const ctx = new (window.AudioContext || window.webkitAudioContext)();
        const now = ctx.currentTime;
        function tone(freq, start, dur, vol = 0.15) {
            const osc = ctx.createOscillator();
            const gain = ctx.createGain();
            osc.type = 'sine';
            osc.frequency.value = freq;
            gain.gain.setValueAtTime(0, now + start);
            gain.gain.linearRampToValueAtTime(vol, now + start + 0.01);
            gain.gain.exponentialRampToValueAtTime(0.001, now + start + dur);
            osc.connect(gain).connect(ctx.destination);
            osc.start(now + start);
            osc.stop(now + start + dur);
        }
        tone(880, 0, 0.12);
        tone(1320, 0.14, 0.12);
        // Fecha o context após som
        setTimeout(() => { try { ctx.close(); } catch {} }, 500);
    } catch {
        // se WebAudio falhar (sem interação prévia, etc), apenas ignora
    }
}

function truncate(s, n = 80) {
    if (!s) return '';
    const str = String(s).replace(/\s+/g, ' ').trim();
    return str.length > n ? str.slice(0, n - 1) + '…' : str;
}

async function showNotif(msg) {
    logEvent(LOG_SRC, 'showNotif chamado', { id: msg.id, from: msg.from_number });
    // Se o drawer dessa thread já está aberto, não notifica (o user já vê)
    if (drawerStore.isOpen && drawerStore.messageBelongsToCurrentThread(msg)) {
        logEvent(LOG_SRC, 'suprimido: drawer aberto na thread', { id: msg.id });
        return;
    }
    // Se está na rota /conversas com aba visível, também não notifica (já tá no Kanban)
    const onConversasRoute = typeof window !== 'undefined' && String(window.location.pathname).includes('/conversas');
    if (onConversasRoute && document.visibilityState === 'visible') {
        logEvent(LOG_SRC, 'suprimido: na rota /conversas visível', { id: msg.id });
        return;
    }

    let name = msg.from_number || 'Desconhecido';
    if (msg.patient_id) {
        const { data } = await tenantDb().from('patients').select('nome_completo').eq('id', msg.patient_id).maybeSingle();
        if (data?.nome_completo) name = data.nome_completo;
    }

    const notif = {
        id: msg.id,
        name,
        body: msg.body || (msg.media_url ? '[mídia]' : ''),
        patient_id: msg.patient_id,
        from_number: msg.from_number,
        channel: msg.channel
    };

    activeNotifs.value.push(notif);
    logEvent(LOG_SRC, 'popup push', { total: activeNotifs.value.length });
    playBeep();

    setTimeout(() => dismiss(notif.id), 10000);
}

function dismiss(id) {
    activeNotifs.value = activeNotifs.value.filter(n => n.id !== id);
}

async function openNotif(notif) {
    dismiss(notif.id);
    if (notif.patient_id) {
        await drawerStore.openForPatient(notif.patient_id);
    } else {
        // thread anônima (número não vinculado a paciente)
        await drawerStore.openForThread({
            thread_key: `anon:${notif.from_number}`,
            tenant_id: tenantStore.activeTenantId,
            patient_id: null,
            patient_name: null,
            contact_number: notif.from_number,
            channel: notif.channel,
            message_count: 1,
            unread_count: 1,
            kanban_status: 'awaiting_us',
            last_message_at: new Date().toISOString()
        });
    }
}

function channelIcon(ch) {
    const map = { whatsapp: 'pi-whatsapp', sms: 'pi-comment', email: 'pi-envelope' };
    return map[ch] || 'pi-bell';
}

function subscribe() {
    const tenantId = tenantStore.activeTenantId;
    const tenantSchema = tenantStore.activeTenantSchema;
    if (!tenantId || !tenantSchema) {
        logEvent(LOG_SRC, 'subscribe skipped — sem tenant');
        return;
    }
    if (channel) supabase.removeChannel(channel);
    logEvent(LOG_SRC, 'subscribing', { tenantId });
    channel = supabase
        .channel(`global_inbound_${tenantId}_${Date.now()}`)
        .on(
            'postgres_changes',
            {
                event: 'INSERT',
                schema: tenantSchema,
                table: 'conversation_messages'
            },
            (payload) => {
                const m = payload.new;
                logEvent(LOG_SRC, 'INSERT recebido', {
                    id: m.id,
                    direction: m.direction,
                    from: m.from_number,
                    preview: m.body?.slice(0, 40)
                });
                if (m.direction !== 'inbound') {
                    logEvent(LOG_SRC, 'ignorando (não é inbound)', { direction: m.direction });
                    return;
                }
                showNotif(m);
            }
        )
        .subscribe((status) => {
            logEvent(LOG_SRC, 'channel status', { status });
        });
}

function unsubscribe() {
    if (channel) {
        supabase.removeChannel(channel);
        channel = null;
    }
}

onMounted(() => {
    loadSoundPref();
    subscribe();
});

onUnmounted(() => unsubscribe());

watch(() => tenantStore.activeTenantId, () => subscribe());

// expõe toggle pra quem quiser usar (ex: na aside do CRM)
defineExpose({ soundEnabled, toggleSound });
</script>

<template>
    <div class="global-notif-container" aria-live="polite">
        <TransitionGroup name="notif-slide" tag="div" class="flex flex-col gap-2">
            <div
                v-for="n in activeNotifs"
                :key="n.id"
                class="global-notif-card"
                role="alert"
            >
                <div class="notif-icon" :class="n.channel === 'whatsapp' ? 'text-emerald-600 bg-emerald-500/10' : 'text-blue-600 bg-blue-500/10'">
                    <i :class="['pi', channelIcon(n.channel)]" />
                </div>
                <div class="notif-body">
                    <div class="notif-header">
                        <span class="notif-name">{{ n.name }}</span>
                        <span class="notif-channel">{{ n.channel === 'whatsapp' ? 'WhatsApp' : n.channel }}</span>
                    </div>
                    <div class="notif-preview">{{ truncate(n.body, 80) }}</div>
                </div>
                <div class="notif-actions">
                    <Button label="Abrir" size="small" severity="success" class="!text-xs" @click="openNotif(n)" />
                    <button class="notif-close" title="Dispensar" @click="dismiss(n.id)">
                        <i class="pi pi-times" />
                    </button>
                </div>
            </div>
        </TransitionGroup>

        <!-- Toggle de som (mini, aparece se tem notificação ou pode ficar sempre visível) -->
        <button
            v-if="activeNotifs.length > 0"
            class="sound-toggle"
            :title="soundEnabled ? 'Som ativado (clique pra desativar)' : 'Som desativado'"
            @click="toggleSound"
        >
            <i :class="soundEnabled ? 'pi pi-volume-up' : 'pi pi-volume-off'" />
        </button>
    </div>
</template>

<style scoped>
.global-notif-container {
    position: fixed;
    bottom: 1.25rem;
    right: 1.25rem;
    z-index: 1000;
    pointer-events: none;
    width: min(380px, calc(100vw - 2rem));
}

.global-notif-card {
    pointer-events: auto;
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    background: var(--surface-card, #fff);
    border: 1px solid var(--surface-border, #e5e7eb);
    border-radius: 10px;
    padding: 0.75rem;
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15), 0 3px 10px rgba(0, 0, 0, 0.08);
}

.notif-icon {
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 50%;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    font-size: 1rem;
}

.notif-body {
    flex: 1;
    min-width: 0;
}

.notif-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
}

.notif-name {
    font-weight: 600;
    font-size: 0.875rem;
    color: var(--text-color, #111827);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.notif-channel {
    font-size: 0.65rem;
    padding: 0 0.35rem;
    background: var(--surface-ground, #f3f4f6);
    color: var(--text-color-secondary, #6b7280);
    border-radius: 999px;
    flex-shrink: 0;
}

.notif-preview {
    font-size: 0.8rem;
    color: var(--text-color-secondary, #6b7280);
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.notif-actions {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    align-items: center;
}

.notif-close {
    width: 1.75rem;
    height: 1.75rem;
    border: none;
    background: transparent;
    color: var(--text-color-secondary, #6b7280);
    border-radius: 50%;
    cursor: pointer;
    display: grid;
    place-items: center;
    font-size: 0.75rem;
    transition: background 0.15s;
}
.notif-close:hover {
    background: var(--surface-ground, #f3f4f6);
}

.sound-toggle {
    pointer-events: auto;
    margin-top: 0.5rem;
    align-self: flex-end;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    border: 1px solid var(--surface-border, #e5e7eb);
    background: var(--surface-card, #fff);
    color: var(--text-color-secondary, #6b7280);
    cursor: pointer;
    display: grid;
    place-items: center;
    font-size: 0.75rem;
}
.sound-toggle:hover {
    background: var(--surface-ground, #f3f4f6);
    color: var(--text-color, #111827);
}

/* Animação de entrada/saída */
.notif-slide-enter-active {
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.notif-slide-leave-active {
    transition: all 0.2s ease-in;
}
.notif-slide-enter-from {
    transform: translateX(400px);
    opacity: 0;
}
.notif-slide-leave-to {
    transform: translateX(400px);
    opacity: 0;
}
</style>
