<!--
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/features/agenda/pages/AgendaRecorrenciasPage.vue
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
-->
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { supabase } from '@/lib/supabase/client'
import { useTenantStore } from '@/stores/tenantStore'
import { useAgendaClinicStaff } from '@/features/agenda/composables/useAgendaClinicStaff'
import { useToast } from 'primevue/usetoast'

const route  = useRoute()
const router = useRouter()
const toast  = useToast()
const tenantStore = useTenantStore()

const mode      = computed(() => route.meta?.mode || 'therapist')
const isClinic  = computed(() => mode.value === 'clinic')
const tenantId  = computed(() => tenantStore.activeTenantId || tenantStore.tenantId)

// ── state ──────────────────────────────────────────────────────────────────────
const loading       = ref(false)
const userId        = ref(null)
const rules         = ref([])
const exceptionsMap = ref({})   // ruleId → Exception[]
const sessionsMap   = ref({})   // ruleId → AgendaEvento[]
const expandedId    = ref(null)

const filterStatus = ref('ativo')
const filterOwner  = ref(null)

const { staff, load: loadStaff } = useAgendaClinicStaff()

const staffOptions = computed(() =>
  (staff.value || []).map(s => ({
    label: s.full_name || s.nome || s.name || 'Profissional',
    value: s.user_id
  }))
)
const staffMap = computed(() => {
  const m = {}
  for (const s of staff.value || []) m[s.user_id] = s.full_name || s.nome || s.name || 'Profissional'
  return m
})

// ── auth / init ────────────────────────────────────────────────────────────────
async function init () {
  const { data } = await supabase.auth.getUser()
  userId.value = data?.user?.id || null
  if (isClinic.value && tenantId.value) await loadStaff(tenantId.value)
  await load()
}

// ── data load ──────────────────────────────────────────────────────────────────
async function load () {
  if (!userId.value) return
  loading.value = true
  try {
    let q = supabase.from('recurrence_rules').select('*').order('start_date', { ascending: false })

    if (isClinic.value) {
      if (!tenantId.value) return
      q = q.eq('tenant_id', tenantId.value)
      if (filterOwner.value) q = q.eq('owner_id', filterOwner.value)
    } else {
      q = q.eq('owner_id', userId.value)
    }
    if (filterStatus.value !== 'all') q = q.eq('status', filterStatus.value)

    const { data: rData, error: rErr } = await q
    if (rErr) throw rErr
    const rawRules = rData || []

    // patient names
    const patientIds = [...new Set(rawRules.map(r => r.patient_id).filter(Boolean))]
    const patientMap = {}
    if (patientIds.length) {
      const { data: pts } = await supabase.from('patients').select('id, nome_completo, avatar_url').in('id', patientIds)
      for (const p of pts || []) patientMap[p.id] = p
    }
    for (const r of rawRules) r._patient = patientMap[r.patient_id] || null

    rules.value = rawRules

    const ruleIds = rawRules.map(r => r.id)
    if (ruleIds.length) await reloadSessions(ruleIds)
  } catch (e) {
    toast.add({ severity: 'warn', summary: 'Erro ao carregar', detail: e?.message, life: 3500 })
  } finally {
    loading.value = false
  }
}

async function reloadSessions (ruleIds) {
  const [exRes, sessRes] = await Promise.all([
    supabase.from('recurrence_exceptions').select('*').in('recurrence_id', ruleIds).order('original_date'),
    supabase.from('agenda_eventos')
      .select('id, recurrence_id, recurrence_date, status, inicio_em, fim_em')
      .in('recurrence_id', ruleIds).order('inicio_em')
  ])
  const exm = {}
  for (const ex of exRes.data || []) {
    if (!exm[ex.recurrence_id]) exm[ex.recurrence_id] = []
    exm[ex.recurrence_id].push(ex)
  }
  exceptionsMap.value = { ...exceptionsMap.value, ...exm, ...Object.fromEntries(ruleIds.filter(id => !exm[id]).map(id => [id, []])) }

  const sm = {}
  for (const s of sessRes.data || []) {
    if (!sm[s.recurrence_id]) sm[s.recurrence_id] = []
    sm[s.recurrence_id].push(s)
  }
  sessionsMap.value = { ...sessionsMap.value, ...sm, ...Object.fromEntries(ruleIds.filter(id => !sm[id]).map(id => [id, []])) }
}

