<!--
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/components/notifications/NotificationItem.vue
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
-->
<script setup>
import { computed } from 'vue';
import { tenantDb } from '@/lib/supabase/tenantClient';
import { useRouter, useRoute } from 'vue-router';
import { formatDistanceToNow } from 'date-fns';
import { ptBR } from 'date-fns/locale';
import { useNotificationStore } from '@/stores/notificationStore';
import { useConversationDrawerStore } from '@/stores/conversationDrawerStore';
import { useTenantStore } from '@/stores/tenantStore';

const props = defineProps({
    item: { type: Object, required: true }
});

const emit = defineEmits(['read', 'archive']);

const router = useRouter();
const route = useRoute();
const store = useNotificationStore();
const conversationDrawer = useConversationDrawerStore();
const tenantStore = useTenantStore();

// Cores por tipo (usadas pra ícone + chips). RGB explícito pra usar com
// color-mix() no scoped CSS sem depender de tokens var().
const typeMap = {
    new_scheduling:   { icon: 'pi-inbox',              label: 'Agendamento',     rgb: '244, 63, 94'   }, // rose-500
    new_patient:      { icon: 'pi-user-plus',          label: 'Novo paciente',   rgb: '14, 165, 233'  }, // sky-500
    recurrence_alert: { icon: 'pi-refresh',            label: 'Recorrência',     rgb: '245, 158, 11'  }, // amber-500
    session_status:   { icon: 'pi-calendar-times',     label: 'Sessão',          rgb: '249, 115, 22'  }, // orange-500
    inbound_message:  { icon: 'pi-whatsapp',           label: 'WhatsApp',        rgb: '16, 185, 129'  }, // emerald-500
    system_alert:     { icon: 'pi-exclamation-circle', label: 'Alerta',          rgb: '239, 68, 68'   }  // red-500
};
const DEFAULT_TYPE = { icon: 'pi-bell', label: '', rgb: '99, 102, 241' };

// Aliases semânticos do deeplink → rota real por role. Mesmo map do AppLayout.
const DEEPLINK_ALIASES = {
    '/crm/conversas': { therapist: '/therapist/conversas', clinic_admin: '/admin/conversas' },
    '/conversas': { therapist: '/therapist/conversas', clinic_admin: '/admin/conversas' }
};

// Mapeamento de slug "última parte da rota" → seção Melissa equivalente.
// Usado quando o user está no layout Melissa (/melissa/*) pra evitar que
// uma notificação o jogue pro layout rail (rota do role) e perca o overlay.
// Slugs cobertos: páginas dedicadas + embeds (financeiro, documentos, etc).
const MELISSA_SLUG_MAP = {
    agenda: 'agenda',
    conversas: 'conversas',
    patients: 'pacientes',
    pacientes: 'pacientes',
    medicos: 'medicos',
    recorrencias: 'recorrencias',
    compromissos: 'compromissos',
    grupos: 'grupos',
    tags: 'tags',
    'cadastros-recebidos': 'cadastros-recebidos',
    financeiro: 'financeiro',
    'financeiro-lancamentos': 'financeiro-lancamentos',
    documentos: 'documentos',
    'documentos-templates': 'documentos-templates',
    'agendamentos-recebidos': 'agendamentos-recebidos',
    'online-scheduling': 'online-scheduling',
    relatorios: 'relatorios',
    notificacoes: 'notificacoes',
    'link-externo': 'link-externo'
};

function resolveDeeplink(link) {
    if (!link || typeof link !== 'string') return link;
    const alias = DEEPLINK_ALIASES[link];
    if (!alias) return link;
    const role = tenantStore?.activeRole || 'therapist';
    return alias[role] || alias.therapist;
}

// Se o user está em /melissa/*, traduz o destino pra equivalente Melissa
// quando possível. Pra rotas sem mapping conhecido, retorna o path original
// (vai navegar pro layout rail — caso raro, geralmente é uma rota muito
// específica que não tem versão Melissa ainda).
function applyMelissaContext(path) {
    if (!path || typeof path !== 'string') return path;
    if (path.startsWith('/melissa')) return path; // já é Melissa
    if (!route.path?.startsWith('/melissa')) return path; // user não está em Melissa
    // Strip prefixos de role e pega o último segmento significativo
    const cleaned = path.replace(/^\/(therapist|admin|clinic|crm)/, '');
    const parts = cleaned.split('/').filter(Boolean);
    if (!parts.length) return path;
    const slug = MELISSA_SLUG_MAP[parts[parts.length - 1]] || MELISSA_SLUG_MAP[parts[0]];
    return slug ? `/melissa/${slug}` : path;
}

