// Painel de motivação do Dashboard — meta + ritmo, ranking do mês, você vs mês passado,
// lembretes (leads novos e agenda de hoje) e leads esquecidos.
// Dados: RPC dashboard_motivation() (migration 20261002_dashboard_motivation.sql)

const MV_MONTHS = ['janeiro','fevereiro','março','abril','maio','junho','julho','agosto','setembro','outubro','novembro','dezembro'];
const mvFirst = (n) => String(n || '').trim().split(/\s+/)[0] || '—';
const mvPlural = (n, s, p) => `${Number(n).toLocaleString('pt-BR')} ${n === 1 ? s : p}`;
// Ritmo necessário para fazer n em `days` dias: "cerca de 4 vendas por dia" (meta grande) ou "uma a cada 3 dias"
function mvRate(n, days, u) {
  if (n <= 0) return '';
  const perDay = n / Math.max(1, days);
  if (perDay > 1) return `cerca de ${mvPlural(Math.ceil(perDay), u.one, u.many)} por dia`;
  if (perDay > 0.5) return `cerca de ${u.fem ? 'uma' : 'um'} por dia`;
  return `${u.fem ? 'uma' : 'um'} a cada ${mvPlural(Math.floor(days / n), 'dia', 'dias')}`;
}
// O que a meta conta no ramo da empresa (horos-segments.js): imóveis, matrículas, pacientes novos, assinaturas…
function mvU() {
  const g = (window.horosSeg && window.horosSeg()) || { id: 'imobiliario', unit: ['venda', 'vendas'], fem: true, verb: 'vender', goalStages: ['Prospectado', 'Conversas', 'Documentação', 'Aprovação', 'Vendas'] };
  // no imobiliário o texto continua falando de "venda" (o imóvel aparece na meta)
  const one = g.id === 'imobiliario' ? 'venda' : g.unit[0], many = g.id === 'imobiliario' ? 'vendas' : g.unit[1];
  const fem = g.id === 'imobiliario' ? true : !!g.fem;
  const cap = (t) => t.charAt(0).toUpperCase() + t.slice(1);
  return { seg: g, id: g.id, one, many, fem, cap: cap(many), goalOne: g.unit[0], goalMany: g.unit[1], verb: g.verb,
           howMany: (g.fem ? 'Quantas ' : 'Quantos ') + g.unit[1], first: (fem ? 'a primeira ' : 'o primeiro ') + one,
           anyNone: (fem ? 'nenhuma ' : 'nenhum ') + one, stages: g.goalStages };
}

function mvPace(d) {
  const G = d.goal || 0, C = d.closed_month || 0, N = d.days_in_month || 30, D = d.day || 1;
  const now = new Date();
  const frac = Math.min(1, Math.max(0, (D - 1 + (now.getHours() + now.getMinutes() / 60) / 24) / N));
  const daysLeft = Math.max(1, N - D + 1);
  const expected = G * frac;
  const projection = frac > 0.02 ? Math.round(C / frac) : C;
  const remaining = Math.max(0, G - C);
  const u = mvU();
  let tone = 'neutral', text;
  if (!G) {
    text = null;
  } else if (C >= G) {
    tone = 'good';
    text = C > G ? `Meta batida, com ${mvPlural(C - G, u.one, u.many)} além. Cada ${u.one} agora é bônus.` : `Meta batida! Cada ${u.one} daqui pra frente é bônus.`;
  } else if (C === 0) {
    tone = expected >= 1 ? 'bad' : 'neutral';
    text = `Faltam ${mvPlural(G, u.one, u.many)} em ${mvPlural(daysLeft, 'dia', 'dias')}: ${mvRate(G, daysLeft, u)}.`;
  } else if (C >= expected) {
    tone = 'good';
    text = `Você está no ritmo. Projeção: ${mvPlural(projection, u.one, u.many)} até o fim do mês.`;
  } else {
    tone = 'bad';
    const gap = Math.max(1, G - projection);
    text = `Faltam ${mvPlural(remaining, u.one, u.many)} em ${mvPlural(daysLeft, 'dia', 'dias')} (${mvRate(remaining, daysLeft, u)}). No ritmo atual você chega a ${projection.toLocaleString('pt-BR')}: ${gap.toLocaleString('pt-BR')} abaixo da meta.`;
  }
  return { G, C, frac, expected, daysLeft, projection, remaining, tone, text };
}

