<!--
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/layout/AppRailPanel.vue
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
-->
<!-- Painel expansível do Layout 2 -->
<script setup>
import { computed, ref, watch, nextTick, onMounted, onBeforeUnmount } from 'vue';
import { useRouter, useRoute } from 'vue-router';

import { useMenuStore } from '@/stores/menuStore';
import { useLayout } from './composables/layout';
import { useEntitlementsStore } from '@/stores/entitlementsStore';
import { useMenuBadges } from '@/composables/useMenuBadges';

import PatientCreatePopover from '@/components/ui/PatientCreatePopover.vue';
import ComponentCadastroRapido from '@/components/ComponentCadastroRapido.vue';

const menuStore = useMenuStore();
const { layoutState, layoutConfig, clearRailHoverClose, scheduleRailHoverClose } = useLayout();
const entitlements = useEntitlementsStore();
const menuBadges = useMenuBadges();
const router = useRouter();
const route = useRoute();

function menuBadgeLabel(item) {
    const key = item?.badgeKey;
    if (!key) return null;
    const val = menuBadges[key]?.value || 0;
    if (!val) return null;
    return key === 'agendaHoje' ? `${val} hoje` : String(val);
}

// ── Seção ativa ──────────────────────────────────────────────
const currentSection = computed(() => {
    const model = menuStore.model || [];
    return model.find((s) => s.label === layoutState.railSectionKey) || null;
});

// Todos os grupos do menu
const allSections = computed(() => {
    const model = menuStore.model || [];
    return model.filter((s) => s.label && Array.isArray(s.items) && s.items.length);
});

// "Início" = chave especial __home__
const isHome = computed(() => layoutState.railSectionKey === '__home__');

// Seções visíveis: tudo em Início, só a selecionada nos demais
const visibleSections = computed(() => (isHome.value ? allSections.value : currentSection.value ? [currentSection.value] : []));

const panelTitle = computed(() => (isHome.value ? 'Início' : currentSection.value?.label || 'Menu'));

// ── Helpers ──────────────────────────────────────────────────
function isLocked(item) {
    if (!item.proBadge || !item.feature) return false;
    try {
        return !entitlements.has(item.feature);
    } catch {
        return false;
    }
}

function toPath(to) {
    if (!to) return '';
    if (typeof to === 'string') return to;
    try {
        return router.resolve(to).path || '';
    } catch {
        return '';
    }
}

function isActive(item) {
    const active = String(layoutState.activePath || route.path || '');
    if (!item.to) return false;
    const p = toPath(item.to);
    if (!p) return false;
    if (active === p) return true;
    const segments = p.split('/').filter(Boolean);
    return segments.length >= 2 && active.startsWith(p + '/');
}

function navigate(item) {
    if (isLocked(item)) {
        router.push({ name: 'upgrade', query: { feature: item.feature || '' } });
        return;
    }
    if (item.to) {
        layoutState.activePath = typeof item.to === 'string' ? item.to : router.resolve(item.to).path;
        router.push(item.to);
    }
}

function closePanel() {
    layoutState.railPanelOpen = false;
}

// ── Hover: mantém painel aberto enquanto mouse está dentro ───
function onPanelMouseEnter() {
    if (layoutConfig.railOpenMode !== 'hover') return;
    clearRailHoverClose();
}
function onPanelMouseLeave() {
    if (layoutConfig.railOpenMode !== 'hover') return;
    if (popoverOpen.value) return; // popover flutuante aberto — não fechar
    scheduleRailHoverClose(200);
}

// ── QuickCreate (Pacientes) ───────────────────────────────
const createPopover = ref(null);
const quickDialog = ref(false);
const popoverOpen = ref(false);

function openQuickCreate(event, item) {
    createPopover.value?.toggle(event);
}
function onQuickCreate() {
    quickDialog.value = true;
}

// ── Busca (todo o menu) ──────────────────────────────────────
const query = ref('');
const showResults = ref(false);
const activeIndex = ref(-1);
const forcedOpen = ref(false);
const searchEl = ref(null);
const searchWrapEl = ref(null);