// ── date generation ────────────────────────────────────────────────────────────
function generateAllDates (rule) {
  const { type, interval = 1, weekdays = [], start_date, end_date, max_occurrences } = rule
  if (!start_date || !Array.isArray(weekdays) || !weekdays.length) return []
  const maxOcc    = Math.min(max_occurrences || 500, 500)
  const endLimitISO = end_date || null
  const dates = []

  if (type === 'custom_weekdays') {
    const cursor = new Date(start_date + 'T12:00:00')
    let safety = 0
    while (dates.length < maxOcc && safety < 3000) {
      safety++
      const iso = cursor.toISOString().slice(0, 10)
      if (endLimitISO && iso > endLimitISO) break
      if (weekdays.includes(cursor.getDay())) dates.push(iso)
      cursor.setDate(cursor.getDate() + 1)
    }
  } else {
    // weekly / biweekly
    const dow = weekdays[0]
    const cursor = new Date(start_date + 'T12:00:00')
    while (cursor.getDay() !== dow) cursor.setDate(cursor.getDate() + 1)
    while (dates.length < maxOcc) {
      const iso = cursor.toISOString().slice(0, 10)
      if (endLimitISO && iso > endLimitISO) break
      dates.push(iso)
      cursor.setDate(cursor.getDate() + 7 * (interval || 1))
    }
  }
  return dates
}

// ── sessions (merged) ──────────────────────────────────────────────────────────
const TODAY = new Date().toISOString().slice(0, 10)

function buildSessions (rule) {
  const exByDate  = {}
  for (const ex of exceptionsMap.value[rule.id] || []) exByDate[ex.original_date] = ex
  const sessByDate = {}
  for (const s of sessionsMap.value[rule.id] || []) sessByDate[s.recurrence_date] = s

  return generateAllDates(rule).map(iso => {
    const real = sessByDate[iso]
    const ex   = exByDate[iso]
    let status = 'agendado'
    if (real) {
      status = real.status || 'agendado'
    } else if (ex) {
      if (ex.type === 'cancel_session' || ex.type === 'therapist_canceled') status = 'cancelado'
      else if (ex.type === 'patient_missed') status = 'faltou'
      else if (ex.type === 'reschedule_session') status = 'remarcado'
    }
    return { date: iso, status, real_id: real?.id || null }
  })
}

// ── stats ──────────────────────────────────────────────────────────────────────
const STATUS_DONE = new Set(['compareceu', 'veio', 'realizado', 'presente'])

function ruleStats (rule) {
  const sessions  = buildSessions(rule)
  const total     = sessions.length
  const done      = sessions.filter(s => STATUS_DONE.has(s.status)).length
  const faltou    = sessions.filter(s => s.status === 'faltou').length
  const cancelado = sessions.filter(s => s.status === 'cancelado').length
  const pendentes = sessions.filter(s => s.status === 'agendado' || s.status === 'remarcado').length
  const progress  = total ? Math.round((done / total) * 100) : 0
  return { total, done, faltou, cancelado, pendentes, progress }
}

// ── formatters ─────────────────────────────────────────────────────────────────
const DIAS_SHORT = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']