const meta = computed(() => typeMap[props.item.type] || DEFAULT_TYPE);
const isUnread = computed(() => !props.item.read_at);

const timeAgo = computed(() => formatDistanceToNow(new Date(props.item.created_at), { addSuffix: true, locale: ptBR }));

// CSS vars injetadas no item via :style — permite color-mix() no scoped CSS
// sem precisar de N classes. `--type-rgb` segue a paleta do typeMap.
const itemStyle = computed(() => ({
    '--type-rgb': meta.value.rgb
}));

const initials = computed(() => props.item.payload?.avatar_initials || '?');

async function openConversationByThreadKey(threadKey) {
    if (!threadKey) return false;
    try {
        const tenantId = tenantStore.activeTenantId;
        const { supabase } = await import('@/lib/supabase/client');
        const { data } = await tenantDb().from('conversation_threads')
            .select('*')
            
            .eq('thread_key', threadKey)
            .maybeSingle();
        if (!data) return false;
        await conversationDrawer.openForThread(data);
        return true;
    } catch {
        return false;
    }
}

async function handleRowClick() {
    const payload = props.item.payload || {};

    // Inbound message → abre drawer global (evita 403 em rota admin/therapist)
    if (props.item.type === 'inbound_message') {
        if (payload.patient_id) {
            conversationDrawer.openForPatient(payload.patient_id);
        } else if (payload.from_number) {
            conversationDrawer.openForThread({
                thread_key: `anon:${payload.from_number}`,
                tenant_id: tenantStore.activeTenantId,
                patient_id: null,
                patient_name: null,
                contact_number: payload.from_number,
                channel: payload.channel || 'whatsapp',
                message_count: 1,
                unread_count: 1,
                kanban_status: 'awaiting_us',
                last_message_at: new Date().toISOString()
            });
        }
        store.drawerOpen = false;
        emit('read', props.item.id);
        return;
    }

    // System alert com thread_key → abre drawer da conversa
    if (payload.thread_key) {
        const ok = await openConversationByThreadKey(payload.thread_key);
        if (ok) {
            store.drawerOpen = false;
            emit('read', props.item.id);
            return;
        }
    }

    // Fallback: segue deeplink resolvido por alias e (se user está em
    // Melissa) traduzido pra equivalente do layout Melissa.
    const deeplink = applyMelissaContext(resolveDeeplink(payload.deeplink));
    if (deeplink) {
        router.push(deeplink);
        store.drawerOpen = false;
        emit('read', props.item.id);
    }
}

async function handleOpenConversation(e) {
    e.stopPropagation();
    const payload = props.item.payload || {};
    const ok = await openConversationByThreadKey(payload.thread_key);
    if (ok) {
        store.drawerOpen = false;
        emit('read', props.item.id);
    }
}

function handleOpenDeeplink(e) {
    e.stopPropagation();
    const payload = props.item.payload || {};
    const deeplink = applyMelissaContext(resolveDeeplink(payload.deeplink));
    if (deeplink) {
        router.push(deeplink);
        store.drawerOpen = false;
        emit('read', props.item.id);
    }
}

function handleMarkRead(e) {
    e.stopPropagation();
    emit('read', props.item.id);
}

function handleArchive(e) {
    e.stopPropagation();
    emit('archive', props.item.id);
}
</script>