const RECENT_KEY = 'menu_search_recent';
const recent = ref([]);
function loadRecent() {
    try {
        recent.value = JSON.parse(localStorage.getItem(RECENT_KEY) || '[]');
    } catch {
        recent.value = [];
    }
}
function saveRecent(q) {
    const v = String(q || '').trim();
    if (!v) return;
    const list = [v, ...recent.value.filter((x) => x !== v)].slice(0, 8);
    recent.value = list;
    localStorage.setItem(RECENT_KEY, JSON.stringify(list));
}
function clearRecent() {
    recent.value = [];
    try {
        localStorage.removeItem(RECENT_KEY);
    } catch {}
}
loadRecent();

watch(query, (v) => {
    const hasText = !!v?.trim();
    if (hasText) {
        forcedOpen.value = false;
        showResults.value = true;
        return;
    }
    showResults.value = forcedOpen.value;
});

function clearSearch() {
    query.value = '';
    activeIndex.value = -1;
    showResults.value = false;
    forcedOpen.value = false;
}

function norm(s) {
    return String(s || '')
        .toLowerCase()
        .normalize('NFD')
        .replace(/\p{Diacritic}/gu, '')
        .trim();
}

function isVisibleItem(it) {
    const v = it?.visible;
    if (typeof v === 'function') return !!v();
    if (v === undefined || v === null) return true;
    return v !== false;
}

function flattenMenu(items, trail = []) {
    const out = [];
    for (const it of items || []) {
        if (!isVisibleItem(it)) continue;
        const nextTrail = [...trail, it?.label].filter(Boolean);
        if (it?.to && !it?.items?.length) {
            out.push({ label: it.label || it.to, to: it.to, icon: it.icon, trail: nextTrail, proBadge: !!(it.__showProBadge ?? it.proBadge), feature: it.feature || null });
        }
        if (it?.items?.length) out.push(...flattenMenu(it.items, nextTrail));
    }
    return out;
}

const allLinks = computed(() => flattenMenu(menuStore.model || []));

const results = computed(() => {
    const q = norm(query.value);
    if (!q) return [];
    const wantPro = q === 'pro' || q.startsWith('pro ') || q.includes(' pro');
    return allLinks.value
        .filter((r) => {
            const hay = `${norm(r.label)} ${norm(r.trail.join(' > '))} ${norm(r.to)}`;
            if (hay.includes(q)) return true;
            if (wantPro && (r.proBadge || r.feature)) return true;
            return false;
        })
        .slice(0, 12);
});

watch(results, (list) => {
    activeIndex.value = list.length ? 0 : -1;
});

function escapeHtml(s) {
    return String(s || '')
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
}
function highlight(text, q) {
    const queryNorm = norm(q);
    const raw = String(text || '');
    if (!queryNorm) return escapeHtml(raw);
    const rawNorm = norm(raw);
    const idx = rawNorm.indexOf(queryNorm);
    if (idx < 0) return escapeHtml(raw);
    const before = escapeHtml(raw.slice(0, idx));
    const mid = escapeHtml(raw.slice(idx, idx + queryNorm.length));
    const after = escapeHtml(raw.slice(idx + queryNorm.length));
    return `${before}<mark class="px-1 rounded bg-yellow-200/40">${mid}</mark>${after}`;
}

function onSearchKeydown(e) {
    if (e.key === 'Escape') {
        showResults.value = false;
        forcedOpen.value = false;
        return;
    }
    if (e.key === 'ArrowDown') {
        e.preventDefault();
        if (!results.value.length) return;
        showResults.value = true;
        activeIndex.value = (activeIndex.value + 1) % results.value.length;
        return;
    }
    if (e.key === 'ArrowUp') {
        e.preventDefault();
        if (!results.value.length) return;
        showResults.value = true;
        activeIndex.value = (activeIndex.value - 1 + results.value.length) % results.value.length;
        return;
    }
    if (e.key === 'Enter' && showResults.value && results.value.length && activeIndex.value >= 0) {
        e.preventDefault();
        goToResult(results.value[activeIndex.value]);
    }
}

function onSearchFocus() {
    if (!query.value?.trim()) {
        forcedOpen.value = true;
        showResults.value = true;
    }
}

