<!--
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/features/patients/tags/TagsPage.vue
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
-->
<script setup>
import Checkbox from 'primevue/checkbox';
import ColorPicker from 'primevue/colorpicker';
import Menu from 'primevue/menu';
import { useConfirm } from 'primevue/useconfirm';
import { useToast } from 'primevue/usetoast';
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
import { useRouter } from 'vue-router';

import PatientCadastroDialog from '@/components/ui/PatientCadastroDialog.vue';
import { supabase } from '@/lib/supabase/client';
import { tenantDb } from '@/lib/supabase/tenantClient';
import { logError } from '@/support/supportLogger';

const router = useRouter();
const toast = useToast();
const confirm = useConfirm();

// ── Hero sticky ───────────────────────────────────────────
const headerEl = ref(null);
const headerSentinelRef = ref(null);
const headerStuck = ref(false);
let _observer = null;

// ── Mobile ────────────────────────────────────────────────
const mobileMenuRef = ref(null);
const searchDlgOpen = ref(false);

const mobileMenuItems = computed(() => [
    { label: 'Adicionar', icon: 'pi pi-plus', command: () => abrirCriar() },
    {
        label: 'Buscar',
        icon: 'pi pi-search',
        command: () => {
            searchDlgOpen.value = true;
        }
    },
    ...(etiquetasSelecionadas.value?.length ? [{ separator: true }, { label: 'Excluir selecionados', icon: 'pi pi-trash', command: () => confirmarExclusaoSelecionadas() }] : []),
    { separator: true },
    { label: 'Recarregar', icon: 'pi pi-refresh', command: () => buscarEtiquetas() }
]);

const dt = ref(null);
const carregando = ref(false);
const hasLoaded = ref(false);
const etiquetas = ref([]);
const etiquetasSelecionadas = ref([]);

const filtros = ref({ global: { value: null, matchMode: 'contains' } });

// ── Quick-stats ───────────────────────────────────────────
const quickStats = computed(() => {
    const all = etiquetas.value || [];
    const padrao = all.filter((t) => t.is_padrao).length;
    const minhas = all.filter((t) => !t.is_padrao).length;
    const emUso = cards.value.length;
    const totalPac = all.reduce((s, t) => s + Number(t.pacientes_count ?? 0), 0);
    return [
        { label: 'Total de tags', value: all.length, cls: '' },
        { label: 'Tags padrão', value: padrao, cls: '' },
        { label: 'Minhas tags', value: minhas, cls: minhas > 0 ? 'qs-ok' : '' },
        { label: 'Tags em uso', value: emUso, cls: emUso > 0 ? 'qs-ok' : '' },
        { label: 'Total pacientes', value: totalPac, cls: '' }
    ];
});

// ── Dialog create/edit ────────────────────────────────────
const dlg = reactive({ open: false, mode: 'create', id: '', nome: '', cor: '', saving: false });

const tagPresetColors = [
    { bg: '6366f1', name: 'Índigo' },
    { bg: '8b5cf6', name: 'Violeta' },
    { bg: 'ec4899', name: 'Rosa' },
    { bg: 'ef4444', name: 'Vermelho' },
    { bg: 'f97316', name: 'Laranja' },
    { bg: 'eab308', name: 'Amarelo' },
    { bg: '22c55e', name: 'Verde' },
    { bg: '14b8a6', name: 'Teal' },
    { bg: '3b82f6', name: 'Azul' },
    { bg: '06b6d4', name: 'Ciano' },
    { bg: '64748b', name: 'Ardósia' },
    { bg: '292524', name: 'Escuro' }
];

const tagDlgPreviewColor = computed(() => {
    if (!dlg.cor) return '#64748b';
    const s = String(dlg.cor).trim();
    return s.startsWith('#') ? s : `#${s}`;
});

const tagPresetBgValues = tagPresetColors.map((p) => p.bg);
const isCustomColor = computed(() => {
    if (!dlg.cor) return false;
    return !tagPresetBgValues.includes(String(dlg.cor).replace('#', '').toLowerCase());
});

// ── Modal pacientes ───────────────────────────────────────
const modalPacientes = reactive({ open: false, loading: false, error: '', tag: null, items: [], search: '' });

const modalTagHex = computed(() => {
    const cor = modalPacientes.tag?.cor;
    if (!cor) return '#6366f1';
    return cor.startsWith('#') ? cor : `#${cor}`;
});

const modalPacientesFiltrado = computed(() => {
    const s = String(modalPacientes.search || '')
        .trim()
        .toLowerCase();
    if (!s) return modalPacientes.items || [];
    return (modalPacientes.items || []).filter(
        (p) =>
            String(p.name || '')
                .toLowerCase()
                .includes(s) ||
            String(p.email || '')
                .toLowerCase()
                .includes(s) ||
            String(p.phone || '')
                .toLowerCase()
                .includes(s)
    );
});

// ── Cards painel lateral ──────────────────────────────────
const cards = computed(() => (etiquetas.value || []).filter((t) => Number(t.pacientes_count ?? 0) > 0).sort((a, b) => Number(b.pacientes_count ?? 0) - Number(a.pacientes_count ?? 0)));