function fmtDate (iso) {
  if (!iso) return ''
  const [y, m, d] = iso.split('-')
  return `${d}/${m}/${y}`
}
function fmtRuleDesc (rule) {
  const days = (rule.weekdays || []).map(d => DIAS_SHORT[d]).join(', ')
  const time = rule.start_time ? rule.start_time.slice(0, 5) : ''
  const freq = rule.interval > 1 ? `a cada ${rule.interval} sem.` : ''
  return [days, time, freq].filter(Boolean).join(' · ')
}
function fmtPeriod (rule) {
  const s = fmtDate(rule.start_date)
  if (rule.end_date) return `${s} até ${fmtDate(rule.end_date)}`
  if (rule.max_occurrences) return `${s} · ${rule.max_occurrences} sessões`
  return `A partir de ${s}`
}
function fmtPillDate (iso) {
  const [, m, d] = iso.split('-')
  const dow = DIAS_SHORT[new Date(iso + 'T12:00:00').getDay()]
  return `${dow} ${Number(d)}/${Number(m)}`
}

// ── status UI ──────────────────────────────────────────────────────────────────
const STATUS_OPTS = [
  { label: 'Agendado',   value: 'agendado' },
  { label: 'Compareceu', value: 'compareceu' },
  { label: 'Faltou',     value: 'faltou' },
  { label: 'Cancelado',  value: 'cancelado' },
  { label: 'Remarcado',  value: 'remarcado' },
]

const PILL_CLASS = {
  agendado:   'pill--pending',
  compareceu: 'pill--done',
  veio:       'pill--done',
  realizado:  'pill--done',
  presente:   'pill--done',
  faltou:     'pill--missed',
  cancelado:  'pill--canceled',
  remarcado:  'pill--rescheduled',
}

// ── actions ────────────────────────────────────────────────────────────────────
async function onPillStatusChange (rule, s, newStatus) {
  try {
    if (s.real_id) {
      await supabase.from('agenda_eventos').update({ status: newStatus }).eq('id', s.real_id)
    } else {
      const { data: ex } = await supabase
        .from('agenda_eventos').select('id')
        .eq('recurrence_id', rule.id).eq('recurrence_date', s.date).maybeSingle()
      if (ex?.id) {
        await supabase.from('agenda_eventos').update({ status: newStatus }).eq('id', ex.id)
      } else {
        await supabase.from('agenda_eventos').insert({
          recurrence_id:    rule.id,
          recurrence_date:  s.date,
          owner_id:         rule.owner_id,
          tenant_id:        rule.tenant_id,
          tipo:             'sessao',
          status:           newStatus,
          inicio_em:        s.date + 'T' + (rule.start_time || '00:00') + ':00',
          fim_em:           s.date + 'T' + (rule.end_time   || '01:00') + ':00',
          visibility_scope: 'public',
          titulo:           'Sessão',
          paciente_id:      rule.patient_id || null,
          patient_id:       rule.patient_id || null,
        })
      }
    }
    toast.add({ severity: 'success', summary: 'Status atualizado', life: 1500 })
    await reloadSessions([rule.id])
  } catch (e) {
    toast.add({ severity: 'warn', summary: 'Erro', detail: e?.message, life: 3500 })
  }
}

async function onCancelRule (rule) {
  const name = rule._patient?.nome_completo || 'paciente'
  if (!confirm(`Encerrar a série de "${name}"?\n\nSessões futuras deixarão de ser geradas. Sessões passadas já registradas são mantidas.`)) return
  try {
    await supabase.from('recurrence_rules')
      .update({ status: 'cancelado', updated_at: new Date().toISOString() })
      .eq('id', rule.id)
    toast.add({ severity: 'success', summary: 'Série encerrada', life: 2000 })
    await load()
  } catch (e) {
    toast.add({ severity: 'warn', summary: 'Erro', detail: e?.message, life: 3500 })
  }
}

async function onReactivateRule (rule) {
  try {
    await supabase.from('recurrence_rules')
      .update({ status: 'ativo', updated_at: new Date().toISOString() })
      .eq('id', rule.id)
    toast.add({ severity: 'success', summary: 'Série reativada', life: 2000 })
    await load()
  } catch (e) {
    toast.add({ severity: 'warn', summary: 'Erro', detail: e?.message, life: 3500 })
  }
}