function MvGoalCard({ d, onSaved }) {
  const [editing, setEditing] = React.useState(false);
  const [val, setVal] = React.useState('');
  const [saving, setSaving] = React.useState(false);
  const p = mvPace(d), u = mvU();
  const monthName = MV_MONTHS[Number(String(d.month).slice(5, 7)) - 1] || '';

  async function save() {
    const num = parseInt(String(val).replace(/\D/g, ''), 10) || 0;
    const sb = window.HOROS_SB, uid = window.HOROS_USER?.id;
    if (num <= 0 || !sb || !uid) return;
    setSaving(true);
    const { error } = await sb.from('profiles').update({ monthly_goal: { type: 'count', count: num, amount: 0, month: d.month } }).eq('id', uid);
    setSaving(false);
    if (error) { window.dispatchEvent(new CustomEvent('horos:toast', { detail: { module: 'system', title: 'Meta não salva', body: error.message || 'Tente de novo.' } })); return; }
    setEditing(false); setVal('');
    onSaved(num);
    window.dispatchEvent(new CustomEvent('horos:goal-saved'));
  }

  const form = (
    <form className="mv-goal-form" onSubmit={e => { e.preventDefault(); save(); }}>
      <input autoFocus type="text" inputMode="numeric" placeholder={d.last_goal ? String(d.last_goal) : 'Ex.: 4'}
        value={val} onChange={e => setVal(e.target.value.replace(/\D/g, '').slice(0, 3))}
        onKeyDown={e => { if (e.key === 'Escape') { setEditing(false); setVal(''); } }} />
      <span>{u.goalMany}</span>
      <button type="submit" className="mv-btn primary" disabled={saving || !val}>{saving ? 'Salvando…' : 'Salvar meta'}</button>
      {p.G > 0 && <button type="button" className="mv-btn" onClick={() => { setEditing(false); setVal(''); }}>Cancelar</button>}
    </form>
  );

  if (!p.G) {
    return (
      <div className="mv-card mv-goal">
        <div className="mv-head"><span className="mv-title">Meta de {monthName}</span></div>
        <div className="mv-goal-empty">
          <div className="mv-big-q">{u.howMany} você vai {u.verb} em {monthName}?</div>
          <p className="mv-muted">Quem define uma meta chega mais longe. O Horos acompanha seu ritmo todos os dias e avisa se você ficar para trás.</p>
          {form}
        </div>
      </div>
    );
  }

  const fillPct = Math.min(100, (p.C / p.G) * 100);
  const markPct = Math.min(100, p.frac * 100);
  return (
    <div className={`mv-card mv-goal tone-${p.tone}`}>
      <div className="mv-head">
        <span className="mv-title">Meta de {monthName}</span>
        {!editing && <button className="mv-icon-btn" title="Alterar meta" onClick={() => { setVal(String(p.G)); setEditing(true); }}><Icon name="pencil" size={14} /></button>}
      </div>
      <div className="mv-goal-nums">
        <span className="mv-goal-cur">{p.C}</span>
        <span className="mv-goal-of">de {mvPlural(p.G, u.goalOne, u.goalMany)}</span>
        <span className="mv-goal-days">{mvPlural(p.daysLeft, 'dia restante', 'dias restantes')}</span>
      </div>
      <div className="mv-bar" role="img" aria-label={`${p.C} de ${p.G} ${u.many}; hoje o esperado seria ${p.expected.toFixed(1)}`}>
        <div className="mv-bar-fill" style={{ width: `${fillPct}%` }} />
        {p.C < p.G && <div className="mv-bar-mark" style={{ left: `${markPct}%` }} title={`Hoje você deveria estar em ${p.expected.toFixed(1)}`}><span>hoje</span></div>}
      </div>
      {editing ? form : <p className={`mv-pace tone-${p.tone}`}>{p.text}</p>}
    </div>
  );
}

// Funil da meta: base de 100 leads prospectados para 1 venda (proporções em goal_funnel().per_sale).
// Mostra, para a meta do mês, quanto ele já fez e quanto falta em cada fase.
function mvFunnelStages() {
  const u = mvU(), st = u.stages, imob = u.id === 'imobiliario';
  return [
    { label: st[0], unit: ['lead', 'leads'], hint: 'Leads que você chamou no WhatsApp ou mexeu no funil este mês' },
    { label: st[1], unit: ['conversa', 'conversas'], hint: 'Leads que responderam você no WhatsApp este mês (a etapa do funil não conta)' },
    { label: st[2], unit: ['lead', 'leads'], hint: imob ? 'Leads que chegaram à etapa de documentos ou enviaram documentos' : `Leads que chegaram à 3ª fase do funil (${st[2].toLowerCase()})` },
    { label: st[3], unit: imob ? ['aprovação', 'aprovações'] : ['lead', 'leads'], hint: imob ? 'Leads em análise ou aprovados, ou com autorização de crédito assinada' : `Leads que chegaram à 4ª fase do funil (${st[3].toLowerCase()})` },
    { label: st[4], unit: [u.one, u.many], hint: imob ? 'Imóveis vendidos este mês' : `${u.cap} este mês` },
  ];
}