// ── Lifecycle ─────────────────────────────────────────────
onMounted(() => {
    const rootMargin = `${document.querySelector('.l2-main') ? '0px' : '-56px'} 0px 0px 0px`;
    _observer = new IntersectionObserver(
        ([entry]) => {
            headerStuck.value = !entry.isIntersecting;
        },
        { threshold: 0, rootMargin }
    );
    if (headerSentinelRef.value) _observer.observe(headerSentinelRef.value);
    buscarEtiquetas();
});

onBeforeUnmount(() => {
    _observer?.disconnect();
});

// ── Auth helpers ──────────────────────────────────────────
async function getOwnerId() {
    const { data, error } = await supabase.auth.getUser();
    if (error) throw error;
    const user = data?.user;
    if (!user) throw new Error('Você precisa estar logado.');
    return user.id;
}

async function getActiveTenantId(uid) {
    const { data, error } = await supabase.from('tenant_members').select('tenant_id').eq('user_id', uid).eq('status', 'active').order('created_at', { ascending: false }).limit(1).single();
    if (error) throw error;
    if (!data?.tenant_id) throw new Error('Tenant não encontrado.');
    return data.tenant_id;
}

// ── Normalização ──────────────────────────────────────────
function normalizarEtiquetaRow(r) {
    const nome = r?.nome ?? r?.name ?? '';
    const cor = r?.cor ?? r?.color ?? null;
    const is_padrao = Boolean(r?.is_padrao ?? r?.is_native ?? false);
    const pacientes_count = Number(r?.pacientes_count ?? r?.patient_count ?? r?.patients_count ?? 0);
    return { ...r, nome, cor, is_padrao, pacientes_count };
}

function isUniqueViolation(e) {
    return e?.code === '23505' || /duplicate key value/i.test(String(e?.message || ''));
}

// ── Seleção ───────────────────────────────────────────────
function estaSelecionada(row) {
    return (etiquetasSelecionadas.value || []).some((s) => s.id === row.id);
}

function alternarSelecao(row, checked) {
    if (row.is_padrao) return;
    const sel = etiquetasSelecionadas.value || [];
    etiquetasSelecionadas.value = checked ? (sel.some((s) => s.id === row.id) ? sel : [...sel, row]) : sel.filter((s) => s.id !== row.id);
}

// ── Fetch ─────────────────────────────────────────────────
async function buscarEtiquetas() {
    carregando.value = true;
    try {
        const ownerId = await getOwnerId();

        // 1) tenta view com contagem
        const v = await tenantDb().from('v_tag_patient_counts').select('*').eq('owner_id', ownerId).order('nome', { ascending: true });

        if (!v.error) {
            etiquetas.value = (v.data || []).map(normalizarEtiquetaRow);
            return;
        }

        // 2) fallback tabela direta
        const t = await tenantDb().from('patient_tags').select('id, owner_id, nome, cor, is_padrao, name, color, is_native, created_at, updated_at').eq('owner_id', ownerId).order('nome', { ascending: true });

        if (t.error && /column .*nome/i.test(String(t.error.message || ''))) {
            const t2 = await tenantDb().from('patient_tags').select('id, owner_id, name, color, is_native, created_at, updated_at').eq('owner_id', ownerId).order('name', { ascending: true });
            if (t2.error) throw t2.error;
            etiquetas.value = (t2.data || []).map((r) => normalizarEtiquetaRow({ ...r, patient_count: 0 }));
            return;
        }

        if (t.error) throw t.error;
        etiquetas.value = (t.data || []).map((r) => normalizarEtiquetaRow({ ...r, pacientes_count: 0 }));
    } catch (e) {
        logError('patients.tags', 'buscarEtiquetas', e);
        toast.add({ severity: 'error', summary: 'Erro ao carregar tags', detail: e?.message || 'Verifique se as tabelas/views existem.', life: 6000 });
    } finally {
        carregando.value = false;
        hasLoaded.value = true;
    }
}

// ── Dialog CRUD ───────────────────────────────────────────
function abrirCriar() {
    dlg.open = true;
    dlg.mode = 'create';
    dlg.id = '';
    dlg.nome = '';
    dlg.cor = '';
}
function abrirEditar(row) {
    dlg.open = true;
    dlg.mode = 'edit';
    dlg.id = row.id;
    dlg.nome = row.nome || '';
    dlg.cor = row.cor || '';
}
function fecharDlg() {
    dlg.open = false;
}

async function salvarDlg() {
    const nome = String(dlg.nome || '').trim();
    if (!nome) return;
    dlg.saving = true;
    try {
        const ownerId = await getOwnerId();
        const raw = String(dlg.cor || '').trim();
        const cor = raw ? `#${raw.replace('#', '')}` : null;

        if (dlg.mode === 'create') {
            const tenantId = await getActiveTenantId(ownerId);
            const res = await tenantDb().from('patient_tags').insert({ owner_id: ownerId, nome, cor });
            if (res.error) throw res.error;
            toast.add({ severity: 'success', summary: 'Tag criada', detail: nome, life: 2500 });
        } else {
            let res = await tenantDb().from('patient_tags').update({ nome, cor, updated_at: new Date().toISOString() }).eq('id', dlg.id).eq('owner_id', ownerId);
            // fallback legado
            if (res.error && /column .*nome/i.test(String(res.error.message || ''))) {
                res = await tenantDb().from('patient_tags').update({ name: nome, color: cor, updated_at: new Date().toISOString() }).eq('id', dlg.id).eq('owner_id', ownerId);
            }
            if (res.error) throw res.error;
            toast.add({ severity: 'success', summary: 'Tag atualizada', detail: nome, life: 2500 });
        }
        dlg.open = false;
        await buscarEtiquetas();
    } catch (e) {
        logError('patients.tags', 'salvarDlg', e);
        if (isUniqueViolation(e)) {
            toast.add({ severity: 'warn', summary: 'Tag já existe', detail: `Já existe uma tag chamada "${String(dlg.nome || '').trim()}". Tente outro nome.`, life: 4500 });
            return;
        }
        toast.add({ severity: 'error', summary: 'Não consegui salvar', detail: e?.message || 'Erro ao salvar a tag.', life: 6000 });
    } finally {
        dlg.saving = false;
    }
}