function toggleExpand (ruleId) {
  expandedId.value = expandedId.value === ruleId ? null : ruleId
}

// ── navigation ─────────────────────────────────────────────────────────────────
function goBack () {
  if (isClinic.value) router.push({ name: 'admin-agenda-clinica' })
  else router.push({ name: 'therapist-agenda' })
}

onMounted(init)
</script>

<template>

  <!-- ─── Header ─────────────────────────────────────────────────── -->
  <div class="rr-page mx-3 md:mx-5">
    <div class="rr-header">
      <div class="flex items-center gap-3">
        <Button icon="pi pi-arrow-left" text severity="secondary" class="h-9 w-9 rounded-full shrink-0" v-tooltip.bottom="'Voltar à agenda'" @click="goBack" />
        <div>
          <div class="text-xl font-bold leading-tight">Recorrências</div>
          <div class="text-sm opacity-55">
            {{ isClinic ? 'Todas as séries da clínica' : 'Suas séries de sessões recorrentes' }}
          </div>
        </div>
      </div>

      <div class="flex items-center gap-2 flex-wrap">
        <!-- Status filter -->
        <SelectButton
          v-model="filterStatus"
          :options="[
            { label: 'Ativas',     value: 'ativo' },
            { label: 'Encerradas', value: 'cancelado' },
            { label: 'Todas',      value: 'all' }
          ]"
          optionLabel="label" optionValue="value" :allowEmpty="false"
          @change="load"
        />

        <!-- Therapist filter (clinic only) -->
        <Select
          v-if="isClinic && staffOptions.length"
          v-model="filterOwner"
          :options="[{ label: 'Todos os terapeutas', value: null }, ...staffOptions]"
          optionLabel="label" optionValue="value"
          placeholder="Todos os terapeutas"
          class="w-[220px]"
          @change="load"
        />

        <Button icon="pi pi-refresh" severity="secondary" outlined class="h-9 w-9 rounded-full" :loading="loading" v-tooltip.bottom="'Recarregar'" @click="load" />
      </div>
    </div>

    <!-- ─── Loading ──────────────────────────────────────────────── -->
    <div v-if="loading" class="flex flex-col gap-3 mt-4">
      <Skeleton v-for="i in 4" :key="i" height="130px" class="rounded-2xl" />
    </div>

    <!-- ─── Empty ────────────────────────────────────────────────── -->
    <div v-else-if="!rules.length" class="rr-empty">
      <i class="pi pi-calendar-times text-5xl opacity-25" />
      <div class="text-lg font-semibold opacity-50">Nenhuma série encontrada</div>
      <div class="text-sm opacity-35">
        {{ filterStatus === 'ativo' ? 'Crie sessões recorrentes na agenda para vê-las aqui.' : 'Altere o filtro de status.' }}
      </div>
      <Button label="Voltar à agenda" icon="pi pi-calendar" outlined severity="secondary" class="rounded-full mt-2" @click="goBack" />
    </div>

    <!-- ─── Rule cards ───────────────────────────────────────────── -->
    <div v-else class="flex flex-col gap-4 mt-4 pb-8">
      <div v-for="rule in rules" :key="rule.id" class="rr-card">

        <!-- Card head: patient info + status badge -->
        <div class="rr-card__head">
          <div class="flex items-start gap-3 min-w-0 flex-1">
            <Avatar
              :label="(rule._patient?.nome_completo || '?')[0].toUpperCase()"
              shape="circle" size="large"
              class="shrink-0"
              style="background:var(--primary-100,#e0e7ff);color:var(--primary-700,#3730a3);font-weight:700"
            />
            <div class="min-w-0 flex-1">
              <div class="font-bold text-base truncate leading-tight">
                {{ rule._patient?.nome_completo || 'Paciente não encontrado' }}
              </div>
              <div v-if="isClinic && staffMap[rule.owner_id]" class="text-xs opacity-55 mt-0.5 truncate">
                <i class="pi pi-user text-xs mr-1" />{{ staffMap[rule.owner_id] }}
              </div>
              <div class="text-sm opacity-65 mt-1">
                <i class="pi pi-clock text-xs mr-1" />{{ fmtRuleDesc(rule) }}
              </div>
              <div class="text-xs opacity-45 mt-0.5">
                <i class="pi pi-calendar text-xs mr-1" />{{ fmtPeriod(rule) }}
              </div>
            </div>
          </div>
          <Tag
            :value="rule.status === 'ativo' ? 'Ativa' : 'Encerrada'"
            :severity="rule.status === 'ativo' ? 'success' : 'secondary'"
            class="shrink-0 self-start"
          />
        </div>

        <!-- Stats + progress -->
        <template v-for="stats in [ruleStats(rule)]" :key="'stats-' + rule.id">
          <div class="rr-stats-row">
            <span class="rr-stat rr-stat--done">{{ stats.done }} compareceu</span>
            <span v-if="stats.faltou"    class="rr-stat rr-stat--missed">{{ stats.faltou }} faltou</span>
            <span v-if="stats.cancelado" class="rr-stat rr-stat--canceled">{{ stats.cancelado }} cancelada{{ stats.cancelado !== 1 ? 's' : '' }}</span>
            <span class="rr-stat rr-stat--pending">{{ stats.pendentes }} pendente{{ stats.pendentes !== 1 ? 's' : '' }}</span>
            <span class="rr-stat rr-stat--total ml-auto">{{ stats.total }} sessões</span>
          </div>
          <div class="px-4 pb-1">
            <ProgressBar :value="stats.progress" class="h-1.5 rounded-full" />
          </div>
        </template>

        <!-- Card footer: actions -->
        <div class="rr-card__foot">
          <Button
            :icon="expandedId === rule.id ? 'pi pi-chevron-up' : 'pi pi-list'"
            :label="expandedId === rule.id ? 'Ocultar sessões' : `Ver sessões (${ruleStats(rule).total})`"
            severity="secondary" outlined size="small" class="rounded-full"
            @click="toggleExpand(rule.id)"
          />
          <div class="flex gap-2 ml-auto">
            <Button
              v-if="rule.status === 'ativo'"
              label="Encerrar série"
              icon="pi pi-times-circle"
              severity="danger" text size="small" class="rounded-full"
              @click="onCancelRule(rule)"
            />
            <Button
              v-else
              label="Reativar"
              icon="pi pi-undo"
              severity="success" text size="small" class="rounded-full"
              @click="onReactivateRule(rule)"
            />
          </div>
        </div>

        <!-- Sessions panel (expanded) -->
        <div v-if="expandedId === rule.id" class="rr-sessions">
          <div class="rr-sessions__grid">
            <div
              v-for="s in buildSessions(rule)"
              :key="s.date"
              class="rr-pill"
              :class="[
                PILL_CLASS[s.status] || 'pill--pending',
                s.date < TODAY ? 'rr-pill--past' : s.date === TODAY ? 'rr-pill--today' : 'rr-pill--future'
              ]"
            >
              <div class="rr-pill__date">{{ fmtPillDate(s.date) }}</div>
              <Select
                :modelValue="s.status"
                :options="STATUS_OPTS"
                optionLabel="label" optionValue="value"
                class="rr-pill__sel"
                @change="e => onPillStatusChange(rule, s, e.value)"
              />
            </div>
          </div>
        </div>

      </div>
    </div>
  </div>
