<script setup>
/*
 * MelissaAgendaSearchPopover — busca da toolbar da Agenda Melissa
 * --------------------------------------------------------------
 * Pattern Cmd+K: input + lista de resultados num Popover. Suporta:
 *   - Datas: "20/04", "20/04/2026", "hoje", "amanhã", "ontem"
 *     → emit('goto-date', date) — pai navega o FullCalendar
 *   - Texto livre: pesquisa server-side em patients.nome_completo + titulo
 *     (via searchEventosByText) com debounce de 300ms. Limite 20 resultados.
 *     → emit('select-evento', ev) — pai aciona gotoDate + auto-select patient
 *
 * Componente autocontido — owns todo o state, debounce, parsing.
 * Pai expõe um botão âncora (`.ma-cal__search-btn`) e chama
 * `popoverRef.value.toggle($event)` no click. Hotkey Cmd+K também
 * vive no pai (acha o botão via querySelector e chama toggle).
 *
 * Emit:
 *   - goto-date(date: Date) — escolheu uma data do parser
 *   - select-evento(ev) — escolheu um evento da lista de busca
 *
 * Exposto via defineExpose:
 *   - toggle(event) — abre/fecha o popover, foca input quando abre
 */
import { ref, onBeforeUnmount } from 'vue';
import Popover from 'primevue/popover';
import { searchEventosByText } from './composables/useMelissaEventos';

const emit = defineEmits(['goto-date', 'select-evento']);

const popRef = ref(null);
const inputRef = ref(null);
const searchQuery = ref('');
const searchResults = ref([]);
const searchLoading = ref(false);
const searchDateMatch = ref(null); // Date | null — preenchido se query parsear como data
let _debounceTimer = null;
// Token monotonico — protege contra race condition: se o user digita "ab"
// e depois "abc", o request "ab" pode resolver DEPOIS do "abc" em conexao
// lenta. Cada request guarda seu token; ao voltar, so aplica se ainda eh
// o mais recente. Cancelamento via AbortController seria ideal mas exigiria
// searchEventosByText aceitar signal — por ora token resolve sem mexer no API.
let _searchToken = 0;

function parseSearchAsDate(str) {
    const t = String(str || '').trim().toLowerCase();
    if (!t) return null;
    if (t === 'hoje') { const d = new Date(); d.setHours(0,0,0,0); return d; }
    if (t === 'amanha' || t === 'amanhã') {
        const d = new Date(); d.setHours(0,0,0,0); d.setDate(d.getDate() + 1); return d;
    }
    if (t === 'ontem') {
        const d = new Date(); d.setHours(0,0,0,0); d.setDate(d.getDate() - 1); return d;
    }
    // DD/MM ou DD/MM/YYYY (também aceita - e .)
    const m = t.match(/^(\d{1,2})[/\-.](\d{1,2})(?:[/\-.](\d{2,4}))?$/);
    if (m) {
        const day = parseInt(m[1], 10);
        const month = parseInt(m[2], 10);
        let year = parseInt(m[3] || '', 10);
        if (!year || Number.isNaN(year)) year = new Date().getFullYear();
        if (year < 100) year += 2000;
        if (day < 1 || day > 31 || month < 1 || month > 12) return null;
        const d = new Date(year, month - 1, day);
        if (Number.isNaN(d.getTime())) return null;
        return d;
    }
    return null;
}

function toggle(event) {
    popRef.value?.toggle(event);
    // Foco no input quando abrir (Popover anima ~150ms). PrimeVue InputText
    // renderiza `<input>` direto, então `$el` já é o elemento focável.
    // Tentamos algumas vezes pra cobrir mount async + transição do Popover.
    let tries = 0;
    const tick = () => {
        const el = inputRef.value?.$el;
        if (el && typeof el.focus === 'function') { el.focus(); el.select?.(); return; }
        if (tries++ < 8) setTimeout(tick, 30);
    };
    setTimeout(tick, 80);
}

function fechar() {
    try { popRef.value?.hide(); } catch {}
}