function applyRecent(q) {
    query.value = q;
    forcedOpen.value = true;
    showResults.value = true;
    activeIndex.value = 0;
    nextTick(() => searchEl.value?.$el?.querySelector?.('input')?.focus?.());
}

function onDocMouseDown(e) {
    if (!showResults.value) return;
    if (!searchWrapEl.value?.contains(e.target)) {
        showResults.value = false;
        forcedOpen.value = false;
    }
}

onMounted(() => document.addEventListener('mousedown', onDocMouseDown));
onBeforeUnmount(() => document.removeEventListener('mousedown', onDocMouseDown));

async function goToResult(r) {
    saveRecent(query.value);
    query.value = '';
    showResults.value = false;
    activeIndex.value = -1;
    forcedOpen.value = false;
    layoutState.activePath = typeof r.to === 'string' ? r.to : router.resolve(r.to).path;
    await router.push(r.to);
}
</script>

<template>
    <Transition name="panel-slide">
        <aside
            v-if="layoutState.railPanelOpen"
            class="w-[260px] shrink-0 h-screen flex flex-col border-r border-[var(--surface-border)] bg-[var(--surface-card)] overflow-hidden"
            :class="{ 'rp-panel--hover': layoutConfig.railOpenMode === 'hover' }"
            aria-label="Menu lateral"
            @mouseenter="onPanelMouseEnter"
            @mouseleave="onPanelMouseLeave"
        >
            <!-- Header -->
            <div class="h-14 shrink-0 flex items-center justify-between px-4 border-b border-[var(--surface-border)]">
                <span class="text-[0.9rem] font-bold tracking-tight text-[var(--text-color)]">
                    {{ panelTitle }}
                </span>
                <button
                    class="w-7 h-7 rounded-lg border-none bg-transparent text-[var(--text-color-secondary)] cursor-pointer grid place-items-center text-xs transition-colors hover:bg-[var(--surface-ground)] hover:text-[var(--text-color)]"
                    aria-label="Fechar painel"
                    @click="closePanel"
                >
                    <i class="pi pi-times" />
                </button>
            </div>

            <!-- Busca — só no Início -->
            <div v-if="isHome" ref="searchWrapEl" class="shrink-0 px-2.5 pt-2.5 border-b border-[var(--surface-border)]">
                <!-- Campo -->
                <div class="relative">
                    <div aria-hidden="true" style="position: absolute; left: -9999px; top: -9999px; width: 1px; height: 1px; overflow: hidden">
                        <input type="email" name="email" autocomplete="username" tabindex="-1" disabled />
                        <input type="password" name="password" autocomplete="current-password" tabindex="-1" disabled />
                    </div>

                    <FloatLabel variant="on" class="w-full">
                        <IconField class="w-full">
                            <InputIcon class="pi pi-search" />
                            <InputText
                                ref="searchEl"
                                id="rp_menu_search"
                                name="rp_menu_search"
                                type="text"
                                inputmode="search"
                                autocomplete="off"
                                autocapitalize="off"
                                autocorrect="off"
                                spellcheck="false"
                                data-lpignore="true"
                                data-1p-ignore="true"
                                v-model="query"
                                class="w-full pr-8"
                                variant="filled"
                                @focus="onSearchFocus"
                                @keydown="onSearchKeydown"
                            />
                        </IconField>
                        <label for="rp_menu_search">Encontrar menu...</label>
                    </FloatLabel>

                    <button
                        v-if="query.trim()"
                        type="button"
                        class="absolute right-2 top-1/2 -translate-y-1/2 bg-transparent border-none cursor-pointer opacity-60 hover:opacity-100 text-[var(--text-color-secondary)] text-xs grid place-items-center p-0.5"
                        @mousedown.prevent="clearSearch"
                        aria-label="Limpar busca"
                    >
                        <i class="pi pi-times" />
                    </button>
                </div>

                <!-- Recentes -->
                <div v-if="showResults && !query.trim() && recent.length" class="mt-1.5 mb-2.5 border border-[var(--surface-border)] rounded-xl bg-[var(--surface-card)] shadow-sm overflow-hidden">
                    <div class="flex items-center justify-between px-2.5 py-1.5 text-[1rem] opacity-65 text-[var(--text-color-secondary)]">
                        <span>Recentes</span>
                        <button type="button" class="bg-transparent border-none cursor-pointer opacity-70 hover:opacity-100 text-inherit text-[1rem]" @mousedown.prevent="clearRecent" aria-label="Limpar recentes">
                            <i class="pi pi-trash" />
                        </button>
                    </div>
                    <button
                        v-for="q in recent"
                        :key="q"
                        class="w-full flex items-center gap-2 px-2.5 py-1.5 bg-transparent border-none cursor-pointer text-[var(--text-color)] text-[1rem] text-left transition-colors hover:bg-[var(--surface-hover)]"
                        type="button"
                        @click.stop.prevent="applyRecent(q)"
                    >
                        <i class="pi pi-history text-[0.8rem] opacity-70 shrink-0" />
                        <span class="flex-1">{{ q }}</span>
                    </button>
                </div>

                <!-- Resultados de busca -->
                <div v-else-if="showResults && results.length" class="mt-1.5 mb-2.5 border border-[var(--surface-border)] rounded-xl bg-[var(--surface-card)] shadow-sm overflow-hidden">
                    <button
                        v-for="(r, i) in results"
                        :key="String(r.to)"
                        type="button"
                        @mousedown.prevent="goToResult(r)"
                        class="w-full flex items-center gap-2 px-2.5 py-1.5 bg-transparent border-none cursor-pointer text-[var(--text-color)] text-[1rem] text-left transition-colors"
                        :class="i === activeIndex ? 'bg-[var(--surface-hover)]' : 'hover:bg-[var(--surface-hover)]'"
                    >
                        <i v-if="r.icon" :class="r.icon" class="text-[0.8rem] opacity-70 shrink-0" />
                        <div class="flex flex-col flex-1">
                            <span class="font-medium leading-tight" v-html="highlight(r.label, query)" />
                            <small class="text-[0.68rem] opacity-60">{{ r.trail.join(' > ') }}</small>
                        </div>
                        <span v-if="r.proBadge" class="text-[0.58rem] font-extrabold uppercase tracking-widest px-1.5 py-px rounded border border-[var(--surface-border)] text-[var(--text-color-secondary)] opacity-70">PRO</span>
                    </button>
                </div>

                <div v-else-if="showResults && query && !results.length" class="py-2 pb-2.5 text-[1rem] opacity-60 text-[var(--text-color-secondary)]">Nenhum item encontrado.</div>

                <div v-else class="pb-2.5" />
            </div>

            <!-- Nav: todo o menu -->
            <nav class="flex-1 overflow-y-auto px-2 py-2.5 flex flex-col gap-0.5 [scrollbar-width:thin] [scrollbar-color:var(--surface-border)_transparent]">
                <template v-for="section in visibleSections" :key="section.label">
                    <!-- Label da seção — só exibe quando mostrando múltiplas seções -->
                    <div v-if="visibleSections.length > 1" class="text-[0.62rem] font-extrabold uppercase tracking-widest text-[var(--text-color-secondary)] opacity-55 px-2.5 pb-1.5 pt-3 first:pt-1">
                        {{ section.label }}
                    </div>

                    <template v-for="item in section.items" :key="item.to || item.label">
                        <!-- Sub-grupo -->
                        <div v-if="item.items?.length" class="flex flex-col gap-px">
                            <div class="text-[0.6rem] font-bold uppercase tracking-widest text-[var(--text-color-secondary)] opacity-40 px-2.5 pb-1 pt-2">
                                {{ item.label }}
                            </div>
                            <button
                                v-for="child in item.items"
                                :key="child.to || child.label"
                                class="w-full flex items-center gap-2.5 px-2.5 py-2 rounded-[9px] border-none bg-transparent text-[var(--text-color-secondary)] cursor-pointer text-left text-[0.83rem] font-medium transition-colors hover:bg-[var(--surface-ground)] hover:text-[var(--text-color)]"
                                :class="{
                                    '!bg-[color-mix(in_srgb,var(--primary-color)_10%,transparent)] !text-[var(--primary-color)] !font-semibold': isActive(child),
                                    'opacity-55': isLocked(child)
                                }"
                                @click="navigate(child)"
                            >
                                <i v-if="child.icon" :class="child.icon" class="text-[1rem] shrink-0 opacity-75 !text-[var(--primary-color)]" />
                                <span class="flex-1">{{ child.label }}</span>
                                <span v-if="isLocked(child)" class="text-[0.58rem] font-extrabold uppercase tracking-widest px-1.5 py-px rounded border border-[var(--surface-border)] text-[var(--text-color-secondary)] opacity-70">PRO</span>
                                <span v-if="menuBadgeLabel(child)" class="text-[0.62rem] font-bold px-1.5 py-px rounded-full bg-[var(--primary-color)] text-white leading-none">{{ menuBadgeLabel(child) }}</span>
                            </button>
                        </div>

                        <!-- Item folha -->
                        <div v-else class="flex items-center gap-1">
                            <button
                                class="flex-1 flex items-center gap-2.5 px-2.5 py-2 rounded-[9px] border-none bg-transparent text-[var(--text-color-secondary)] cursor-pointer text-left text-[0.83rem] font-medium transition-colors hover:bg-[var(--surface-ground)] hover:text-[var(--text-color)]"
                                :class="{
                                    '!bg-[color-mix(in_srgb,var(--primary-color)_10%,transparent)] !text-[var(--primary-color)] !font-semibold': isActive(item),
                                    'opacity-55': isLocked(item)
                                }"
                                @click="navigate(item)"
                            >
                                <i v-if="item.icon" :class="item.icon" class="text-[1rem] shrink-0 opacity-75 !text-[var(--primary-color)]" />
                                <span class="flex-1">{{ item.label }}</span>
                                <span v-if="isLocked(item)" class="text-[0.58rem] font-extrabold uppercase tracking-widest px-1.5 py-px rounded border border-[var(--surface-border)] text-[var(--text-color-secondary)] opacity-70">PRO</span>
                                <span v-if="menuBadgeLabel(item)" class="text-[0.62rem] font-bold px-1.5 py-px rounded-full bg-[var(--primary-color)] text-white leading-none">{{ menuBadgeLabel(item) }}</span>
                            </button>
                            <button
                                v-if="item.quickCreate"
                                class="w-6 h-6 shrink-0 rounded-md border-none bg-transparent text-[var(--text-color-secondary)] cursor-pointer grid place-items-center text-xs transition-colors hover:bg-[var(--surface-ground)] hover:text-[var(--text-color)]"
                                @click.stop="openQuickCreate($event, item)"
                                title="Novo paciente"
                            >
                                <i class="pi pi-plus" />
                            </button>
                        </div>
                    </template>
                </template>
            </nav>

            <!-- PatientCreatePopover (shared) -->
            <PatientCreatePopover ref="createPopover" @quick-create="onQuickCreate" @show="popoverOpen = true" @hide="popoverOpen = false" />

            <!-- Cadastro Rápido Dialog -->
            <ComponentCadastroRapido
                v-model="quickDialog"
                title="Cadastro Rápido"
                table-name="patients"
                name-field="nome_completo"
                email-field="email_principal"
                phone-field="telefone"
                :extra-payload="{ status: 'Ativo' }"
                @created="quickDialog = false"
            />
        </aside>
    </Transition>
</template>

<style scoped>
/* ── Hover mode: painel flutua sobre o conteúdo (não empurra) ──
   Reserva o espaço da barra de ícones (60px) e do topbar (56px),
   igual ao comportamento do .layout-sidebar no layout clássico.  */
.rp-panel--hover {
    position: fixed !important;
    top: calc(56px + var(--notice-banner-height, 0px)) !important;
    left: 60px !important;
    height: calc(100vh - 56px - var(--notice-banner-height, 0px)) !important;
    box-shadow: 2px 0 6px rgba(0, 0, 0, 0.06) !important;
    z-index: 999;
}

.panel-slide-enter-active,
.panel-slide-leave-active {
    transition:
        width 0.22s cubic-bezier(0.22, 1, 0.36, 1),
        opacity 0.18s ease;
    overflow: hidden;
}
.panel-slide-enter-from,
.panel-slide-leave-to {
    width: 0 !important;
    opacity: 0;
}
</style>