// ── Delete ────────────────────────────────────────────────
function confirmarExclusaoUma(row) {
    confirm.require({
        message: `Excluir a tag "${row.nome}"? (Remove também os vínculos com pacientes)`,
        header: 'Confirmar exclusão',
        icon: 'pi pi-exclamation-triangle',
        acceptLabel: 'Excluir',
        rejectLabel: 'Cancelar',
        accept: async () => excluirTags([row])
    });
}

function confirmarExclusaoSelecionadas() {
    const rows = etiquetasSelecionadas.value || [];
    if (!rows.length) return;
    const nomes = rows
        .slice(0, 5)
        .map((r) => r.nome)
        .join(', ');
    confirm.require({
        message: rows.length <= 5 ? `Excluir: ${nomes}? (remove também os vínculos)` : `Excluir ${rows.length} tags selecionadas? (remove também os vínculos)`,
        header: 'Confirmar exclusão',
        icon: 'pi pi-exclamation-triangle',
        acceptLabel: 'Excluir',
        rejectLabel: 'Cancelar',
        accept: async () => excluirTags(rows)
    });
}

async function excluirTags(rows) {
    if (!rows?.length) return;
    try {
        const ownerId = await getOwnerId();
        const ids = rows.filter((r) => !r.is_padrao).map((r) => r.id);
        if (!ids.length) {
            toast.add({ severity: 'warn', summary: 'Nada para excluir', detail: 'Tags padrão não podem ser removidas.', life: 4000 });
            return;
        }
        const pivotDel = await tenantDb().from('patient_patient_tag').delete().eq('owner_id', ownerId).in('tag_id', ids);
        if (pivotDel.error) throw pivotDel.error;
        const tagDel = await tenantDb().from('patient_tags').delete().eq('owner_id', ownerId).in('id', ids);
        if (tagDel.error) throw tagDel.error;
        etiquetasSelecionadas.value = [];
        toast.add({ severity: 'success', summary: 'Excluído', detail: `${ids.length} tag(s) removida(s).`, life: 3000 });
        await buscarEtiquetas();
    } catch (e) {
        logError('patients.tags', 'excluirTags', e);
        toast.add({ severity: 'error', summary: 'Não consegui excluir', detail: e?.message || 'Erro ao excluir tags.', life: 6000 });
    }
}

// ── Modal pacientes da tag ────────────────────────────────
async function abrirModalPacientesDaTag(tag) {
    modalPacientes.open = true;
    modalPacientes.tag = tag;
    modalPacientes.items = [];
    modalPacientes.search = '';
    modalPacientes.error = '';
    await carregarPacientesDaTag(tag);
}

async function recarregarModalPacientes() {
    if (!modalPacientes.tag) return;
    await carregarPacientesDaTag(modalPacientes.tag);
}

async function carregarPacientesDaTag(tag) {
    modalPacientes.loading = true;
    modalPacientes.error = '';
    try {
        const ownerId = await getOwnerId();
        const { data, error } = await tenantDb().from('patient_patient_tag').select('patient_id, patients:patients(id, nome_completo, email_principal, telefone, avatar_url)').eq('owner_id', ownerId).eq('tag_id', tag.id);
        if (error) throw error;
        modalPacientes.items = (data || [])
            .map((r) => r.patients)
            .filter(Boolean)
            .map((p) => ({ id: p.id, name: p.nome_completo || '—', email: p.email_principal || '—', phone: p.telefone || '—', avatar_url: p.avatar_url || null }))
            .sort((a, b) => String(a.name).localeCompare(String(b.name), 'pt-BR'));
    } catch (e) {
        logError('patients.tags', 'carregarPacientesDaTag', e);
        modalPacientes.error = e?.message || 'Não consegui carregar os pacientes desta tag.';
    } finally {
        modalPacientes.loading = false;
    }
}

// ── Helpers ───────────────────────────────────────────────
function onlyDigits(v) {
    return String(v ?? '').replace(/\D/g, '');
}

function fmtPhoneBR(v) {
    const d = onlyDigits(v);
    if (!d) return '—';
    if ((d.length === 12 || d.length === 13) && d.startsWith('55')) return fmtPhoneBR(d.slice(2));
    if (d.length === 11) return `(${d.slice(0, 2)}) ${d.slice(2, 7)}-${d.slice(7, 11)}`;
    if (d.length === 10) return `(${d.slice(0, 2)}) ${d.slice(2, 6)}-${d.slice(6, 10)}`;
    return d;
}