// Toda vez que o popover fecha (via ESC, click-fora, ou submit/select),
// reseta o state pra proxima abertura comecar limpa. Antes, ESC mantinha
// query+resultados "fantasmas" — UX confusa: reabrir mostrava busca
// antiga que talvez nao fizesse mais sentido.
function onPopoverHide() {
    searchQuery.value = '';
    searchResults.value = [];
    searchDateMatch.value = null;
    searchLoading.value = false;
    ++_searchToken; // invalida requests em flight
}

function onSearchInput() {
    if (_debounceTimer) clearTimeout(_debounceTimer);
    const q = searchQuery.value;
    searchDateMatch.value = parseSearchAsDate(q);
    // Se digitou data, mostra resultado imediato (sem hit no DB)
    if (searchDateMatch.value) {
        // Invalida tokens em flight pra eles nao voltarem e sobrescreverem [].
        ++_searchToken;
        searchResults.value = [];
        searchLoading.value = false;
        return;
    }
    if (String(q || '').trim().length < 2) {
        ++_searchToken;
        searchResults.value = [];
        searchLoading.value = false;
        return;
    }
    searchLoading.value = true;
    _debounceTimer = setTimeout(async () => {
        const myToken = ++_searchToken;
        try {
            const results = await searchEventosByText(q);
            // Race guard: se outro request foi disparado depois deste,
            // descarta este (out-of-order resolution).
            if (myToken !== _searchToken) return;
            searchResults.value = results;
        } finally {
            // So zera loading se este eh o ultimo request — senao deixa o
            // proximo controlar o estado (evita flicker de loading=false
            // entre requests sequenciais rapidos).
            if (myToken === _searchToken) searchLoading.value = false;
        }
    }, 300);
}

function onSearchSubmit() {
    // Enter: prioriza data, senão pega o primeiro resultado
    if (searchDateMatch.value) {
        irParaData(searchDateMatch.value);
        return;
    }
    if (searchResults.value.length > 0) {
        selecionarResultado(searchResults.value[0]);
    }
}

function irParaData(date) {
    emit('goto-date', date);
    fechar(); // @hide handler limpa state
}

function selecionarResultado(ev) {
    if (!ev?.inicio_em) return;
    emit('select-evento', ev);
    fechar(); // @hide handler limpa state
}

function fmtDataResultado(iso) {
    const d = new Date(iso);
    if (Number.isNaN(d.getTime())) return '';
    return d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric', weekday: 'short' });
}
function fmtHoraResultado(iso) {
    const d = new Date(iso);
    if (Number.isNaN(d.getTime())) return '';
    return d.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
}

onBeforeUnmount(() => {
    if (_debounceTimer) clearTimeout(_debounceTimer);
});

defineExpose({ toggle, close: fechar });
</script>

