/* Tapa do Dia — Mulheres Milionárias
   App principal: gerador ao vivo (Claude API) + banco curado de reserva. */

const { useState, useEffect, useRef, useCallback } = React;

/* ---------------------------------------------------------------- presets */

const THEMES = {
  dark: {
    '--bg': '#100e0c', '--bg-2': '#16130f', '--surface': '#1c1815', '--surface-2': '#242019',
    '--ink': '#f3ece1', '--ink-soft': '#b6ab99', '--ink-faint': '#7c7367',
    '--hairline': 'rgba(255,255,255,0.09)',
    '--card-shadow': '0 50px 100px -34px rgba(0,0,0,0.85)',
    '--grain': '0.05'
  },
  claro: {
    '--bg': '#ece5da', '--bg-2': '#f2ece2', '--surface': '#fbf8f2', '--surface-2': '#ffffff',
    '--ink': '#1b1712', '--ink-soft': '#5b5346', '--ink-faint': '#948977',
    '--hairline': 'rgba(28,24,19,0.13)',
    '--card-shadow': '0 44px 80px -34px rgba(70,52,28,0.30)',
    '--grain': '0.025'
  }
};

const ACCENTS = {
  '#d6b87a': { ink: '#15110b', glow: 'rgba(214,184,122,0.20)' }, // champagne
  '#d9a441': { ink: '#1a1305', glow: 'rgba(217,164,65,0.20)' },  // dourado
  '#a83248': { ink: '#fff5f6', glow: 'rgba(168,50,72,0.20)' },   // vinho
  '#2c8a63': { ink: '#f1fff8', glow: 'rgba(44,138,99,0.20)' },   // esmeralda
  '#c8623c': { ink: '#fff4ef', glow: 'rgba(200,98,60,0.20)' }    // terracota
};

const QUOTE_FONTS = {
  display:   { family: "'Bricolage Grotesque', sans-serif", weight: 700, style: 'normal', tracking: '-0.02em', lh: 1.04 },
  impacto:   { family: "'Archivo', sans-serif",            weight: 900, style: 'normal', tracking: '-0.03em', lh: 1.0 },
  editorial: { family: "'Newsreader', serif",              weight: 500, style: 'italic', tracking: '0',       lh: 1.08 }
};

const FILTERS = [
  { key: 'all', label: 'Todos' },
  { key: 'verdade', label: 'Verdade ácida' },
  { key: 'citacao', label: 'Citação' },
  { key: 'sarcasmo', label: 'Sarcasmo' },
  { key: 'riqueza', label: 'Riqueza' },
  { key: 'posicionamento', label: 'Posicionamento' },
  { key: 'fitness', label: 'Fitness' },
  { key: 'religioso', label: 'Religioso' },
  { key: 'motivacional', label: 'Motivacional' },
  { key: 'mentalidade', label: 'Mentalidade' }
];

const TYPE_DESC = {
  verdade: 'verdade ácida que todo mundo sabe mas ninguém fala',
  citacao: 'citação REAL de alguém reconhecido em negócios, finanças ou filosofia',
  sarcasmo: 'sarcasmo empresarial que dói porque é real',
  riqueza: 'mentalidade financeira, patrimônio e legado',
  posicionamento: 'valor, preço e autoridade',
  fitness: 'corpo, disciplina física, treino e saúde tratados como ativo e como espelho da disciplina nos negócios',
  religioso: 'fé, propósito e espiritualidade — mas com substância e ação, nunca religiosidade vazia; respeitosa e sem citar versículo específico',
  motivacional: 'motivação COM DENTE — empurrão direto pra agir, nada de "acredite em você" genérico',
  mentalidade: 'mentalidade milionária: abundância vs escassez, como rica pensa diferente de quebrada'
};