<template>
    <div
        class="notif-item"
        :class="{ 'notif-item--unread': isUnread }"
        :style="itemStyle"
        role="button"
        tabindex="0"
        @click="handleRowClick"
        @keydown.enter="handleRowClick"
    >
        <!-- Ícone do tipo (avatar de iniciais quando vier do payload, senão o ícone) -->
        <div class="notif-item__type" aria-hidden="true">
            <span v-if="item.payload?.avatar_initials" class="notif-item__avatar">{{ initials }}</span>
            <i v-else :class="['pi', meta.icon, 'notif-item__icon']" />
            <span v-if="isUnread" class="notif-item__unread-dot" aria-label="Não lida" />
        </div>

        <!-- Conteúdo -->
        <div class="notif-item__body">
            <div class="notif-item__head">
                <span class="notif-item__type-label">{{ meta.label }}</span>
                <span class="notif-item__time">{{ timeAgo }}</span>
            </div>
            <p class="notif-item__title">{{ item.payload?.title }}</p>
            <p v-if="item.payload?.detail" class="notif-item__detail">{{ item.payload.detail }}</p>

            <div v-if="item.payload?.thread_key || item.payload?.deeplink" class="notif-item__quick" @click.stop>
                <button
                    v-if="item.payload?.thread_key"
                    class="notif-quick-btn"
                    title="Abrir conversa"
                    @click="handleOpenConversation"
                >
                    <i class="pi pi-comment" />
                    <span>Conversa</span>
                </button>
                <button
                    v-if="item.payload?.deeplink"
                    class="notif-quick-btn notif-quick-btn--primary"
                    :title="item.payload?.actionLabel || 'Abrir'"
                    @click="handleOpenDeeplink"
                >
                    <span>{{ item.payload?.actionLabel || 'Abrir' }}</span>
                    <i class="pi pi-arrow-right" />
                </button>
            </div>
        </div>

        <!-- Ações secundárias (revelam no hover) -->
        <div class="notif-item__actions" @click.stop>
            <button v-if="isUnread" class="notif-item__btn" title="Marcar como lida" @click="handleMarkRead">
                <i class="pi pi-check" />
            </button>
            <button class="notif-item__btn" title="Arquivar" @click="handleArchive">
                <i class="pi pi-times" />
            </button>
        </div>
    </div>
</template>

<style scoped>
/* Item-card: layout flat, mas com type-color como spine visual.
   --type-rgb é injetado inline (vem do typeMap). color-mix() pinta
   bg/border/icon usando a mesma cor — uma var, várias intensidades. */