</template>

<style scoped>
/* ── Page ─────────────────────────────────────────────────────────── */
.rr-page {
  padding-bottom: 2rem;
}

/* ── Header ───────────────────────────────────────────────────────── */
.rr-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  padding: 20px 0 16px;
  border-bottom: 1px solid var(--surface-border);
  margin-bottom: 4px;
}

/* ── Empty ────────────────────────────────────────────────────────── */
.rr-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 64px 24px;
  text-align: center;
}

/* ── Card ─────────────────────────────────────────────────────────── */
.rr-card {
  border-radius: 1.25rem;
  border: 1px solid var(--surface-border);
  background: var(--surface-card);
  overflow: hidden;
  transition: box-shadow 0.15s;
}
.rr-card:hover {
  box-shadow: 0 2px 16px color-mix(in srgb, var(--primary-400, #818cf8) 10%, transparent);
}

.rr-card__head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 16px 12px;
}

.rr-card__foot {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px 14px;
  border-top: 1px solid var(--surface-border);
}

/* ── Stats row ────────────────────────────────────────────────────── */
.rr-stats-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 4px 16px 8px;
}

.rr-stat {
  font-size: 0.72rem;
  font-weight: 600;
  border-radius: 999px;
  padding: 2px 8px;
}
.rr-stat--done      { background: var(--green-100, #dcfce7);  color: var(--green-700, #15803d); }
.rr-stat--missed    { background: var(--red-100, #fee2e2);    color: var(--red-700, #b91c1c); }
.rr-stat--canceled  { background: var(--orange-100, #ffedd5); color: var(--orange-700, #c2410c); }
.rr-stat--pending   { background: var(--surface-200, #e5e7eb); color: var(--text-color-secondary); }
.rr-stat--total     { background: transparent; color: var(--text-color-secondary); font-weight: 400; }

/* ── Sessions panel ───────────────────────────────────────────────── */
.rr-sessions {
  border-top: 1px solid var(--surface-border);
  background: color-mix(in srgb, var(--surface-ground) 60%, transparent);
  padding: 14px 16px;
  max-height: 420px;
  overflow-y: auto;
}

.rr-sessions__grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* ── Pill ─────────────────────────────────────────────────────────── */
.rr-pill {
  display: flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  padding: 4px 4px 4px 10px;
  border: 1px solid transparent;
  min-width: 0;
}

.rr-pill--past   { opacity: 0.7; }
.rr-pill--today  { box-shadow: 0 0 0 2px var(--primary-400, #818cf8); opacity: 1 !important; }
.rr-pill--future { }

.pill--pending    { background: var(--surface-200, #e5e7eb);      border-color: var(--surface-300, #d1d5db);      color: var(--text-color-secondary); }
.pill--done       { background: var(--green-50, #f0fdf4);         border-color: var(--green-200, #bbf7d0);        color: var(--green-800, #166534); }
.pill--missed     { background: var(--red-50, #fff1f2);           border-color: var(--red-200, #fecaca);          color: var(--red-700, #b91c1c); }
.pill--canceled   { background: var(--orange-50, #fff7ed);        border-color: var(--orange-200, #fed7aa);       color: var(--orange-700, #c2410c); }
.pill--rescheduled{ background: var(--blue-50, #eff6ff);          border-color: var(--blue-200, #bfdbfe);         color: var(--blue-700, #1d4ed8); }

.rr-pill__date {
  font-size: 0.72rem;
  font-weight: 700;
  white-space: nowrap;
  letter-spacing: -0.01em;
}

.rr-pill__sel {
  /* shrink the PrimeVue Select to pill size */
  --p-select-padding-x: 6px;
  --p-select-padding-y: 2px;
  font-size: 0.7rem;
  min-width: 0;
  border: none;
  background: transparent !important;
  box-shadow: none !important;
}
:deep(.rr-pill__sel .p-select-label) {
  font-size: 0.7rem;
  padding: 2px 4px;
  font-weight: 600;
}
:deep(.rr-pill__sel .p-select-dropdown) {
  width: 1.4rem;
}
</style>