const BANK = window.TAPAS_BANK || [];
const TYPES = window.TAPA_TYPES || {};
const VALID_KEYS = Object.keys(TYPE_DESC);
const AI_OK = !!(window.claude && typeof window.claude.complete === 'function');
const STORE_KEY = 'tapadodia.v1';

/* ---------------------------------------------------------------- helpers */

const norm = (s) => (s || '').toLowerCase().replace(/[^a-z0-9áàâãéêíóôõúüç]+/gi, '');

function parseTapas(raw) {
  if (!raw) return [];
  let s = String(raw).trim().replace(/```json/gi, '').replace(/```/g, '').trim();
  const a = s.indexOf('['), b = s.lastIndexOf(']');
  if (a >= 0 && b > a) s = s.slice(a, b + 1);
  let arr;
  try { arr = JSON.parse(s); } catch (e) { return []; }
  if (!Array.isArray(arr)) return [];
  return arr
    .filter(o => o && typeof o.text === 'string' && o.text.trim().length > 5 && o.text.length < 240)
    .map(o => ({
      t: o.text.trim(),
      a: (o.author && String(o.author).trim()) || null,
      k: VALID_KEYS.includes(o.type) ? o.type : 'verdade',
      gen: true
    }));
}

const PERSONA = `Você é o "Tapa do Dia", um gerador de frases de impacto para MULHERES MILIONÁRIAS — empreendedoras, CEOs, donas do próprio negócio e da própria riqueza.

Cada frase é um TAPA NA CARA: bate na consciência e dói porque é verdade. Pode ser motivacional com dente, sarcástica, filosófica ou uma citação real.

TOM: sarcástico mas inteligente; ironia fina; direto e sem frescura; sempre no universo de dinheiro, negócio, patrimônio, liberdade financeira, posicionamento e legado. Toda frase tem dente.

NUNCA: autoajuda genérica, linguagem de coach de Instagram, "acredite em você", "jornada", "propósito maior", "o universo conspira", nem nada óbvio demais que uma mulher que fatura 7 dígitos ignoraria. Nunca infantilize.

TIPOS (campo "type"): verdade, citacao, sarcasmo, riqueza, posicionamento.
Para "citacao" use SOMENTE citações reais e corretamente atribuídas (author = nome real). Se não tiver certeza absoluta da autoria, use outro tipo e author = null.

Frases curtas e afiadas: 1 a 2 sentenças, no máximo ~30 palavras. Português do Brasil.`;

async function generateBatch(filterKey, avoidList, n = 4) {
  const typeLine = filterKey === 'all'
    ? 'Varie livremente entre os 5 tipos.'
    : `Todos do tipo "${filterKey}" (${TYPE_DESC[filterKey]}).`;
  const avoid = avoidList && avoidList.length
    ? `\nNÃO repita nem parafraseie estas ideias já usadas: ${avoidList.map(x => `"${x}"`).join('; ')}.`
    : '';
  const user = `${PERSONA}

Gere ${n} tapas NOVOS, originais e afiados. ${typeLine}${avoid}

Responda SOMENTE com um array JSON válido, sem nenhum texto fora dele, no formato exato:
[{"text":"...","author":null,"type":"sarcasmo"}]`;
  const raw = await window.claude.complete({ messages: [{ role: 'user', content: user }] });
  return parseTapas(raw);
}

/* ---------------------------------------------------------------- App */

function loadSaved() {
  try {
    const s = JSON.parse(localStorage.getItem(STORE_KEY));
    if (s && typeof s === 'object') return s;
  } catch (e) {}
  return null;
}