.notif-item {
    position: relative;
    display: grid;
    grid-template-columns: 40px 1fr auto;
    align-items: start;
    gap: 0.75rem;
    padding: 0.75rem 0.85rem 0.75rem 0.95rem;
    margin: 0.4rem 0.6rem;
    background: var(--surface-card, #fff);
    border: 1px solid var(--surface-border);
    border-radius: 10px;
    cursor: pointer;
    transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease, background-color 160ms ease;
}
/* Spine colorida: 3px na esquerda da cor do tipo. Reforça pertencimento
   sem usar background pesado. */
.notif-item::before {
    content: '';
    position: absolute;
    left: 0;
    top: 10%;
    bottom: 10%;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: rgb(var(--type-rgb));
    opacity: 0.55;
    transition: opacity 160ms ease, top 160ms ease, bottom 160ms ease;
}
.notif-item:hover {
    transform: translateY(-1px);
    box-shadow: 0 4px 14px color-mix(in srgb, rgb(var(--type-rgb)) 12%, transparent);
    border-color: color-mix(in srgb, rgb(var(--type-rgb)) 40%, var(--surface-border));
    background: var(--surface-card);
}
.notif-item:hover::before { opacity: 1; top: 0; bottom: 0; }
.notif-item:focus-visible {
    outline: 2px solid color-mix(in srgb, rgb(var(--type-rgb)) 60%, transparent);
    outline-offset: 2px;
}
/* Não-lida: bg sutil tinted da cor do tipo + título mais forte */
.notif-item--unread {
    background: color-mix(in srgb, rgb(var(--type-rgb)) 6%, var(--surface-card, #fff));
    border-color: color-mix(in srgb, rgb(var(--type-rgb)) 22%, var(--surface-border));
}
.notif-item--unread:hover {
    background: color-mix(in srgb, rgb(var(--type-rgb)) 9%, var(--surface-card, #fff));
}

/* Type avatar: círculo 40px com bg type-color 14% + ícone 100% */
.notif-item__type {
    position: relative;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: color-mix(in srgb, rgb(var(--type-rgb)) 14%, transparent);
    color: rgb(var(--type-rgb));
    display: grid;
    place-items: center;
    flex-shrink: 0;
}
.notif-item__icon {
    font-size: 1rem;
}
.notif-item__avatar {
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: rgb(var(--type-rgb));
}

/* Pulse dot pra "não lida" — fica no canto sup-direito do avatar */
.notif-item__unread-dot {
    position: absolute;
    top: -1px;
    right: -1px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: rgb(var(--type-rgb));
    border: 2px solid var(--surface-card, #fff);
    box-shadow: 0 0 0 0 color-mix(in srgb, rgb(var(--type-rgb)) 50%, transparent);
    animation: notif-unread-pulse 2.2s ease-in-out infinite;
}
@keyframes notif-unread-pulse {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, rgb(var(--type-rgb)) 50%, transparent); }
    50%      { box-shadow: 0 0 0 5px color-mix(in srgb, rgb(var(--type-rgb)) 0%, transparent); }
}

/* Body */
.notif-item__body {
    min-width: 0;
}
.notif-item__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.15rem;
}
.notif-item__type-label {
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: rgb(var(--type-rgb));
    opacity: 0.85;
}
.notif-item__time {
    font-size: 0.68rem;
    color: var(--text-color-secondary);
    opacity: 0.75;
    white-space: nowrap;
    flex-shrink: 0;
}
.notif-item__title {
    font-weight: 600;
    font-size: 0.86rem;
    color: var(--text-color);
    margin: 0 0 0.15rem;
    line-height: 1.3;
    /* 2 linhas máx */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.notif-item--unread .notif-item__title { font-weight: 700; }
.notif-item__detail {
    font-size: 0.78rem;
    color: var(--text-color-secondary);
    margin: 0;
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Quick actions: chips type-color */
.notif-item__quick {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
    margin-top: 0.55rem;
}
.notif-quick-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.28rem 0.7rem;
    border: 1px solid color-mix(in srgb, rgb(var(--type-rgb)) 28%, var(--surface-border));
    background: color-mix(in srgb, rgb(var(--type-rgb)) 8%, transparent);
    color: rgb(var(--type-rgb));
    border-radius: 9999px;
    font-size: 0.7rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 140ms ease, border-color 140ms ease, transform 140ms ease;
}
.notif-quick-btn:hover {
    background: color-mix(in srgb, rgb(var(--type-rgb)) 16%, transparent);
    border-color: color-mix(in srgb, rgb(var(--type-rgb)) 50%, transparent);
    transform: translateY(-1px);
}
.notif-quick-btn i { font-size: 0.7rem; }
.notif-quick-btn--primary {
    background: rgb(var(--type-rgb));
    color: #fff;
    border-color: rgb(var(--type-rgb));
}
.notif-quick-btn--primary:hover {
    background: color-mix(in srgb, rgb(var(--type-rgb)) 88%, #000 12%);
    border-color: color-mix(in srgb, rgb(var(--type-rgb)) 88%, #000 12%);
    color: #fff;
}

/* Ações secundárias (mark-read / archive): revelam no hover */
.notif-item__actions {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
    opacity: 0;
    transform: translateX(4px);
    transition: opacity 160ms ease, transform 160ms ease;
}
.notif-item:hover .notif-item__actions,
.notif-item:focus-within .notif-item__actions {
    opacity: 1;
    transform: translateX(0);
}
.notif-item__btn {
    width: 1.65rem;
    height: 1.65rem;
    border-radius: 50%;
    border: 1px solid transparent;
    background: var(--surface-100, var(--surface-hover, rgba(0,0,0,0.04)));
    color: var(--text-color-secondary);
    display: grid;
    place-items: center;
    cursor: pointer;
    font-size: 0.7rem;
    transition: background-color 140ms ease, color 140ms ease, border-color 140ms ease;
}
.notif-item__btn:hover {
    background: color-mix(in srgb, rgb(var(--type-rgb)) 12%, transparent);
    color: rgb(var(--type-rgb));
    border-color: color-mix(in srgb, rgb(var(--type-rgb)) 30%, transparent);
}
</style>