<template>
    <Popover ref="popRef" class="ma-tsearch-pop" @hide="onPopoverHide">
        <div class="ma-tsearch flex flex-col w-[min(440px,calc(100vw-32px))] max-h-[500px] overflow-hidden">
            <div class="ma-tsearch__field relative flex items-center gap-2 px-2.5 py-1.5 mx-2 mt-2 mb-1.5 bg-[var(--m-bg-soft)] border border-[var(--m-border)] rounded-[10px] flex-shrink-0 transition-[border-color,background-color] duration-[140ms] focus-within:border-[var(--p-primary-color)] focus-within:bg-[var(--m-bg-soft-hover)] focus-within:shadow-[0_0_0_3px_color-mix(in_srgb,var(--p-primary-color)_12%,transparent)]">
                <i class="pi pi-search ma-tsearch__field-icon text-[var(--m-text-muted)] text-[0.85rem] flex-shrink-0" />
                <InputText
                    ref="inputRef"
                    v-model="searchQuery"
                    placeholder="Data (20/04) ou nome do paciente…"
                    class="ma-tsearch__input"
                    @input="onSearchInput"
                    @keydown.enter="onSearchSubmit"
                    @keydown.esc="fechar"
                />
                <button
                    v-if="searchQuery"
                    class="ma-tsearch__clear w-[22px] h-[22px] grid place-items-center border-0 bg-[var(--m-bg-medium)] text-[var(--m-text-muted)] rounded-full cursor-pointer flex-shrink-0 transition-colors duration-[140ms] hover:bg-[var(--m-border-strong)] hover:text-[var(--m-text)]"
                    v-tooltip.top="'Limpar'"
                    @click="searchQuery = ''; onSearchInput()"
                >
                    <i class="pi pi-times text-xs" />
                </button>
            </div>

            <!-- Resultado data (sem hit no DB) -->
            <button
                v-if="searchDateMatch"
                class="ma-tsearch__result ma-tsearch__result--date w-auto self-stretch flex items-center gap-2.5 px-2.5 py-2 mx-2 mb-1.5 rounded-lg cursor-pointer text-left [font-family:inherit] transition-colors duration-[140ms]"
                @click="irParaData(searchDateMatch)"
            >
                <span class="ma-tsearch__result-icon w-8 h-8 grid place-items-center rounded-lg flex-shrink-0 text-[0.78rem]"><i class="pi pi-calendar" /></span>
                <span class="ma-tsearch__result-main flex-1 min-w-0 flex flex-col gap-0.5">
                    <span class="ma-tsearch__result-title text-[0.85rem] font-medium whitespace-nowrap overflow-hidden text-ellipsis capitalize">Ir para {{ searchDateMatch.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric', weekday: 'long' }) }}</span>
                    <span class="ma-tsearch__result-sub text-[0.72rem] text-[var(--m-text-muted)] whitespace-nowrap overflow-hidden text-ellipsis">Pular para essa data no calendário</span>
                </span>
                <i class="pi pi-arrow-right text-xs opacity-50" />
            </button>

            <!-- Loading -->
            <div v-else-if="searchLoading" class="ma-tsearch__loading flex items-center gap-2 px-3.5 py-[18px] text-[var(--m-text-muted)] text-[0.85rem]">
                <i class="pi pi-spin pi-spinner" /> <span>Buscando…</span>
            </div>

            <!-- Resultados (eventos) -->
            <div v-else-if="searchResults.length > 0" class="ma-tsearch__results flex-1 min-h-0 overflow-y-auto p-1">
                <button
                    v-for="ev in searchResults"
                    :key="ev.id"
                    class="ma-tsearch__result w-full flex items-center gap-2.5 px-2.5 py-2 border-0 bg-transparent text-[var(--m-text)] rounded-lg cursor-pointer text-left [font-family:inherit] transition-colors duration-[140ms] hover:bg-[var(--m-bg-soft-hover)] focus-visible:bg-[var(--m-bg-soft-hover)] focus-visible:outline-none"
                    @click="selecionarResultado(ev)"
                >
                    <span class="ma-tsearch__result-icon w-8 h-8 grid place-items-center rounded-lg text-white flex-shrink-0 text-[0.78rem]" :style="{ background: ev.color }">
                        <i :class="ev.tipo === 'sessao' ? 'pi pi-user' : 'pi pi-calendar'" />
                    </span>
                    <span class="ma-tsearch__result-main flex-1 min-w-0 flex flex-col gap-0.5">
                        <span class="ma-tsearch__result-title text-[0.85rem] font-medium text-[var(--m-text)] whitespace-nowrap overflow-hidden text-ellipsis">{{ ev.label }}</span>
                        <span class="ma-tsearch__result-sub text-[0.72rem] text-[var(--m-text-muted)] whitespace-nowrap overflow-hidden text-ellipsis">
                            {{ fmtDataResultado(ev.inicio_em) }} · {{ fmtHoraResultado(ev.inicio_em) }}
                            <span v-if="ev.modalidade"> · {{ ev.modalidade }}</span>
                        </span>
                    </span>
                </button>
            </div>

            <!-- Vazio (já buscou mas nada encontrou) -->
            <div v-else-if="String(searchQuery || '').trim().length >= 2" class="ma-tsearch__empty flex flex-col items-center gap-2 px-4 py-[26px] mx-2 mb-2.5 mt-1 text-[var(--m-text-muted)] text-[0.82rem] border-[1.5px] border-dashed border-[color-mix(in_srgb,var(--p-primary-color)_22%,var(--m-border))] rounded-[14px] bg-[color-mix(in_srgb,var(--m-bg-soft)_50%,transparent)] text-center [&>i]:text-[2.2rem] [&>i]:opacity-70 [&>i]:text-[var(--p-primary-color)] [&>i]:mb-0.5">
                <i class="pi pi-search-minus" />
                <span class="ma-tsearch__empty-title text-[0.88rem] font-semibold text-[var(--m-text)]">Busca não encontrada…</span>
                <span class="ma-tsearch__empty-sub text-[0.78rem] text-[var(--m-text-muted)] leading-[1.35] [&_strong]:text-[var(--m-text)] [&_strong]:font-semibold">
                    Nada para "<strong>{{ searchQuery }}</strong>"
                </span>
            </div>

            <!-- Hint inicial — Message PrimeVue (auto-resolve via PrimeVueResolver) -->
            <Message
                v-else
                severity="info"
                :closable="false"
                icon="pi pi-info-circle"
                class="ma-tsearch__hint mx-2 mb-2.5"
            >
                Digite uma data (<strong>20/04</strong>, <strong>hoje</strong>, <strong>amanhã</strong>) ou
                o nome do paciente (<strong>André</strong>).
            </Message>
        </div>
    </Popover>
</template>

<style scoped>
/* Estilos que NAO migram pra utilities Tailwind:
   - .ma-tsearch__input.p-inputtext: override do PrimeVue InputText pra ele
     viver dentro do "field box" (zera bordas, bg, shadow padrao). Selector
     composto com classe externa do PrimeVue.
   - .ma-tsearch__hint :deep(.p-message-text): override de typography dentro
     do componente filho Message (isolado por scope).
   - .ma-tsearch__result--date: a versao "Ir para data" tem cores azul fixas
     (rgb diretas, sem var()) com !important pra blindar contra hover do
     base .ma-tsearch__result. State modifier mais limpo em CSS. */

.ma-tsearch__input.p-inputtext {
    flex: 1;
    border: none;
    background: transparent;
    outline: none;
    box-shadow: none;
    padding: 8px 0;
    font-size: 0.9rem;
    color: var(--m-text);
    min-width: 0;
}
.ma-tsearch__input.p-inputtext:enabled:focus,
.ma-tsearch__input.p-inputtext:enabled:hover {
    box-shadow: none;
    border-color: transparent;
    background: transparent;
}
.ma-tsearch__input.p-inputtext::placeholder { color: var(--m-text-muted); }

.ma-tsearch__hint :deep(.p-message-text) {
    font-size: 0.82rem;
    line-height: 1.4;
}
.ma-tsearch__hint strong { font-weight: 600; }

/* "Ir para data" — destaque azul claro independente da primary do tenant.
   Cores diretas (sem color-mix com bg-soft) pra garantir contraste em
   dark mode onde --m-bg-soft tem alpha 50% e diluiria o tint demais.
   !important pra blindar contra hover do .ma-tsearch__result base. */
.ma-tsearch__result--date {
    background: rgba(59, 130, 246, 0.16) !important;
    border: 1.5px solid rgba(59, 130, 246, 0.55) !important;
}
.ma-tsearch__result--date:hover,
.ma-tsearch__result--date:focus-visible {
    background: rgba(59, 130, 246, 0.26) !important;
    border-color: rgba(59, 130, 246, 0.75) !important;
}
.ma-tsearch__result--date .ma-tsearch__result-icon {
    background: #3b82f6 !important;
    color: white !important;
}
.ma-tsearch__result--date .ma-tsearch__result-title {
    color: #2563eb !important; /* blue-600 — boa leitura em ambos os modos */
}
/* Dark mode — clareia o título pra contrastar com o fundo escuro */
html.app-dark .ma-tsearch__result--date .ma-tsearch__result-title {
    color: #93c5fd !important; /* blue-300 */
}
html.app-dark .ma-tsearch__result--date .ma-tsearch__result-sub {
    color: rgba(147, 197, 253, 0.7) !important;
}
</style>