function App() {
  const saved = useRef(loadSaved()).current;
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  const [authed, setAuthed] = useState(() => !!(window.authLoad && window.authLoad()));
  const [account, setAccount] = useState(() => (window.authLoad && window.authLoad()) || null);

  const [tapa, setTapa] = useState(saved && saved.tapa ? saved.tapa : null);
  const [filter, setFilter] = useState(saved && saved.filter ? saved.filter : 'all');
  const [count, setCount] = useState(saved && saved.count ? saved.count : 0);
  const [animKey, setAnimKey] = useState(0);
  const [copied, setCopied] = useState(false);
  const [genActive, setGenActive] = useState(0);

  const seenRef = useRef(new Set(saved && saved.seen ? saved.seen : []));
  const bufferRef = useRef(FILTERS.reduce((acc, f) => { acc[f.key] = []; return acc; }, {}));
  const fetchingRef = useRef({});
  const filterRef = useRef(filter);
  filterRef.current = filter;

  /* apply theme / accent / fonts to :root */
  useEffect(() => {
    const root = document.documentElement;
    const theme = THEMES[t.theme] || THEMES.dark;
    Object.entries(theme).forEach(([k, v]) => root.style.setProperty(k, v));
    const acc = ACCENTS[t.accent] || ACCENTS['#d6b87a'];
    root.style.setProperty('--accent', t.accent);
    root.style.setProperty('--accent-ink', acc.ink);
    root.style.setProperty('--accent-glow', acc.glow);
    const f = QUOTE_FONTS[t.quoteFont] || QUOTE_FONTS.display;
    root.style.setProperty('--font-quote', f.family);
    root.style.setProperty('--quote-weight', f.weight);
    root.style.setProperty('--quote-style', f.style);
    root.style.setProperty('--quote-tracking', f.tracking);
    root.style.setProperty('--quote-lh', f.lh);
  }, [t.theme, t.accent, t.quoteFont]);

  /* persist */
  useEffect(() => {
    try {
      localStorage.setItem(STORE_KEY, JSON.stringify({
        tapa, filter, count, seen: Array.from(seenRef.current).slice(-400)
      }));
    } catch (e) {}
  }, [tapa, filter, count]);

  const recentSeen = () => Array.from(seenRef.current).slice(-10);

  /* avoid-list uses the actual recent texts, not normalised */
  const recentTexts = useRef([]);

  const prefetch = useCallback(async (key) => {
    if (!AI_OK) return;
    if (fetchingRef.current[key]) return;
    if (bufferRef.current[key].length >= 4) return;
    fetchingRef.current[key] = true;
    setGenActive(g => g + 1);
    try {
      const fresh = await generateBatch(key, recentTexts.current.slice(-8));
      const unique = [];
      const local = new Set(bufferRef.current[key].map(x => norm(x.t)));
      for (const item of fresh) {
        const nz = norm(item.t);
        if (seenRef.current.has(nz) || local.has(nz)) continue;
        if (key !== 'all' && item.k !== key) item.k = key;
        local.add(nz);
        unique.push(item);
      }
      bufferRef.current[key] = bufferRef.current[key].concat(unique);
    } catch (e) {
      /* silent — bank fallback covers it */
    } finally {
      fetchingRef.current[key] = false;
      setGenActive(g => Math.max(0, g - 1));
    }
  }, []);

  /* warm the buffer for the active filter */
  useEffect(() => {
    prefetch(filter);
  }, [filter, prefetch]);

  function takeFromBuffer(key) {
    const buf = bufferRef.current[key];
    while (buf.length) {
      const item = buf.shift();
      if (!seenRef.current.has(norm(item.t))) return item;
    }
    return null;
  }

  function bankFallback(key) {
    const pool = BANK.filter(x => key === 'all' || x.k === key);
    if (!pool.length) return null;
    let unseen = pool.filter(x => !seenRef.current.has(norm(x.t)));
    if (!unseen.length) {
      pool.forEach(x => seenRef.current.delete(norm(x.t)));
      unseen = pool.slice();
    }
    return { ...unseen[Math.floor(Math.random() * unseen.length)] };
  }

  const deliver = useCallback(() => {
    const f = filterRef.current;
    let next = takeFromBuffer(f);
    if (!next) next = bankFallback(f);
    if (!next) return;
    seenRef.current.add(norm(next.t));
    recentTexts.current.push(next.t);
    if (recentTexts.current.length > 16) recentTexts.current.shift();
    setTapa(next);
    setCount(c => c + 1);
    setAnimKey(k => k + 1);
    setCopied(false);
    prefetch(f); // refill in background
  }, [prefetch]);

  const copyStory = useCallback(async () => {
    if (!tapa) return;
    const txt = `"${tapa.t}"${tapa.a ? `\n— ${tapa.a}` : ''}\n\nTapa do Dia · Mulheres Milionárias`;
    try {
      await navigator.clipboard.writeText(txt);
    } catch (e) {
      const ta = document.createElement('textarea');
      ta.value = txt; document.body.appendChild(ta); ta.select();
      try { document.execCommand('copy'); } catch (_) {}
      document.body.removeChild(ta);
    }
    setCopied(true);
    setTimeout(() => setCopied(false), 1800);
  }, [tapa]);

  const typeMeta = tapa ? (TYPES[tapa.k] || {}) : {};

  const logout = useCallback(() => {
    if (window.authClear) window.authClear();
    setAccount(null);
    setAuthed(false);
  }, []);

  /* gate: sem sessão válida, mostra o login com a cara do app */
  if (!authed && window.LoginGate) {
    const Gate = window.LoginGate;
    return (
      <React.Fragment>
        <style>{APP_CSS}</style>
        <Gate onSuccess={(email) => { setAccount({ email: email, ok: true }); setAuthed(true); }} />
      </React.Fragment>
    );
  }

  return (
    <div className="stage">
      <style>{APP_CSS}</style>
      <div className="bg-glow" aria-hidden="true"></div>
      <div className="grain" aria-hidden="true"></div>

      <header className="masthead">
        <div className="mark-rule"></div>
        <h1 className="wordmark">Tapa do Dia</h1>
        <p className="subwordmark">Mulheres Milionárias</p>
      </header>

      <nav className="filters" aria-label="Filtrar por tipo de tapa">
        {FILTERS.map(f => (
          <button
            key={f.key}
            className={'chip' + (filter === f.key ? ' chip-on' : '')}
            onClick={() => setFilter(f.key)}
            type="button"
          >{f.label}</button>
        ))}
      </nav>

      <main className="arena">
        {tapa ? (
          <div
            key={animKey}
            className="card slapping"
            data-intensity={t.slap}
          >
            <div className="card-top">
              <span className="type-tag">{typeMeta.tag || ''}</span>
              {tapa.gen
                ? <span className="src src-ai" title="Gerado ao vivo">✦ ao vivo</span>
                : <span className="src" title="Do banco">do baralho</span>}
            </div>
            <blockquote className="tapa-quote">{tapa.t}</blockquote>
            {tapa.a && <cite className="tapa-author">{tapa.a}</cite>}
          </div>
        ) : (
          <div className="card card-empty">
            <span className="type-tag empty-kicker">Pronta?</span>
            <p className="empty-line">Aperte o botão e leve o seu tapa do dia.</p>
            <p className="empty-sub">Sem filtro de coach. Só verdade com dente.</p>
          </div>
        )}
      </main>

      <div className="controls">
        <button className="btn btn-primary" onClick={deliver} type="button">
          {count === 0 ? 'Receber o tapa' : 'Outro tapa'}
        </button>
        {tapa && (
          <button className="btn btn-ghost" onClick={copyStory} type="button">
            {copied ? 'Copiado ✓' : 'Copiar pra story'}
          </button>
        )}
      </div>

      <footer className="meta">
        <span className="counter">
          {count} {count === 1 ? 'tapa levado' : 'tapas levados'}
        </span>
        <span className="meta-dot">·</span>
        <span className={'gen-status' + (genActive > 0 ? ' gen-on' : '')}>
          {AI_OK
            ? (genActive > 0 ? 'gerando novos tapas' : 'gerador ao vivo pronto')
            : 'modo offline · baralho curado'}
        </span>
        <span className="meta-dot">·</span>
        <button className="logout-btn" type="button" onClick={logout} title="Encerrar sessão">sair</button>
      </footer>

      <TweaksPanel>
        <TweakSection label="Atmosfera" />
        <TweakRadio
          label="Tema" value={t.theme}
          options={[{ value: 'dark', label: 'Escuro' }, { value: 'claro', label: 'Claro' }]}
          onChange={(v) => setTweak('theme', v)}
        />
        <TweakColor
          label="Acento" value={t.accent}
          options={Object.keys(ACCENTS)}
          onChange={(v) => setTweak('accent', v)}
        />
        <TweakSection label="Frase" />
        <TweakRadio
          label="Tipografia" value={t.quoteFont}
          options={[{ value: 'display', label: 'Display' }, { value: 'impacto', label: 'Impacto' }, { value: 'editorial', label: 'Editorial' }]}
          onChange={(v) => setTweak('quoteFont', v)}
        />
        <TweakSection label="O tapa" />
        <TweakRadio
          label="Intensidade" value={t.slap}
          options={[{ value: 'sutil', label: 'Sutil' }, { value: 'medio', label: 'Médio' }, { value: 'forte', label: 'Forte' }]}
          onChange={(v) => setTweak('slap', v)}
        />
      </TweaksPanel>
    </div>
  );
}

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "claro",
  "accent": "#d6b87a",
  "quoteFont": "display",
  "slap": "medio"
}/*EDITMODE-END*/;