function MvFunnelCard({ d, f }) {
  const G = d.goal || 0, u = mvU(), MV_FUNNEL_STAGES = mvFunnelStages();
  const per = (f && f.per_sale && f.per_sale.length === MV_FUNNEL_STAGES.length) ? f.per_sale : [100, 30, 10, 3, 1];
  const reached = (f && f.reached && f.reached.length === MV_FUNNEL_STAGES.length) ? f.reached : [0, 0, 0, 0, 0];
  if (!G) {
    return (
      <div className="mv-card mv-funnel">
        <div className="mv-head"><span className="mv-title">Funil da meta</span><span className="mv-funnel-base">{per[0]} leads = 1 {u.one}</span></div>
        <p className="mv-muted">Defina sua meta do mês e o Horos mostra quantos leads você precisa prospectar e quantos precisam chegar em cada etapa para bater a meta.</p>
      </div>
    );
  }
  const p = mvPace(d);
  const rows = MV_FUNNEL_STAGES.map((s, i) => {
    const need = G * per[i];
    const done = reached[i] || 0;
    return { ...s, need, done, left: Math.max(0, need - done), pct: Math.min(100, need ? (done / need) * 100 : 0) };
  });
  const pros = rows[0];
  const perDay = Math.ceil(pros.left / Math.max(1, p.daysLeft));
  const tone = pros.left === 0 ? 'good' : (pros.done < pros.need * p.frac ? 'bad' : 'neutral');
  return (
    <div className="mv-card mv-funnel">
      <div className="mv-head">
        <span className="mv-title">Funil da meta</span>
        <span className="mv-funnel-base" title={`A cada 100 leads prospectados, 1 vira ${u.one}`}>{per[0]} leads = 1 {u.one} · meta {mvPlural(G, u.one, u.many)}</span>
      </div>
      <p className={`mv-pace tone-${tone}`}>
        {pros.left === 0
          ? <>Prospecção da meta feita: <b>{pros.done}</b> de {pros.need} leads. Agora é levar cada um para a próxima etapa.</>
          : <>Faltam <b>{mvPlural(pros.left, 'lead', 'leads')}</b> para prospectar até o fim do mês: cerca de <b>{perDay.toLocaleString('pt-BR')} por dia</b>.</>}
      </p>
      <div className="mv-funnel-row">
        {rows.map((r, i) => (
          <div key={r.label} className={`mv-fs${r.left === 0 ? ' ok' : ''}`} title={r.hint} style={{ '--fs-mix': `${100 - i * 15}%` }}>
            <span className="mv-fs-label">{r.label}</span>
            <span className="mv-fs-nums"><b>{r.done}</b><span>/ {r.need}</span></span>
            <span className="mv-fs-bar"><span style={{ width: `${r.pct}%` }} /></span>
            <span className="mv-fs-left">{r.left === 0 ? 'Meta da etapa batida' : `Faltam ${mvPlural(r.left, r.unit[0], r.unit[1])}`}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// Critério do ranking: vendas; ninguém vendeu → leads avançados (marcos reais, ver user_advanced_leads); ninguém avançou → mensagens enviadas
function mvRankUnits() {
  const u = mvU();
  return {
    sales: { one: u.one, many: u.many, label: 'por ' + u.many },
    adv:   { one: 'lead avançado', many: 'leads avançados', short: 'avanços', label: 'por leads avançados' },
    msgs:  { one: 'mensagem', many: 'mensagens', short: 'msgs', label: 'por mensagens enviadas' },
  };
}

// Pódio + lista (LeaderboardCard em data.jsx). O seletor troca o critério; a frase de motivação vale para o critério do mês
function mvRankRuns() {
  return [
    { id: 'sales', label: mvU().cap },
    { id: 'adv', label: 'Leads avançados' },
    { id: 'msgs', label: 'Mensagens enviadas' },
  ];
}
function mvRankRows(list, metric) {
  const score = (x) => Number(metric === 'sales' ? x.closed : metric === 'adv' ? x.adv : x.msgs) || 0;
  const tb = (x) => Number(metric === 'sales' ? x.value : metric === 'adv' ? x.msgs : 0) || 0;
  const fmtR = window.fmtCurrency || (v => v.toLocaleString('pt-BR'));
  const u = mvU();
  const sorted = [...list].sort((a, b) => score(b) - score(a) || tb(b) - tb(a) || String(a.name).localeCompare(String(b.name), 'pt-BR'));
  return sorted.map(x => {
    const sc = score(x);
    let byline = null;
    if (metric === 'sales') byline = Number(x.value) > 0 ? `R$ ${fmtR(Number(x.value))} no mês` : null;
    else if (metric === 'adv') byline = mvPlural(Number(x.msgs) || 0, 'mensagem', 'mensagens');
    else byline = Number(x.closed) > 0 ? mvPlural(Number(x.closed), u.one, u.many) : null;
    return {
      userId: x.user_id, userName: x.name, avatarUrl: x.avatar_url || null, value: sc, byline,
      rank: 1 + sorted.filter(o => score(o) > sc).length,
    };
  });
}

function MvRankCard({ r, month }) {
  const [metric, setMetric] = React.useState(null);
  const today = new Date();
  const from = month ? `${month}-01` : new Date(today.getFullYear(), today.getMonth(), 1);
  if (!r || !r.total) {
    return (
      <LeaderboardCard className="mv-rank" title="Ranking do mês" fromDate={from} toDate={today} rankings={[]}
        emptyText="Entre numa equipe para aparecer no ranking." />
    );
  }
  const mode = r.mode || 'sales';
  const active = metric || mode;
  const MV_RANK_UNIT = mvRankUnits(), MV_RANK_RUNS = mvRankRuns(), su = mvU();
  const u = MV_RANK_UNIT[active] || MV_RANK_UNIT.sales;
  const mu = MV_RANK_UNIT[mode] || MV_RANK_UNIT.sales;
  const me = r.me;
  const uid = window.HOROS_USER?.id;
  const noSales = mode !== 'sales';
  const allZero = !r.leader || !r.leader.score;
  // RPC antiga sem 'list': usa o top 3 + você
  const list = (r.list && r.list.length) ? r.list
    : (r.top || []).map(t => ({ user_id: t.user_id, name: t.name, avatar_url: t.avatar_url, closed: mode === 'sales' ? t.score : 0, adv: mode === 'adv' ? t.score : 0, msgs: mode === 'msgs' ? t.score : 0, value: 0 }));
  const rows = mvRankRows(list, active);
  let line, tone = 'neutral';
  if (!r.eligible) {
    line = noSales ? `Ninguém da equipe fechou ${su.anyNone} ainda este mês.` : `${mvFirst(r.leader.name)} lidera com ${mvPlural(r.leader.score, mu.one, mu.many)}.`;
  } else if (allZero) {
    tone = 'warn';
    line = `Ninguém fechou ${su.anyNone} ainda este mês. ${su.first.charAt(0).toUpperCase() + su.first.slice(1)} te coloca em 1º lugar sozinho.`;
  } else if (r.above) {
    tone = 'bad';
    const need = r.above.score - me.score + 1;
    line = `Mais ${mvPlural(need, mu.one, mu.many)} e você passa ${mvFirst(r.above.name)}.`;
  } else if (r.tied > 0) {
    tone = 'warn';
    line = noSales ? `Empatado em 1º no atendimento. ${su.first.charAt(0).toUpperCase() + su.first.slice(1)} decide.` : `Empatado em 1º. ${su.fem ? 'Mais uma' : 'Mais um'} ${su.one} te deixa sozinho no topo.`;
  } else if (noSales) {
    tone = 'good';
    line = `Você lidera o atendimento. Agora transforme em ${su.one}: ${su.first} do mês te põe em 1º.`;
  } else {
    tone = 'good';
    line = r.below ? `Você lidera! ${mvFirst(r.below.name)} está a ${mvPlural(me.score - r.below.score, mu.one, mu.many)}.` : 'Você lidera o ranking!';
  }
  return (
    <LeaderboardCard className="mv-rank" title="Ranking do mês" badge={r.team_name || null}
      fromDate={from} toDate={today} rankings={rows} currentUserId={uid} pageSize={5}
      unit={active === 'sales' ? null : (u.short || null)}
      zeroText={active === mode ? null : `Ninguém da equipe tem ${u.many} este mês ainda.`}
      runOptions={MV_RANK_RUNS} selectedRunId={active} onRunChange={setMetric}>
      {active === 'adv' && <div className="mv-rank-mode">Conta lead com documentos recebidos, crédito autorizado, visita realizada, envio para análise, aprovação ou venda</div>}
      {active === mode && (
        <>
          {noSales && !allZero && <div className="mv-rank-mode">Ninguém fechou {su.anyNone} ainda · ranking {mu.label}</div>}
          <p className={`mv-pace tone-${tone}`}>{line}</p>
        </>
      )}
    </LeaderboardCard>
  );
}

// Texto da IA: parágrafos, listas com "- " / "• " / "1." e **negrito**
function MvRichText({ text, refs }) {
  if (window.HorosRichText) return <HorosRichText text={text} refs={refs} />;
  const inline = (s) => String(s).split(/(\*\*[^*]+\*\*)/g).map((p, i) =>
    p.startsWith('**') && p.endsWith('**') ? <b key={i}>{p.slice(2, -2)}</b> : p);
  const blocks = [];
  String(text || '').split('\n').forEach(raw => {
    const line = raw.trim();
    if (!line) { blocks.push(null); return; }
    const li = line.match(/^(?:[-•*]|\d+[.)])\s+(.*)$/);
    const last = blocks[blocks.length - 1];
    if (li) {
      if (last && last.type === 'ul') last.items.push(li[1]); else blocks.push({ type: 'ul', items: [li[1]] });
    } else if (last && last.type === 'p') {
      last.lines.push(line);
    } else {
      blocks.push({ type: 'p', lines: [line] });
    }
  });
  return blocks.filter(Boolean).map((b, i) => b.type === 'ul'
    ? <ul key={i}>{b.items.map((it, j) => <li key={j}>{inline(it)}</li>)}</ul>
    : <p key={i}>{b.lines.map((l, j) => <React.Fragment key={j}>{j > 0 && <br />}{inline(l)}</React.Fragment>)}</p>);
}

// ── Assistente: frase + 3 recomendações (api/intelligence/dashboard-coach), cache de 2h por usuário ──
const MV_COACH_TTL = 2 * 3600 * 1000;

function MvCoachCard({ onOpen, waiting }) {
  // lead com conversa aguardando → abre a conversa em Mensagens; outro lead → Horos 360; sem lead → responde aqui
  function tipClick(t) {
    const conv = t.lead_id && ((waiting && waiting.items) || []).find(x => x.lead_id === t.lead_id);
    if (conv) return mvOpenConv(conv.conv_id);
    if (t.lead_id) return onOpen(t.lead_id);
    // dica de gestor: abre onde se resolve (fila, equipe ou o relatório da pessoa)
    if (t.href) {
      if (t.user_id && t.href === 'reports.html') { try { sessionStorage.setItem('horos:report-scope', t.user_id); } catch {} }
      return void (location.href = t.href);
    }
    ask(`${t.title}. ${t.detail} Me ajude com isso.`);
  }
  const uid = window.HOROS_USER?.id;
  const key = `horos-coach3-${uid}`; // 3: gestor passou a receber dicas da equipe (2026-11-08)
  const [state, setState] = React.useState(() => {
    try {
      const c = JSON.parse(localStorage.getItem(key) || 'null');
      if (c && Date.now() - c.at < MV_COACH_TTL) return { data: c.data, at: c.at, loading: false, error: false };
    } catch {}
    return { data: null, at: 0, loading: true, error: false };
  });
  const [q, setQ] = React.useState('');
  const [thread, setThread] = React.useState([]);          // conversa dentro do bloco: [{ role, content, error? }]
  const [asking, setAsking] = React.useState(false);
  const threadRef = React.useRef(null);

  React.useEffect(() => {
    const el = threadRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [thread.length, asking]);

  const fetchCoach = React.useCallback(async () => {
    setState(s => ({ ...s, loading: true, error: false }));
    try {
      const token = window.horosToken ? await window.horosToken() : window.HOROS_SESSION?.access_token;
      const r = await fetch('/api/intelligence/dashboard-coach', {
        method: 'POST', cache: 'no-store',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: '{}',
      });
      const d = await r.json();
      if (!d.ok) throw new Error(d.error || 'erro');
      const at = Date.now();
      try { localStorage.setItem(key, JSON.stringify({ at, data: d })); } catch {}
      setState({ data: d, at, loading: false, error: false });
    } catch {
      setState(s => ({ ...s, loading: false, error: true }));
    }
  }, [key]);

  React.useEffect(() => { if (!state.data) fetchCoach(); }, []);

  // Responde aqui mesmo (mesmo backend do chat de IA), sem abrir a janela de IA
  async function ask(text, base) {
    const message = String(text || '').trim();
    if (!message || asking) return;
    setQ('');
    const prev = (base || thread).filter(m => !m.error);
    setThread([...prev, { role: 'user', content: message }]);
    setAsking(true);
    try {
      // a IA recebe o que o bloco acabou de recomendar, para responder no contexto
      const ctx = state.data ? [{ role: 'assistant', content: [state.data.headline, ...(state.data.tips || []).map(t => `- ${t.title}: ${t.detail}`)].join('\n') }] : [];
      const history = [...ctx, ...prev].map(m => ({ role: m.role, content: m.content }));
      const token = window.horosToken ? await window.horosToken() : window.HOROS_SESSION?.access_token;
      const r = await fetch('/api/intelligence/broker-chat', {
        method: 'POST', cache: 'no-store',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
        body: JSON.stringify({ message, history }),
      });
      const d = await r.json().catch(() => ({}));
      if (!r.ok || !d.ok || !d.text) throw new Error(d.error || 'erro');
      setThread(t => [...t, { role: 'assistant', content: d.text, refs: d.refs || [] }]);
    } catch {
      setThread(t => [...t, { role: 'assistant', content: 'Não consegui responder agora.', error: true, retry: message }]);
    } finally {
      setAsking(false);
    }
  }

  // refaz a pergunta que falhou (tira a pergunta e o erro e manda de novo)
  function retry(m) {
    const i = thread.indexOf(m);
    ask(m.retry, thread.slice(0, Math.max(0, i - 1)));
  }

  const { data, loading, error } = state;
  const chatting = thread.length > 0;
  // mascote: trabalha enquanto carrega/responde, comemora quando chega a resposta
  const mascotRef = React.useRef(null);
  const busy = loading || asking;
  const wasBusy = React.useRef(busy);
  React.useEffect(() => {
    if (wasBusy.current && !busy) mascotRef.current?.react?.(error ? 'sad' : 'happy', 1600);
    wasBusy.current = busy;
  }, [busy]);
  const mins = state.at ? Math.max(0, Math.round((Date.now() - state.at) / 60000)) : null;
  return (
    <div className="mv-card mv-coach">
      <div className="mv-head">
        <span className="mv-title mv-coach-title">
          <horos-mascot ref={mascotRef} size="30" state={loading ? 'loading' : asking ? 'thinking' : 'idle'} aria-hidden="true" />
          Assistente <span className="mv-ai-chip">IA</span>
        </span>
        <span className="mv-coach-meta">
          {mins != null && !loading && <span>{mins < 1 ? 'agora' : `há ${mins} min`}</span>}
          <button className="mv-icon-btn" title="Gerar de novo" onClick={fetchCoach} disabled={loading}>
            <Icon name="refresh" size={14} className={`ico${loading ? ' mv-spin' : ''}`} />
          </button>
        </span>
      </div>
      {chatting ? (
        <div className="mv-chat">
          <button className="mv-chat-back" onClick={() => setThread([])} disabled={asking}>
            <Icon name="arrLeft" size={13} /> Voltar às recomendações
          </button>
          <div className="mv-chat-thread" ref={threadRef} aria-live="polite">
            {thread.map((m, i) => (
              m.role === 'user'
                ? <div key={i} className="mv-chat-q">{m.content}</div>
                : <div key={i} className={`mv-chat-a${m.error ? ' err' : ''}`}>
                    <MvRichText text={m.content} refs={m.refs} />
                    {m.error && <button className="mv-btn" onClick={() => retry(m)} disabled={asking}>Tentar de novo</button>}
                  </div>
            ))}
            {asking && <div className="mv-chat-a typing" aria-label="Respondendo"><span /><span /><span /></div>}
          </div>
        </div>
      ) : loading && !data ? (
        <div className="mv-coach-skel"><i /><i /><i /><i /></div>
      ) : error && !data ? (
        <div className="mv-coach-err">
          <span>Não consegui analisar agora.</span>
          <button className="mv-btn" onClick={fetchCoach}>Tentar de novo</button>
        </div>
      ) : data ? (
        <>
          <p className="mv-coach-head">{data.headline}</p>
          <ol className="mv-list mv-steps">
            {(data.tips || []).map((t, i) => (
              <li key={i}>
                <button className="mv-item" onClick={() => tipClick(t)}>
                  <span className={`mv-step k-${t.kind}`}>{i + 1}</span>
                  <span className="mv-item-txt">
                    <span className="mv-item-name">{t.title}</span>
                    <span className="mv-item-sub">{t.detail}</span>
                  </span>
                  <Icon name="arrRight" size={14} className="ico mv-item-go" />
                </button>
              </li>
            ))}
          </ol>
        </>
      ) : null}
      <form className="mv-coach-ask" onSubmit={e => { e.preventDefault(); ask(q); }}>
        <input value={q} onChange={e => setQ(e.target.value)} placeholder={chatting ? 'Continue a conversa…' : 'Pergunte ao assistente…'} aria-label="Pergunte ao assistente" />
        <button type="submit" className="mv-coach-send" disabled={!q.trim() || asking} title="Perguntar"><Icon name="send" size={14} /></button>
      </form>
    </div>
  );
}

function MvDelta({ cur, prev }) {
  const diff = cur - prev;
  const cls = diff > 0 ? 'up' : diff < 0 ? 'down' : 'eq';
  return (
    <span className={`mv-delta ${cls}`}>
      {diff !== 0 && <Icon name={diff > 0 ? 'arrUp' : 'arrDown'} size={12} />}
      {diff === 0 ? 'igual' : Math.abs(diff)}
    </span>
  );
}

function MvSelfCard({ d }) {
  const rows = [
    { label: mvU().cap, cur: d.closed_month || 0, prev: d.closed_prev || 0 },
    { label: 'Mensagens enviadas', cur: d.msgs_month || 0, prev: d.msgs_prev || 0 },
    { label: 'Leads novos', cur: d.new_month || 0, prev: d.new_prev || 0 },
  ];
  return (
    <div className="mv-card mv-self">
      <div className="mv-head">
        <span className="mv-title">Você vs. mês passado</span>
        <span className="mv-chip">até dia {d.day}</span>
      </div>
      <div className="mv-self-rows">
        {rows.map(r => (
          <div key={r.label} className="mv-self-row">
            <span className="mv-self-lbl">{r.label}</span>
            <span className="mv-self-cur">{r.cur.toLocaleString('pt-BR')}</span>
            <span className="mv-self-prev">antes {r.prev.toLocaleString('pt-BR')}</span>
            <MvDelta cur={r.cur} prev={r.prev} />
          </div>
        ))}
      </div>
      <MvReminders refreshKey={d.now} />
    </div>
  );
}

// ── Leads novos que ainda não receberam mensagem (RPC dashboard_reminders); clique leva até o lead ──
const MV_NEW_MAX = 30;
function MvReminders({ refreshKey }) {
  const [r, setR] = React.useState(null);
  React.useEffect(() => {
    let alive = true;
    const sb = window.HOROS_SB;
    if (!sb) return;
    sb.rpc('dashboard_reminders', { p_limit: MV_NEW_MAX }).then(({ data, error }) => { if (alive && !error && data && !data.error) setR(data); }, () => {});
    return () => { alive = false; };
  }, [refreshKey]);
  if (!r) return <div className="mv-rem"><div className="mv-rem-t">Leads novos</div><SkelText lines={2} last="60%" /></div>;
  const open = (l) => (l.conv_id ? mvOpenConv(l.conv_id) : window.horosGoToLead(l.id));
  return (
    <div className="mv-rem">
      <div className="mv-rem-t">Leads novos{r.new_total > 0 && <span className="mv-rem-n">{r.new_total.toLocaleString('pt-BR')} sem mensagem</span>}</div>
      {!r.new_total && <p className="mv-muted">Nenhum lead novo esperando.</p>}
      {r.new_total > 0 && (
        <ul className="mv-list mv-rem-list">
          {r.new_leads.map(l => (
            <li key={l.id}>
              <button className="mv-item" onClick={() => open(l)} title={l.conv_id ? 'Abrir a conversa' : 'Ir até o lead'}>
                <span className={`mv-temp t-${l.temp || 'none'}`} />
                <span className="mv-item-txt">
                  <span className="mv-item-name">{l.name || 'Sem nome'}</span>
                  <span className="mv-item-sub">{[window.fmtPhone ? window.fmtPhone(l.phone) : l.phone, window.kbSince ? window.kbSince(l.created_at) : ''].filter(Boolean).join(' · ')}</span>
                </span>
                <Icon name="arrRight" size={14} className="ico mv-item-go" />
              </button>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

// Abre a conversa na tela de Mensagens (MessagesPage lê 'horos:open-conv' ao carregar)
function mvOpenConv(convId) {
  try { sessionStorage.setItem('horos:open-conv', convId); } catch {}
  window.location.href = 'messages.html';
}

function mvStaleSub(l, colName) {
  return [colName(l.col), l.never ? 'nunca recebeu mensagem' : `último contato há ${mvPlural(l.days, 'dia', 'dias')}`, l.value > 0 ? `R$ ${fmtCurrency(l.value)}` : ''].filter(Boolean).join(' · ');
}

function MvStaleRow({ l, colName }) {
  return (
    <li className="mv-stale-row">
      <button className="mv-item" onClick={() => window.horosOpenLeadSheet(l.id)} title="Abrir a ficha">
        <span className={`mv-temp t-${l.temp || 'none'}`} />
        <span className="mv-item-txt">
          <span className="mv-item-name">{l.name || 'Sem nome'}</span>
          <span className="mv-item-sub">{mvStaleSub(l, colName)}</span>
        </span>
        <span className="mv-days">{l.days}d</span>
      </button>
      <span className="mv-stale-acts">
        {l.conv_id && <button className="mv-icon-btn" title="Abrir a conversa" onClick={() => mvOpenConv(l.conv_id)}><Icon name="msgs" size={15} /></button>}
        <button className="mv-icon-btn" title="Ver no funil" onClick={() => window.horosGoToLead(l.id)}><Icon name="crm" size={15} /></button>
      </span>
    </li>
  );
}

// "Ver todos": busca e paginação na lista inteira (RPC dashboard_forgotten)
function MvStaleModal({ colName, onClose }) {
  const PAGE = 30;
  const [q, setQ] = React.useState('');
  const [items, setItems] = React.useState([]);
  const [total, setTotal] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const fetchPage = React.useCallback(async (offset, search) => {
    setBusy(true);
    const { data, error } = await window.HOROS_SB.rpc('dashboard_forgotten', { p_search: search || null, p_limit: PAGE, p_offset: offset });
    setBusy(false);
    if (error || !data || data.error) return;
    setTotal(data.total);
    setItems(prev => (offset ? [...prev, ...data.items] : data.items));
  }, []);
  React.useEffect(() => { const t = setTimeout(() => fetchPage(0, q.trim()), q ? 300 : 0); return () => clearTimeout(t); }, [q, fetchPage]);
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [onClose]);
  return ReactDOM.createPortal(
    <div className="arq-ov" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="arq-modal mv-stale-modal" role="dialog" aria-modal="true" aria-label="Leads esquecidos">
        <div className="mv-head">
          <span className="mv-title">Leads esquecidos{total != null && <> · {total.toLocaleString('pt-BR')}</>}</span>
          <button className="mv-icon-btn" onClick={onClose} aria-label="Fechar"><Icon name="close" size={16} /></button>
        </div>
        <input className="mv-stale-q" autoFocus value={q} onChange={e => setQ(e.target.value)} placeholder="Buscar por nome ou telefone" aria-label="Buscar" />
        <ul className="mv-list mv-stale-all">
          {items.map(l => <MvStaleRow key={l.id} l={l} colName={colName} />)}
          {total === 0 && <li className="mv-muted">Nenhum lead encontrado.</li>}
        </ul>
        {total != null && items.length < total && (
          <button className="mv-btn" onClick={() => fetchPage(items.length, q.trim())} disabled={busy}>{busy ? 'Carregando…' : 'Mostrar mais'}</button>
        )}
      </div>
    </div>, document.body);
}

function MvStaleCard({ d }) {
  const st = d.stale || {};
  const n = st.count || 0;
  const [all, setAll] = React.useState(false);
  const cols = (window.loadKanbanCols ? window.loadKanbanCols() : []) || [];
  const colName = (id) => (cols.find(c => c.id === id) || {}).title || '';
  return (
    <div className="mv-card mv-stale">
      <div className="mv-head">
        <span className="mv-title">Leads esquecidos</span>
        {n > 0 && <button className="mv-link" onClick={() => setAll(true)}>Ver todos</button>}
      </div>
      {n === 0 ? (
        <div className="mv-empty">
          <Icon name="check" size={18} />
          <span>Nenhum lead sem contato há mais de 7 dias. Continue assim.</span>
        </div>
      ) : (
        <>
          <div className="mv-stale-sum">
            <b>{n.toLocaleString('pt-BR')}</b>
            <span>
              {n === 1 ? 'lead' : 'leads'} sem contato há 7+ dias
              {st.value > 0 && <> · <strong>R$ {fmtCurrency(st.value)}</strong> esfriando</>}
              {st.hot > 0 && <> · <em>{mvPlural(st.hot, 'quente', 'quentes')}</em></>}
            </span>
          </div>
          <ul className="mv-list">
            {(st.top || []).map(l => <MvStaleRow key={l.id} l={l} colName={colName} />)}
          </ul>
        </>
      )}
      {all && <MvStaleModal colName={colName} onClose={() => setAll(false)} />}
    </div>
  );
}

const mvPick = (o, ks) => { const r = {}; if (o) ks.forEach(k => { if (o[k] !== undefined) r[k] = o[k]; }); return r; };

function MotivationStrip() {
  if (window.useHorosSegment) window.useHorosSegment(); // textos da meta seguem o ramo (horos-segments.js)
  const solo = window.useHorosSolo ? window.useHorosSolo() : false;
  // Abre na hora com o último resultado (por usuário) e atualiza por trás. Cada consulta entra na tela assim que
  // chega (não espera as outras), tem limite de tempo e, se falhar, tenta de novo sozinha — o bloco nunca some.
  const uidOf = () => window.HOROS_USER?.id || window.HOROS_SESSION?.user?.id || '';
  const keyOf = (uid) => 'horos-mv-cache:' + uid;
  const readCache = () => {
    try {
      const uid = uidOf() || localStorage.getItem('horos-mv-cache:last') || '';
      const c = uid && JSON.parse(localStorage.getItem(keyOf(uid)) || 'null');
      return c && Date.now() - c.at < 864e5 ? c.data : null;
    } catch { return null; }
  };
  const [data, setData] = React.useState(readCache);
  const [failed, setFailed] = React.useState(false);
  const dataRef = React.useRef(data);
  const loadingRef = React.useRef(false);
  const retryRef = React.useRef({ n: 0, t: null });

  const save = React.useCallback((patch) => {
    const next = { ...(dataRef.current || {}), ...patch };
    dataRef.current = next;
    setData(next);
    if (!next.month) return;   // só guarda quando já tem a parte principal
    const uid = uidOf();
    if (uid) try { localStorage.setItem(keyOf(uid), JSON.stringify({ at: Date.now(), data: next })); localStorage.setItem('horos-mv-cache:last', uid); } catch {}
  }, []);

  const load = React.useCallback(async () => {
    const sb = window.HOROS_SB;
    if (!sb || !uidOf()) {   // sessão ainda abrindo: tenta de novo em instantes
      clearTimeout(retryRef.current.t);
      retryRef.current.t = setTimeout(() => load(), 300);
      return;
    }
    if (loadingRef.current) return;
    loadingRef.current = true;
    if (!dataRef.current) { const c = readCache(); if (c) { dataRef.current = c; setData(c); } }
    const withTimeout = (p, ms) => Promise.race([p, new Promise(res => setTimeout(() => res({ data: null, error: { message: 'timeout' } }), ms))]);
    const rpc = (name, args, ms) => withTimeout(Promise.resolve(sb.rpc(name, args)).then(r => r, e => ({ data: null, error: e })), ms);

    // complementos: entram quando chegam; falha não derruba o bloco
    rpc('dashboard_waiting', { p_limit: 6 }, 15000).then(({ data: w, error }) => { if (!error && w) save({ waiting: w }); });
    rpc('goal_funnel', undefined, 15000).then(({ data: f, error }) => { if (!error && f && !f.error) save({ funnel: f }); });

    const { data: res, error } = await rpc('dashboard_motivation', undefined, 15000);
    loadingRef.current = false;
    if (error || !res || res.error) {
      const r = retryRef.current;
      if (r.n < 5) { r.n += 1; clearTimeout(r.t); r.t = setTimeout(() => load(), [1500, 3000, 6000, 12000, 20000][r.n - 1]); }
      else if (!dataRef.current) setFailed(true);
      return;
    }
    retryRef.current.n = 0;
    setFailed(false);
    save({ waiting: { total: 0, items: [] }, ...mvPick(dataRef.current, ['waiting', 'funnel']), ...res });
  }, [save]);

  React.useEffect(() => {
    load();
    const onVis = () => { if (document.visibilityState === 'visible') load(); };
    const onLead = () => load();
    const iv = setInterval(() => { if (document.visibilityState === 'visible') load(); }, 120000);
    document.addEventListener('visibilitychange', onVis);
    window.addEventListener('horos:lead-updated', onLead);
    window.addEventListener('horos:lead-created', onLead);
    return () => {
      clearInterval(iv); clearTimeout(retryRef.current.t);
      document.removeEventListener('visibilitychange', onVis);
      window.removeEventListener('horos:lead-updated', onLead);
      window.removeEventListener('horos:lead-created', onLead);
    };
  }, [load]);

  if (failed && !data?.month) {
    return (
      <div className="mv-grid">
        <div className="mv-card mv-stale mv-failed">
          <span className="mv-muted">Não foi possível carregar suas metas agora.</span>
          <button className="mv-btn" onClick={() => { setFailed(false); retryRef.current.n = 0; load(); }}>Tentar de novo</button>
        </div>
      </div>
    );
  }

  if (!data || !data.month) {
    return (
      <div className="mv-grid" aria-busy="true">
        {[['mv-goal', 'bars', 2], ['mv-self', 'bars', 3], ['mv-funnel', 'bars', 2], ...(solo ? [] : [['mv-rank', 'rows', 4]]), ['mv-coach', 'rows', 3], ['mv-stale', 'rows', 3]].map(([c, kind, rows]) => <SkelPanel key={c} className={`mv-card ${c}`} kind={kind} rows={rows} />)}
      </div>
    );
  }

  return (
    <>
      <div className="mv-grid">
        <MvGoalCard d={data} onSaved={(n) => save({ goal: n, last_goal: n })} />
        <MvSelfCard d={data} />
        <MvFunnelCard d={data} f={data.funnel} />
        {!solo && <MvRankCard r={data.ranking} month={data.month} />}
        <MvCoachCard onOpen={window.horosGoToLead} waiting={data.waiting} />
        <MvStaleCard d={data} />
      </div>
    </>
  );
}

window.MotivationStrip = MotivationStrip;