const editPatientId = ref(null);
const editPatientDialog = ref(false);
function abrirPaciente(patient) {
    if (!patient?.id) return;
    editPatientId.value = String(patient.id);
    editPatientDialog.value = true;
}
watch(editPatientDialog, (isOpen) => {
    if (!isOpen) editPatientId.value = null;
});

const HIGHLIGHT_MS = 24 * 60 * 60 * 1000;
function isRecent(row) {
    if (!row?.created_at) return false;
    return Date.now() - new Date(row.created_at).getTime() < HIGHLIGHT_MS;
}
</script>

<template>
    <PatientCadastroDialog v-model="editPatientDialog" :patient-id="editPatientId" />

    <!-- Sentinel -->
    <div ref="headerSentinelRef" class="h-px" />

    <!-- ══════════════════════════════════════════════════════
       HERO sticky
  ═══════════════════════════════════════════════════════ -->
    <section
        ref="headerEl"
        class="sticky mx-3 md:mx-4 my-3 z-20 overflow-hidden rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)] px-3 py-2.5 transition-[border-radius] duration-200"
        :class="{ 'rounded-tl-none rounded-tr-none': headerStuck }"
        :style="{ top: 'var(--layout-sticky-top, 56px)' }"
    >
        <!-- Blobs -->
        <div class="absolute inset-0 pointer-events-none overflow-hidden" aria-hidden="true">
            <div class="absolute w-64 h-64 -top-16 -right-8 rounded-full blur-[60px] bg-pink-400/[0.09]" />
            <div class="absolute w-72 h-72 top-0 -left-16 rounded-full blur-[60px] bg-indigo-500/[0.09]" />
        </div>

        <div class="relative z-1 flex items-center gap-3">
            <!-- Brand -->
            <div class="flex items-center gap-2 shrink-0">
                <div class="grid place-items-center w-9 h-9 rounded-md shrink-0 bg-indigo-500/10 text-indigo-500">
                    <i class="pi pi-tags text-base" />
                </div>
                <div class="min-w-0 hidden lg:block">
                    <div class="text-[1rem] font-bold tracking-tight text-[var(--text-color)]">Tags de Pacientes</div>
                    <div class="text-[1rem] text-[var(--text-color-secondary)]">Classifique pacientes por temas — ex.: Burnout, Ansiedade, Triagem</div>
                </div>
            </div>

            <!-- Busca desktop -->
            <div class="hidden xl:flex flex-1 min-w-0 mx-2">
                <div class="w-64">
                    <FloatLabel variant="on">
                        <IconField class="w-full">
                            <InputIcon class="pi pi-search" />
                            <InputText id="tagSearch" v-model="filtros.global.value" class="w-full" :disabled="carregando" />
                        </IconField>
                        <label for="tagSearch">Buscar tag...</label>
                    </FloatLabel>
                </div>
            </div>

            <!-- Ações desktop -->
            <div class="hidden xl:flex items-center gap-1 shrink-0">
                <Button v-if="etiquetasSelecionadas?.length" label="Excluir selecionados" icon="pi pi-trash" severity="danger" outlined class="rounded-full" @click="confirmarExclusaoSelecionadas" />
                <Button label="Nova tag" icon="pi pi-plus" class="rounded-full" @click="abrirCriar" />
                <Button icon="pi pi-refresh" severity="secondary" outlined class="h-9 w-9 rounded-full" :loading="carregando" @click="buscarEtiquetas" />
            </div>

            <!-- Mobile -->
            <div class="flex xl:hidden items-center gap-1 shrink-0 ml-auto">
                <Button icon="pi pi-search" severity="secondary" outlined class="h-9 w-9 rounded-full" @click="searchDlgOpen = true" />
                <Button icon="pi pi-plus" class="h-9 w-9 rounded-full" @click="abrirCriar" />
                <Button label="Ações" icon="pi pi-ellipsis-v" severity="secondary" size="small" class="rounded-full" @click="(e) => mobileMenuRef.toggle(e)" />
                <Menu ref="mobileMenuRef" :model="mobileMenuItems" :popup="true" />
            </div>
        </div>
    </section>

    <!-- Dialog busca mobile -->
    <Dialog v-model:visible="searchDlgOpen" modal :draggable="false" pt:mask:class="backdrop-blur-xs" header="Buscar tag" class="w-[94vw] max-w-sm">
        <div class="pt-1">
            <InputGroup>
                <InputGroupAddon><i class="pi pi-search" /></InputGroupAddon>
                <InputText v-model="filtros.global.value" placeholder="Nome da tag..." autofocus />
                <Button v-if="filtros.global.value" icon="pi pi-times" severity="secondary" @click="filtros.global.value = null" />
            </InputGroup>
        </div>
        <template #footer>
            <Button label="Fechar" severity="secondary" outlined class="rounded-full" @click="searchDlgOpen = false" />
        </template>
    </Dialog>

    <!-- ══════════════════════════════════════════════════════
       QUICK-STATS
  ═══════════════════════════════════════════════════════ -->
    <div class="grid grid-cols-2 md:grid-cols-4 gap-2 px-3 md:px-4 mb-3">
        <template v-if="carregando">
            <Skeleton v-for="n in 4" :key="n" height="3.5rem" class="rounded-md" />
        </template>
        <template v-else>
            <div
                v-for="s in quickStats"
                :key="s.label"
                class="flex flex-col gap-0.5 px-4 py-2.5 rounded-md border flex-1 min-w-[80px] transition-colors duration-150"
                :class="{
                    'border-green-500/25 bg-green-500/5': s.cls === 'qs-ok',
                    'border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)]': !s.cls
                }"
            >
                <div class="text-[1.35rem] font-bold leading-none" :class="s.cls === 'qs-ok' ? 'text-green-500' : 'text-[var(--text-color)]'">{{ s.value }}</div>
                <div class="text-[1rem] text-[var(--text-color-secondary)] opacity-75 truncate">{{ s.label }}</div>
            </div>
        </template>
    </div>

    <!-- ══════════════════════════════════════════════════════
       CONTEÚDO: tabela (esq.) + painel lateral (dir.)
  ═══════════════════════════════════════════════════════ -->
    <div class="flex flex-col lg:flex-row gap-3 px-3 md:px-4 pb-5">
        <!-- ── TABELA ──────────────────────────────────────── -->
        <div class="w-full lg:flex-1 min-w-0">
            <div class="rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)] overflow-hidden">
                <!-- Cabeçalho da seção -->
                <div class="flex items-center justify-between px-4 py-3 border-b border-[var(--surface-border,#e2e8f0)]">
                    <div class="flex items-center gap-2">
                        <i class="pi pi-table text-[var(--text-color-secondary)] opacity-60" />
                        <span class="font-semibold text-[1rem]">Lista de tags</span>
                    </div>
                    <span class="inline-flex items-center justify-center min-w-[22px] h-[22px] px-1.5 rounded-full bg-[var(--primary-color,#6366f1)] text-white text-[1rem] font-bold">
                        {{ etiquetas.length }}
                    </span>
                </div>

                <DataTable
                    ref="dt"
                    :value="etiquetas"
                    dataKey="id"
                    :loading="carregando"
                    paginator
                    :rows="10"
                    :rowsPerPageOptions="[10, 20, 50]"
                    stripedRows
                    responsiveLayout="scroll"
                    :filters="filtros"
                    filterDisplay="menu"
                    paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
                    currentPageReportTemplate="Mostrando {first} a {last} de {totalRecords} tags"
                    class="tags-datatable"
                    :rowClass="(r) => (isRecent(r) ? 'row-new-highlight' : '')"
                >
                    <!-- Seleção -->
                    <Column :exportable="false" headerStyle="width: 3rem">
                        <template #body="{ data }">
                            <Checkbox :binary="true" :disabled="!!data.is_padrao" :modelValue="estaSelecionada(data)" @update:modelValue="alternarSelecao(data, $event)" />
                        </template>
                    </Column>

                    <Column field="nome" header="Tag" sortable style="min-width: 18rem">
                        <template #body="{ data }">
                            <div class="flex items-center gap-2 min-w-0">
                                <!-- Barra lateral colorida -->
                                <span class="w-[3px] h-5 rounded-sm shrink-0" :style="{ background: data.cor || '#94a3b8' }" />
                                <span class="font-medium truncate">{{ data.nome }}</span>
                                <span v-if="data.is_padrao" class="inline-flex items-center px-1.5 py-px rounded text-[0.65rem] font-semibold bg-[var(--surface-border,#e2e8f0)] text-[var(--text-color-secondary)] shrink-0">padrão</span>
                            </div>
                        </template>
                    </Column>

                    <Column header="Pacientes" sortable sortField="pacientes_count" style="width: 10rem">
                        <template #body="{ data }">
                            <button
                                v-if="Number(data.pacientes_count ?? 0) > 0"
                                class="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-semibold cursor-pointer border-0 transition-opacity hover:opacity-75"
                                :style="{ background: `${data.cor ? (data.cor.startsWith('#') ? data.cor : '#' + data.cor) : '#6366f1'}18`, color: data.cor ? (data.cor.startsWith('#') ? data.cor : '#' + data.cor) : '#6366f1' }"
                                @click="abrirModalPacientesDaTag(data)"
                            >
                                <i class="pi pi-users text-[0.6rem]" />
                                {{ data.pacientes_count }}
                            </button>
                            <span v-else class="text-[var(--text-color-secondary)] opacity-40 text-xs">—</span>
                        </template>
                    </Column>

                    <Column header="Ações" style="width: 9rem">
                        <template #body="{ data }">
                            <div class="flex gap-1.5 justify-end">
                                <Button icon="pi pi-pencil" severity="secondary" outlined rounded size="small" :disabled="data.is_padrao" v-tooltip.top="data.is_padrao ? 'Tags padrão não podem ser editadas' : 'Editar'" @click="abrirEditar(data)" />
                                <Button
                                    icon="pi pi-trash"
                                    severity="danger"
                                    outlined
                                    rounded
                                    size="small"
                                    :disabled="data.is_padrao"
                                    v-tooltip.top="data.is_padrao ? 'Tags padrão não podem ser excluídas' : 'Excluir'"
                                    @click="confirmarExclusaoUma(data)"
                                />
                            </div>
                        </template>
                    </Column>

                    <template #empty>
                        <div class="py-10 text-center">
                            <div class="mx-auto mb-3 grid h-12 w-12 place-items-center rounded-md bg-indigo-500/10 text-indigo-500">
                                <i class="pi pi-search text-xl" />
                            </div>
                            <div class="font-semibold text-[var(--text-color)]">Nenhuma tag encontrada</div>
                            <div class="mt-1 text-[1rem] text-[var(--text-color-secondary)]">Tente limpar filtros ou mudar o termo de busca.</div>
                            <div class="mt-4 flex justify-center gap-2">
                                <Button severity="secondary" outlined icon="pi pi-filter-slash" label="Limpar filtros" @click="filtros.global.value = null" />
                                <Button icon="pi pi-plus" label="Criar tag" @click="abrirCriar" />
                            </div>
                        </div>
                    </template>
                </DataTable>
            </div>
            <LoadedPhraseBlock v-if="hasLoaded" class="mt-3" />
        </div>

        <!-- ── PAINEL LATERAL: tags mais usadas ─────────────── -->
        <div class="w-full lg:w-[272px] lg:shrink-0">
            <div class="rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)] overflow-hidden">
                <!-- Header do painel -->
                <div class="flex items-center gap-2.5 px-3.5 pt-3 pb-2.5 border-b border-[var(--surface-border,#f1f5f9)]">
                    <div class="w-8 h-8 rounded-md flex items-center justify-center shrink-0 bg-pink-500/10 text-pink-500">
                        <i class="pi pi-tags text-[0.9rem]" />
                    </div>
                    <div class="flex-1 min-w-0">
                        <span class="block text-[1rem] font-bold text-[var(--text-color)]">Mais usadas</span>
                        <span class="block text-[0.72rem] text-[var(--text-color-secondary)]">Tags com pacientes associados</span>
                    </div>
                    <span v-if="cards.length" class="inline-flex items-center justify-center min-w-[20px] h-5 px-1 rounded-full bg-[var(--primary-color,#6366f1)] text-white text-[0.65rem] font-bold shrink-0">{{ cards.length }}</span>
                </div>

                <!-- Skeleton -->
                <div v-if="carregando" class="flex flex-col gap-2 p-3">
                    <Skeleton v-for="n in 4" :key="n" height="2.75rem" class="rounded-md" />
                </div>

                <!-- Empty -->
                <div v-else-if="cards.length === 0" class="flex flex-col items-center justify-center gap-2 px-4 py-8 text-center text-[var(--text-color-secondary)]">
                    <i class="pi pi-tags text-2xl opacity-20" />
                    <div class="font-semibold text-[0.8rem]">Nenhuma tag em uso</div>
                    <div class="text-[0.72rem] opacity-70 leading-relaxed">As tags mais usadas aparecem aqui quando houver pacientes associados.</div>
                </div>

                <!-- Lista das tags -->
                <div v-else class="flex flex-col max-h-[480px] overflow-y-auto divide-y divide-[var(--surface-border,#f1f5f9)]">
                    <button
                        v-for="t in cards"
                        :key="t.id"
                        class="flex items-center gap-2.5 px-3.5 py-2.5 text-left w-full bg-transparent border-none hover:bg-[var(--surface-ground,#f8fafc)] transition-colors duration-100 cursor-pointer group"
                        @click="abrirModalPacientesDaTag(t)"
                    >
                        <!-- Barra de cor -->
                        <div class="w-[3px] h-5 rounded-sm shrink-0" :style="{ background: t.cor || '#94a3b8' }" />

                        <div class="flex-1 min-w-0">
                            <div class="font-semibold text-[0.8rem] truncate text-[var(--text-color)] flex items-center gap-1.5">
                                {{ t.nome }}
                                <span v-if="t.is_padrao" class="inline-flex items-center px-1 py-px rounded text-[0.6rem] font-semibold bg-[var(--surface-border,#e2e8f0)] text-[var(--text-color-secondary)]">padrão</span>
                            </div>
                            <div class="text-[1rem] text-[var(--text-color-secondary)]">{{ Number(t.pacientes_count ?? 0) }} paciente(s)</div>
                        </div>

                        <!-- Badge contagem com cor da tag -->
                        <span
                            class="inline-flex items-center justify-center min-w-[22px] h-[22px] px-1 rounded-full font-bold text-[0.68rem] shrink-0"
                            :style="{ background: `${t.cor ? (t.cor.startsWith('#') ? t.cor : '#' + t.cor) : '#6366f1'}18`, color: t.cor ? (t.cor.startsWith('#') ? t.cor : '#' + t.cor) : '#6366f1' }"
                            >{{ Number(t.pacientes_count ?? 0) }}</span
                        >
                        <i class="pi pi-chevron-right text-[0.6rem] opacity-30 group-hover:opacity-100 transition-all duration-150 shrink-0" :style="{ color: 'var(--text-color-secondary)' }" />
                    </button>
                </div>

                <!-- Footer hint -->
                <div v-if="cards.length" class="px-3.5 py-2 text-[1rem] text-[var(--text-color-secondary)] opacity-50 border-t border-[var(--surface-border,#f1f5f9)] text-center">Clique para ver os pacientes da tag</div>
            </div>
        </div>
    </div>

    <!-- ══════════════════════════════════════════════════════
       Dialog: Criar / Editar tag
  ═══════════════════════════════════════════════════════ -->
    <Dialog
        v-model:visible="dlg.open"
        modal
        :draggable="false"
        :closable="!dlg.saving"
        :dismissableMask="!dlg.saving"
        class="w-[96vw] max-w-lg"
        :pt="{ content: { class: 'p-0' }, header: { class: 'pb-0' }, footer: { class: 'pt-0' } }"
        pt:mask:class="backdrop-blur-xs"
    >
        <template #header>
            <div class="flex w-full items-center gap-3 px-1">
                <span class="shrink-0 w-3.5 h-3.5 rounded-full border-2 border-white/30 shadow-[0_0_0_3px_rgba(0,0,0,0.08)] transition-colors duration-200" :style="{ backgroundColor: tagDlgPreviewColor }" />
                <div class="min-w-0">
                    <div class="text-[0.95rem] font-semibold truncate text-[var(--text-color)]">
                        {{ dlg.nome || (dlg.mode === 'create' ? 'Nova tag' : 'Editar tag') }}
                    </div>
                    <div class="text-[0.72rem] text-[var(--text-color-secondary)] opacity-70">
                        {{ dlg.mode === 'create' ? 'Criar nova tag' : 'Editando tag' }}
                    </div>
                </div>
            </div>
        </template>

        <!-- Banner preview -->
        <div class="h-16 flex items-center justify-center rounded-md mb-4 transition-colors duration-[250ms]" :style="{ backgroundColor: tagDlgPreviewColor }">
            <span class="text-[0.9rem] font-bold tracking-tight px-4 py-1.5 bg-black/15 rounded-full backdrop-blur-sm text-white">
                {{ dlg.nome || 'Nome da tag' }}
            </span>
        </div>

        <!-- Corpo -->
        <div class="flex flex-col gap-4">
            <FloatLabel variant="on">
                <IconField>
                    <InputIcon class="pi pi-tag" />
                    <InputText id="tag-nome" v-model="dlg.nome" class="w-full" variant="filled" :disabled="dlg.saving" @keydown.enter.prevent="salvarDlg" />
                </IconField>
                <label for="tag-nome">Nome da tag *</label>
            </FloatLabel>

            <!-- Seletor de cor -->
            <div class="border border-[var(--surface-border,#e2e8f0)] rounded-md bg-[var(--surface-ground,#f8fafc)] p-3.5">
                <div class="text-[0.68rem] font-bold uppercase tracking-[0.07em] text-[var(--text-color-secondary)] opacity-60 mb-2.5">Cor da tag</div>
                <div class="flex flex-wrap gap-[0.4rem]">
                    <!-- Presets -->
                    <button
                        v-for="p in tagPresetColors"
                        :key="p.bg"
                        class="w-7 h-7 rounded-full grid place-items-center cursor-pointer relative border-0 transition-transform duration-[120ms] hover:scale-110 hover:shadow-[0_3px_10px_rgba(0,0,0,0.2)] disabled:cursor-not-allowed"
                        :class="dlg.cor === p.bg ? 'scale-110 shadow-[0_0_0_2px_var(--text-color),0_0_0_4px_var(--surface-card,#fff)]' : ''"
                        :style="{ backgroundColor: `#${p.bg}` }"
                        :title="p.name"
                        :disabled="dlg.saving"
                        @click="dlg.cor = p.bg"
                    >
                        <i v-if="dlg.cor === p.bg" class="pi pi-check text-white font-black" style="font-size: 11px" />
                    </button>

                    <!-- Custom ColorPicker -->
                    <div
                        class="w-7 h-7 rounded-full grid place-items-center cursor-pointer overflow-hidden relative transition-transform duration-[120ms] hover:scale-110 hover:shadow-[0_3px_10px_rgba(0,0,0,0.2)]"
                        :class="isCustomColor ? 'scale-110 shadow-[0_0_0_2px_var(--text-color),0_0_0_4px_var(--surface-card,#fff)]' : ''"
                        style="background: conic-gradient(red, yellow, lime, cyan, blue, magenta, red)"
                        title="Cor personalizada"
                    >
                        <i v-if="isCustomColor" class="pi pi-check text-white font-black absolute z-10 pointer-events-none drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]" style="font-size: 11px" />
                        <ColorPicker
                            v-model="dlg.cor"
                            format="hex"
                            :disabled="dlg.saving"
                            class="absolute inset-0 [&_.p-colorpicker-preview]:w-full [&_.p-colorpicker-preview]:h-full [&_.p-colorpicker-preview]:border-0 [&_.p-colorpicker-preview]:rounded-full [&_.p-colorpicker-preview]:opacity-0"
                        />
                    </div>

                    <!-- Limpar cor -->
                    <button
                        v-if="dlg.cor"
                        class="w-7 h-7 rounded-full grid place-items-center cursor-pointer border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)] text-[var(--text-color-secondary)] transition-transform duration-[120ms] hover:scale-110 disabled:cursor-not-allowed"
                        title="Sem cor"
                        :disabled="dlg.saving"
                        @click="dlg.cor = ''"
                    >
                        <i class="pi pi-times" style="font-size: 11px" />
                    </button>
                </div>
            </div>
        </div>

        <template #footer>
            <div class="flex items-center justify-end gap-2 pt-2">
                <Button label="Cancelar" severity="secondary" outlined class="rounded-full" :disabled="dlg.saving" @click="fecharDlg" />
                <Button label="Salvar" icon="pi pi-check" class="rounded-full" :loading="dlg.saving" :disabled="!String(dlg.nome || '').trim()" @click="salvarDlg" />
            </div>
        </template>
    </Dialog>

    <!-- ══════════════════════════════════════════════════════
       Dialog: Pacientes da tag
  ═══════════════════════════════════════════════════════ -->
    <Dialog
        v-model:visible="modalPacientes.open"
        modal
        :draggable="false"
        :style="{ width: '860px', maxWidth: '96vw' }"
        :pt="{
            root: { style: `border: 4px solid ${modalTagHex}` },
            header: { style: `border-bottom: 1px solid ${modalTagHex}30` }
        }"
        pt:mask:class="backdrop-blur-xs"
    >
        <template #header>
            <div class="flex items-center gap-3">
                <div class="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold text-base shrink-0" :style="{ background: modalTagHex }">
                    {{ (modalPacientes.tag?.nome || '?')[0].toUpperCase() }}
                </div>
                <div>
                    <div class="text-[1rem] font-bold" :style="{ color: modalTagHex }">Tag — {{ modalPacientes.tag?.nome }}</div>
                    <div class="text-[0.72rem] text-[var(--text-color-secondary)]">{{ modalPacientes.items.length }} paciente{{ modalPacientes.items.length !== 1 ? 's' : '' }}</div>
                </div>
            </div>
        </template>

        <div class="flex flex-col gap-3">
            <!-- Busca + contador -->
            <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
                <IconField class="w-full sm:w-72">
                    <InputIcon><i class="pi pi-search" /></InputIcon>
                    <InputText v-model="modalPacientes.search" placeholder="Buscar paciente..." class="w-full" :disabled="modalPacientes.loading" />
                </IconField>
                <div class="flex items-center gap-2">
                    <span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-semibold" :style="{ background: `${modalTagHex}18`, color: modalTagHex }">{{ modalPacientes.items.length }} paciente(s)</span>
                    <Button icon="pi pi-refresh" outlined class="h-8 w-8 rounded-full shrink-0" :style="{ borderColor: modalTagHex, color: modalTagHex }" @click="recarregarModalPacientes" />
                </div>
            </div>

            <!-- Loading -->
            <div v-if="modalPacientes.loading" class="flex items-center gap-2 py-4" :style="{ color: modalTagHex }"><i class="pi pi-spin pi-spinner" /> Carregando…</div>

            <Message v-else-if="modalPacientes.error" severity="error">{{ modalPacientes.error }}</Message>

            <div v-else>
                <!-- Empty -->
                <div v-if="modalPacientes.items.length === 0" class="py-10 text-center">
                    <div class="mx-auto mb-3 grid h-12 w-12 place-items-center rounded-md" :style="{ background: `${modalTagHex}18`, color: modalTagHex }">
                        <i class="pi pi-users text-xl" />
                    </div>
                    <div class="font-semibold">Nenhum paciente nesta tag</div>
                    <div class="mt-1 text-sm text-[var(--text-color-secondary)]">Associe pacientes a esta tag na página de pacientes.</div>
                </div>

                <!-- Tabela -->
                <DataTable v-else :value="modalPacientesFiltrado" dataKey="id" paginator :rows="8" :rowsPerPageOptions="[8, 15, 30]" stripedRows responsiveLayout="scroll">
                    <Column field="name" header="Paciente" sortable>
                        <template #body="{ data }">
                            <div class="flex items-center gap-3">
                                <Avatar v-if="data.avatar_url" :image="data.avatar_url" shape="circle" />
                                <Avatar v-else :label="(data.name || '?')[0].toUpperCase()" shape="circle" :style="{ background: `${modalTagHex}25`, color: modalTagHex }" />
                                <div class="min-w-0">
                                    <div class="font-medium truncate">{{ data.name }}</div>
                                    <div class="text-[0.75rem] text-[var(--text-color-secondary)] truncate">{{ data.email || '—' }}</div>
                                </div>
                            </div>
                        </template>
                    </Column>

                    <Column header="Telefone" style="width: 13rem">
                        <template #body="{ data }">
                            <span class="text-[var(--text-color-secondary)]">{{ fmtPhoneBR(data.phone) }}</span>
                        </template>
                    </Column>

                    <Column header="Ação" style="width: 9rem">
                        <template #body="{ data }">
                            <Button label="Abrir" icon="pi pi-external-link" size="small" outlined :style="{ borderColor: modalTagHex, color: modalTagHex }" @click="abrirPaciente(data)" />
                        </template>
                    </Column>

                    <template #empty>
                        <div class="py-8 text-center">
                            <i class="pi pi-search text-2xl opacity-20 mb-2 block" />
                            <div class="font-semibold text-[1rem]">Nenhum resultado</div>
                            <div class="text-[0.75rem] opacity-60 mt-1">Nenhum paciente corresponde à busca.</div>
                            <Button class="mt-3" severity="secondary" outlined icon="pi pi-filter-slash" label="Limpar" size="small" @click="modalPacientes.search = ''" />
                        </div>
                    </template>
                </DataTable>
            </div>
        </div>

        <template #footer>
            <Button label="Fechar" icon="pi pi-times" outlined class="rounded-full" :style="{ borderColor: modalTagHex, color: modalTagHex }" @click="modalPacientes.open = false" />
        </template>
    </Dialog>

    <ConfirmDialog />
</template>