/* ---------------------------------------------------------------- styles */

const APP_CSS = `
.stage{
  position:relative; min-height:100dvh; width:100%;
  display:flex; flex-direction:column; align-items:center;
  padding:clamp(20px,5vh,52px) 22px calc(env(safe-area-inset-bottom) + 26px);
  gap:clamp(18px,3vh,30px);
  isolation:isolate;
}
.bg-glow{
  position:fixed; inset:0; z-index:-2; pointer-events:none;
  background:
    radial-gradient(60% 48% at 50% 38%, var(--accent-glow), transparent 70%),
    linear-gradient(180deg, var(--bg-2), var(--bg) 60%);
}
.grain{
  position:fixed; inset:0; z-index:-1; pointer-events:none; opacity:var(--grain);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode:overlay;
}

/* masthead */
.masthead{ text-align:center; display:flex; flex-direction:column; align-items:center; gap:6px; }
.mark-rule{ width:38px; height:2px; background:var(--accent); margin-bottom:10px; border-radius:2px; }
.wordmark{
  font-family:var(--font-quote); font-weight:700; line-height:.9;
  font-size:clamp(30px,8vw,46px); letter-spacing:-0.03em; color:var(--ink);
}
.subwordmark{
  font-family:var(--font-mono); text-transform:uppercase;
  letter-spacing:.42em; font-size:clamp(9px,2.6vw,11px); color:var(--accent);
  padding-left:.42em;
}

/* filters */
.filters{
  display:flex; flex-wrap:wrap; justify-content:center; gap:8px;
  max-width:560px;
}
.chip{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.04em;
  text-transform:uppercase;
  color:var(--ink-soft); background:transparent;
  border:1px solid var(--hairline); border-radius:999px;
  padding:7px 13px; cursor:pointer;
  transition:all .18s ease; white-space:nowrap;
}
.chip:hover{ color:var(--ink); border-color:var(--accent); }
.chip-on{
  color:var(--accent-ink); background:var(--accent); border-color:var(--accent);
}

/* arena + card */
.arena{
  flex:1; width:100%; max-width:620px;
  display:flex; align-items:center; justify-content:center;
  min-height:0;
}
.card{
  position:relative; width:100%;
  background:linear-gradient(180deg,var(--surface-2),var(--surface));
  border:1px solid var(--hairline);
  border-radius:22px;
  padding:clamp(28px,6vw,52px) clamp(24px,6vw,48px) clamp(30px,6vw,50px);
  box-shadow:var(--card-shadow);
  display:flex; flex-direction:column; gap:22px;
  overflow:hidden;
}
.card::before{
  content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--accent); opacity:.9;
}
.card-top{ display:flex; align-items:center; justify-content:space-between; gap:12px; }
.type-tag{
  font-family:var(--font-mono); text-transform:uppercase;
  font-size:10px; letter-spacing:.26em; color:var(--accent);
}
.src{
  font-family:var(--font-mono); font-size:9.5px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--ink-faint);
}
.src-ai{ color:var(--accent); animation:pulse-dot 2.6s ease-in-out infinite; }
.tapa-quote{
  font-family:var(--font-quote);
  font-weight:var(--quote-weight); font-style:var(--quote-style);
  letter-spacing:var(--quote-tracking); line-height:var(--quote-lh);
  font-size:clamp(27px,6.6vw,46px);
  color:var(--ink); text-wrap:balance;
  animation:quote-in .5s cubic-bezier(.2,.7,.2,1) both;
}
.tapa-author{
  font-family:var(--font-mono); font-style:normal;
  font-size:13px; letter-spacing:.04em; color:var(--ink-soft);
  animation:quote-in .55s .08s cubic-bezier(.2,.7,.2,1) both;
}
.tapa-author::before{ content:"— "; color:var(--accent); }

/* empty state */
.card-empty{ gap:14px; }
.empty-kicker{ color:var(--ink-faint); }
.empty-line{
  font-family:var(--font-quote); font-weight:700; letter-spacing:-0.02em;
  line-height:1.06; font-size:clamp(26px,6vw,40px); color:var(--ink);
  text-wrap:balance;
}
.empty-sub{ font-family:var(--font-mono); font-size:12px; color:var(--ink-faint); letter-spacing:.02em; }

/* controls */
.controls{
  display:flex; flex-wrap:wrap; gap:10px; justify-content:center; width:100%;
  max-width:620px;
}
.btn{
  font-family:var(--font-quote); font-weight:600;
  font-size:clamp(15px,3.6vw,17px); letter-spacing:-0.01em;
  border-radius:999px; padding:15px 30px; cursor:pointer;
  transition:transform .12s ease, background .18s ease, color .18s ease, border-color .18s ease;
  border:1px solid transparent;
}
.btn:active{ transform:translateY(1px) scale(.985); }
.btn-primary{
  background:var(--accent); color:var(--accent-ink);
  box-shadow:0 12px 32px -12px var(--accent-glow);
  flex:1 1 auto; min-width:200px;
}
.btn-primary:hover{ filter:brightness(1.06); }
.btn-ghost{
  background:transparent; color:var(--ink-soft);
  border-color:var(--hairline);
}
.btn-ghost:hover{ color:var(--ink); border-color:var(--accent); }

/* meta */
.meta{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap; justify-content:center;
  font-family:var(--font-mono); font-size:11px; letter-spacing:.06em;
  color:var(--ink-faint); text-transform:uppercase;
}
.counter{ color:var(--ink-soft); }
.meta-dot{ opacity:.5; }
.logout-btn{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--ink-faint); background:transparent; border:0; cursor:pointer; padding:0;
  transition:color .18s;
}
.logout-btn:hover{ color:var(--accent); }
.gen-status{ display:inline-flex; align-items:center; gap:7px; }
.gen-status::before{
  content:""; width:6px; height:6px; border-radius:50%;
  background:var(--ink-faint);
}
.gen-on{ color:var(--accent); }
.gen-on::before{ background:var(--accent); animation:pulse-dot 1s ease-in-out infinite; }

@media (max-width:430px){
  .btn-ghost{ flex:1 1 100%; }
}
`;

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
