/* === preview.jsx ====================================================
   Renders the personalized Nous app inside the browser frame.
   Reactive to:
   - state.cosmetic       (Level 1 → sidebar branding, user name)
   - state.data           (Level 2 → suppliers, KPI values, SKUs)
   - state.persona        (Level 3 → which screen opens + first message)
   - state.vars           (Level 4 → variable substitution in text)
   - state.hotspots       (Level 5 → overlayed callouts)
   - state.activeScreen   (which Nous surface is on screen)
   ===================================================================== */

const { DOCOL, PERSONAS, FR_MAP, DYNAMIC_VARS } = window.STUDIO_DATA;

/* ---------- helpers ---------- */
function firstName(full) { return (full || '').trim().split(' ')[0]; }
function initialsOf(full) {
    return (full || '').trim().split(/\s+/).slice(0,2).map(s => s[0] || '').join('').toUpperCase();
}
function findPersona(id) { return PERSONAS.find(p => p.id === id) || PERSONAS[0]; }
function timeGreeting() {
    const h = new Date().getHours();
    return h < 12 ? 'manhã' : h < 18 ? 'tarde' : 'noite';
}

/* Pull the canonical value for a variable. Vars override; fall back to
   the original Level 1/3 source so the preview always renders something. */
function V(state, key, fallback) {
    const raw = state.vars?.[key];
    if (raw !== undefined && String(raw).trim() !== '') return String(raw);
    return fallback ?? '';
}

/* Replace {{varname}} with state.vars[varname] (or {{varname}} if missing). */
function subVars(text, vars) {
    if (typeof text !== 'string' || !text) return text;
    return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (_, k) => {
        const v = vars?.[k];
        return (v !== undefined && String(v).trim() !== '') ? String(v) : `{{${k}}}`;
    });
}

/* Render text that supports **bold** and \n as paragraphs. Used in
   question answers so AE can format the narrative simply. */
function RichText({ text, vars }) {
    const resolved = subVars(text || '', vars);
    const paragraphs = resolved.split(/\n\n+/);
    return (
        <>
            {paragraphs.map((p, pi) => (
                <p key={pi} style={{ marginBottom: pi < paragraphs.length - 1 ? 12 : 0 }}>
                    {p.split(/(\*\*[^*]+\*\*)/g).map((seg, si) =>
                        seg.startsWith('**') && seg.endsWith('**')
                            ? <strong key={si}>{seg.slice(2, -2)}</strong>
                            : <React.Fragment key={si}>{seg}</React.Fragment>
                    )}
                </p>
            ))}
        </>
    );
}

/* Resolve an `uploaded:<idbKey>` HTML artifact from IndexedDB into a
   Blob URL and render it like a normal iframe report. */
function StoredReport({ idbKey, title, meta, height }) {
    const [url, setUrl] = React.useState(null);
    const [missing, setMissing] = React.useState(false);
    React.useEffect(() => {
        let objUrl;
        let alive = true;
        window.STUDIO_DATA.idbGetHtml(idbKey).then(text => {
            if (!alive) return;
            if (text == null) { setMissing(true); return; }
            objUrl = URL.createObjectURL(new Blob([text], { type: 'text/html' }));
            setUrl(objUrl);
        }).catch(() => setMissing(true));
        return () => { alive = false; if (objUrl) URL.revokeObjectURL(objUrl); };
    }, [idbKey]);
    return (
        <Card className="answer-artifact answer-artifact--iframe" style={{ marginTop: 18 }}>
            <div className="artifact-head">
                <div className="artifact-title">{title}</div>
                {url && (
                    <a className="artifact-open" href={url} target="_blank" rel="noopener noreferrer">
                        <Icon name="arrow-up-right" size={12} /> Abrir em nova aba
                    </a>
                )}
            </div>
            {meta.length > 0 && (
                <div className="artifact-meta">
                    {meta.map((m, i) => (
                        <span key={i} className={i === 0 ? 'artifact-meta-active' : 'artifact-meta-item'}>{m}</span>
                    ))}
                </div>
            )}
            {missing ? (
                <div className="artifact-loading artifact-loading--err">
                    <Icon name="search" size={14} />
                    Arquivo HTML não encontrado neste navegador. Reenvie o arquivo pelo gerenciador de Respostas P2P.
                </div>
            ) : url ? (
                <iframe className="artifact-iframe-frame" src={url} title={title} style={{ height: height + 'px' }} />
            ) : (
                <div className="artifact-loading">Carregando artefato…</div>
            )}
        </Card>
    );
}

/* Render a question's answer artifact (kpi-grid, iframe, custom-html). */
function AnswerArtifact({ artifact, vars, state, onSetState }) {
    if (!artifact) return null;
    if (artifact.type === 'iframe') {
        const src = subVars(artifact.src || '', vars);
        const title = subVars(artifact.title || 'Artefato', vars);
        const meta = Array.isArray(artifact.meta) ? artifact.meta.map(m => subVars(m, vars)) : [];
        const height = artifact.height || 720;
        /* uploaded:<idbKey> → resolve HTML from IndexedDB into a Blob URL */
        if (src.startsWith('uploaded:')) {
            return <StoredReport idbKey={src.slice('uploaded:'.length)} title={title} meta={meta} height={height} />;
        }
        return (
            <Card className="answer-artifact answer-artifact--iframe" style={{ marginTop: 18 }}>
                <div className="artifact-head">
                    <div className="artifact-title">{title}</div>
                    <a className="artifact-open" href={src} target="_blank" rel="noopener noreferrer">
                        <Icon name="arrow-up-right" size={12} /> Abrir em nova aba
                    </a>
                </div>
                {meta.length > 0 && (
                    <div className="artifact-meta">
                        {meta.map((m, i) => (
                            <span key={i} className={i === 0 ? 'artifact-meta-active' : 'artifact-meta-item'}>
                                {m}
                            </span>
                        ))}
                    </div>
                )}
                <iframe
                    className="artifact-iframe-frame"
                    src={src}
                    title={title}
                    style={{ height: height + 'px' }}
                    loading="lazy"
                />
            </Card>
        );
    }
    if (artifact.type === 'custom-html') {
        const html = subVars(artifact.html || '', vars);
        return (
            <div className="answer-artifact answer-artifact--html">
                <div dangerouslySetInnerHTML={{ __html: html }} />
            </div>
        );
    }
    /* default: kpi-grid */
    return (
        <Card className="answer-artifact" style={{ marginTop: 18 }}>
            <div className="artifact-head">
                <div className="artifact-title">{subVars(artifact.title || 'Resposta', vars)}</div>
                <div className="artifact-toolbar">
                    {['bookmark', 'arrow-up', 'file-text', 'share', 'eye'].map((i, k) => (
                        <span key={k} className="artifact-tool"><Icon name={i === 'arrow-up' ? 'arrow-up' : 'file-text'} size={12} /></span>
                    ))}
                </div>
            </div>
            {Array.isArray(artifact.meta) && artifact.meta.length > 0 && (
                <div className="artifact-meta">
                    {artifact.meta.map((m, i) => (
                        <span key={i} className={i === 0 ? 'artifact-meta-active' : 'artifact-meta-item'}>
                            {subVars(m, vars)}
                        </span>
                    ))}
                </div>
            )}
            <div className="artifact-kpi-grid">
                {(artifact.kpis || []).map((k, i) => {
                    const canDrill = state && onSetState && window.openDrilldown && !state.editMode;
                    const blob = `${k.label || ''} ${k.value || ''}`.toLowerCase();
                    const metric = /(saving|desbloque|roi|economia|ganho)/.test(blob) ? 'saving' : 'spend';
                    const onClick = canDrill
                        ? () => window.openDrilldown(onSetState, state, { title: subVars(k.label, vars) || subVars(artifact.title, vars), metric })
                        : undefined;
                    return (
                        <div key={i}
                            className={`artifact-kpi${k.highlight ? ' artifact-kpi--hl' : ''}${canDrill ? ' artifact-kpi--drill' : ''}`}
                            onClick={onClick}
                            title={canDrill ? 'Ver detalhamento' : undefined}>
                            <div className="artifact-kpi-label">{subVars(k.label, vars)}</div>
                            <div className="artifact-kpi-value">{subVars(k.value, vars)}</div>
                            {k.sub && <div className="artifact-kpi-sub">{subVars(k.sub, vars)}</div>}
                        </div>
                    );
                })}
            </div>
        </Card>
    );
}

/* ---------- inline variable pill (level 4 preview mode) ---------- */
function Var({ k, vars, level4Open }) {
    const value = vars[k] ?? '';
    if (level4Open) {
        return <span className="var-pill">{`{{${k}}}`}</span>;
    }
    return <span className="var-resolved">{value}</span>;
}

/* ==================================================================
   PERSONALIZED SIDEBAR
   ================================================================== */
function PreviewSidebar({ state, onNavigate }) {
    const { cosmetic, persona, activeScreen } = state;
    const personaObj = findPersona(persona.activeId);
    const personaName = V(state, 'persona_nome', personaObj.name);
    const empresa = V(state, 'empresa', cosmetic.company);
    const recents = state.recentsByPersona?.[persona.activeId] || state.recentsByPersona?.ops || [];
    const primary = [
        { id: 'chat',    label: 'Chats',                icon: 'message-circle' },
        { id: 'mining',  label: 'Process Mining',       icon: 'bar-chart-3' },
        { id: 'agents',  label: 'Agentes',              icon: 'briefcase' },
        { id: 'roai',    label: 'RoAI - Value Tracker', icon: 'trending-up' },
        { id: 'rotinas', label: 'Rotinas',              icon: 'clock' },
        { id: 'achados', label: 'Achados',              icon: 'file-text' },
    ];
    const secondary = [
        { id: 'brain',   label: 'Agentes de Conhecimento', icon: 'brain' },
        { id: 'consumo', label: 'Consumo',                 icon: 'gauge' },
        { id: 'admin',   label: 'Admin Center',            icon: 'settings' },
    ];

    return (
        <aside className="nous-sidebar">
            <div className="logo-row">
                <div className="lockup">
                    <NousMark size={26} />
                    <span className="lockup-text">
                        <strong>UpFlux</strong> <span className="regular">Nous</span>
                    </span>
                </div>
                <div className="sidebar-actions">
                    <button className="sidebar-bell" title="Notificações">
                        <Icon name="bell" size={14} />
                        <span className="bell-badge">22</span>
                        <span className="bell-dot" />
                    </button>
                    <button className="sidebar-collapse" title="Recolher menu">
                        <Icon name="panel-left-close" size={14} />
                    </button>
                </div>
            </div>
            <nav>
                {primary.map((n) => (
                    <NavItem
                        key={n.id}
                        icon={<Icon name={n.icon} size={16} />}
                        label={n.label}
                        active={activeScreen === n.id}
                        onClick={() => onNavigate(n.id)}
                    />
                ))}
            </nav>
            <Button
                variant="primary"
                icon={<Icon name="sparkles" size={14} />}
                full
                style={{ marginBottom: 12 }}
                onClick={() => onNavigate('chat', { newConversation: true })}
            >
                Pergunte ao Nous
            </Button>
            <div className="search-input" style={{ marginBottom: 16 }}>
                <Icon name="search" size={14} style={{ color: 'var(--text-muted)' }} />
                <input placeholder="Buscar..." />
            </div>
            <div style={{ marginBottom: 12 }}>
                <div className="section-label">{(empresa || '').toUpperCase()}</div>
                <button className="org-chip">
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                        <Icon name="workflow" size={14} style={{ color: 'var(--nous-purple)' }} />
                        <span>1 - Compras ao Pagamento</span>
                    </span>
                    <Icon name="chevron-down" size={12} style={{ color: 'var(--text-muted)' }} />
                </button>
            </div>
            <div className="recents">
                <div className="section-label">Recentes</div>
                <div className="recents-list">
                    {recents.map((c) => (
                        <button key={c} className="recent-item" onClick={() => onNavigate('chat')}>{c}</button>
                    ))}
                </div>
            </div>
            <nav className="secondary-nav">
                {secondary.map((n) => (
                    <NavItem
                        key={n.id}
                        icon={<Icon name={n.icon} size={16} />}
                        label={n.label}
                        active={activeScreen === n.id}
                        onClick={() => onNavigate(n.id)}
                    />
                ))}
            </nav>
            <div className="footer">
                <div className="user-row">
                    <div className="avatar">
                        {initialsOf(personaName)}
                    </div>
                    <span style={{ fontSize: 13, color: 'var(--text-primary)' }}>{personaName}</span>
                </div>
                <button className="trust">
                    <Icon name="shield" size={12} />Trust Center
                </button>
            </div>
        </aside>
    );
}

/* ==================================================================
   CHAT — landing + thread depending on persona
   ================================================================== */
/* ==================================================================
   CHAT — LLM-like conversation with knowledge-base matching
   ================================================================== */

/* Flatten the discovery library into a searchable index. */
function buildAnswerIndex(discovery) {
    const out = [];
    Object.entries(discovery || {}).forEach(([category, list]) => {
        (list || []).forEach((q) => {
            if (q.answer) out.push({ category, id: q.id, label: q.label, answer: q.answer });
        });
    });
    return out;
}
function normalizeText(s) {
    return (s || '').toLowerCase()
        .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
        .replace(/[^a-z0-9\s]/g, ' ')
        .replace(/\s+/g, ' ').trim();
}
const CHAT_STOPWORDS = new Set(['a','o','os','as','de','da','do','das','dos','e','em','um','uma','que','qual','quais','quanto','quantas','como','para','por','no','na','nos','nas','com','sem','meu','minha','sao','esta','estao','ter','tem','the','of']);
function significantWords(s) {
    return normalizeText(s).split(' ').filter(w => w.length > 2 && !CHAT_STOPWORDS.has(w));
}
/* Score a user query against an indexed question; returns 0..1 */
function matchScore(query, item) {
    const qw = significantWords(query);
    if (!qw.length) return 0;
    const iw = new Set(significantWords(item.label));
    /* keyword aliases boost domain terms */
    let hits = 0;
    qw.forEach(w => { if (iw.has(w)) hits++; });
    const recall = hits / qw.length;
    const precision = hits / Math.max(iw.size, 1);
    return recall * 0.7 + precision * 0.3;
}
function findBestAnswer(query, index) {
    let best = null, bestScore = 0;
    index.forEach(item => {
        const s = matchScore(query, item);
        if (s > bestScore) { bestScore = s; best = item; }
    });
    return bestScore >= 0.34 ? { ref: { category: best.category, id: best.id }, score: bestScore } : null;
}

function PreviewChat({ state, level4Open, onSetState }) {
    const { persona } = state;
    const personaObj = findPersona(persona.activeId);
    const personaName = V(state, 'persona_nome', personaObj.name);
    const fn = firstName(personaName);
    const conversation = state.conversation || [];
    /* Merge the standard P2P bank into this demo's discovery at render time,
       so every P2P demo always carries the mandatory standardized answers. */
    const discovery = React.useMemo(
        () => window.STUDIO_DATA.mergeStandardBank(state.discovery),
        [state.discovery]
    );
    const index = React.useMemo(() => buildAnswerIndex(discovery), [discovery]);

    const ask = (text, presetRef) => {
        const clean = (text || '').trim();
        if (!clean) return;
        const ref = presetRef || (findBestAnswer(clean, index) || {}).ref || null;
        const turn = { id: 't' + Date.now().toString(36) + Math.random().toString(36).slice(2, 5), question: clean, ref };
        const c = state.consumo || { used: 0, quota: 50 };
        onSetState({ ...state, conversation: [...conversation, turn], consumo: { ...c, used: (c.used || 0) + 1 } });
    };

    if (conversation.length === 0) {
        return <ChatLanding state={state} discovery={discovery} firstName={fn} level4Open={level4Open} onSetState={onSetState} onAsk={ask} />;
    }

    return (
        <div className="nous-scroll chat-conversation">
            <div className="chat-thread-bar">
                <button className="chat-back-btn" onClick={() => onSetState({ ...state, conversation: [], activeCategory: null })}>
                    <Icon name="plus" size={13} />
                    Nova conversa
                </button>
            </div>
            <div className="chat-stream">
                {conversation.map((turn) => (
                    <ConversationTurn key={turn.id} turn={turn} state={state} discovery={discovery} onSetState={onSetState} />
                ))}
            </div>
            <div className="composer-dock">
                <ChatInput onSubmit={(t) => ask(t)} />
            </div>
        </div>
    );
}

/* One question→answer turn, with a "formulating" streaming phase. */
function ConversationTurn({ turn, state, discovery, onSetState }) {
    const [phase, setPhase] = React.useState('thinking');
    const [elapsed, setElapsed] = React.useState(0);
    React.useEffect(() => {
        if (phase !== 'thinking') return;
        const started = Date.now();
        const tick = setInterval(() => setElapsed(((Date.now() - started) / 1000)), 200);
        const done = setTimeout(() => { setPhase('done'); clearInterval(tick); }, 1500 + Math.random() * 1100);
        return () => { clearInterval(tick); clearTimeout(done); };
    }, []);

    const dsc = discovery || state.discovery;
    const q = turn.ref && dsc?.[turn.ref.category]?.find(x => x.id === turn.ref.id);
    const answer = q?.answer;
    const demoId = state.__demoId || 'demo';
    const STATUS = ['Consultando a base de conhecimento…', 'Analisando dados do processo…', 'Formulando resposta…'];
    const statusIdx = Math.min(STATUS.length - 1, Math.floor(elapsed / 0.7));

    return (
        <>
            <ChatMessage role="user">{turn.question}</ChatMessage>
            {phase === 'thinking' ? (
                <div className="msg-assistant-wrap">
                    <StreamingIndicator status={STATUS[statusIdx]} elapsed={elapsed.toFixed(1)} />
                </div>
            ) : answer ? (
                <ChatMessage role="assistant">
                    <div className="msg-assistant" style={{ marginBottom: 0 }}>
                        <RichText text={answer.text} vars={state.vars} />
                    </div>
                    {answer.artifact && <AnswerArtifact artifact={answer.artifact} vars={state.vars} state={state} onSetState={onSetState} />}
                </ChatMessage>
            ) : (
                <ChatMessage role="assistant">
                    <div className="chat-nodata">
                        <Icon name="search" size={15} style={{ color: 'var(--text-muted)', flexShrink: 0, marginTop: 1 }} />
                        <div>
                            <strong>Não encontrei dados sobre esse tema na base de conhecimento desta demonstração.</strong>
                            <p style={{ margin: '6px 0 0', color: 'var(--text-secondary)' }}>
                                Esta base cobre compras ao pagamento (P2P): cotações, fornecedores, pagamentos,
                                cauda longa, conformidade e o business case de RoAI. Tente reformular ou explore as
                                categorias sugeridas abaixo.
                            </p>
                        </div>
                    </div>
                </ChatMessage>
            )}
        </>
    );
}

/* Chat-home landing — greeting, composer, categorias (clickable) + expanded
   discovery questions when a category is open. */
function ChatLanding({ state, discovery: mergedDiscovery, firstName, level4Open, onSetState, onAsk }) {
    const { activeCategory } = state;
    const discovery = mergedDiscovery || state.discovery;
    /* QW2 — mostra até `displayLimit` perguntas; resto sob "Mostrar mais" */
    const displayLimit = (state.discoveryDisplayLimit || 6);
    const [showAll, setShowAll] = React.useState(false);
    React.useEffect(() => { setShowAll(false); }, [activeCategory]);
    const setCategory = (cat) => onSetState({
        ...state,
        activeCategory: state.activeCategory === cat ? null : cat,
    });
    const CATS = [
        { id: 'estrategico', label: 'Estratégico' },
        { id: 'gestao',      label: 'Gestão' },
        { id: 'operacional', label: 'Operacional' },
    ];
    return (
        <div className="chat-home">
            <div className="mark"><NousMark size={56} /></div>
            <h1 className="greeting">
                Como posso ajudar nesta {timeGreeting()},{' '}
                {level4Open
                    ? <Var k="persona_nome" vars={state.vars} level4Open={true} />
                    : <Editable id="chat.greeting.name" defaultValue={firstName} />}?
            </h1>
            <div className="composer-wrap"><ChatInput onSubmit={(t) => onAsk(t)} /></div>
            <div className="shortcuts">
                <span className="label">Ou explore por categoria:</span>
                {CATS.map((c) => (
                    <button
                        key={c.id}
                        className={`category-chip${activeCategory === c.id ? ' active' : ''}`}
                        onClick={() => setCategory(c.id)}
                    >
                        <span className="icon"><Icon name="sparkles" size={14} /></span>
                        {c.label}
                    </button>
                ))}
            </div>
            {activeCategory && (
                <div className="category-panel">
                    <div className="category-panel-head">
                        <span className="category-panel-title">
                            <Icon name="sparkles" size={13} style={{ color: 'var(--nous-purple)', marginRight: 6, verticalAlign: -1 }} />
                            {CATS.find(c => c.id === activeCategory).label}
                        </span>
                        <button className="category-panel-close" onClick={() => setCategory(null)}>×</button>
                    </div>
                    <ul className="category-questions">
                        {(() => {
                            const all = discovery?.[activeCategory] || [];
                            const shown = showAll ? all : all.slice(0, displayLimit);
                            return shown.map((q) => (
                                <li
                                    key={q.id}
                                    className="category-question"
                                    onClick={() => onAsk(q.label, { category: activeCategory, id: q.id })}
                                >
                                    <span className="qid">{q.id}</span>
                                    <span className="qlabel">
                                        <Editable id={`discovery.${activeCategory}.${q.id}`} defaultValue={q.label} />
                                    </span>
                                    <Icon name="chevron-right" size={12} style={{ color: 'var(--text-muted)' }} />
                                </li>
                            ));
                        })()}
                    </ul>
                    {(discovery?.[activeCategory] || []).length > displayLimit && (
                        <button className="category-more" onClick={() => setShowAll(v => !v)}>
                            {showAll
                                ? 'Mostrar menos'
                                : `Mostrar mais ${(discovery?.[activeCategory] || []).length - displayLimit} perguntas`}
                        </button>
                    )}
                </div>
            )}
        </div>
    );
}

/* Ops director answer — gargalos P2P, KPIs executivos */
function OpsAnswer({ state, level4Open }) {
    const { vars, cosmetic, data } = state;
    const empresa = V(state, 'empresa', cosmetic.company);
    const meta = V(state, 'meta_lead_time', '25 dias');
    return (
        <ChatMessage role="assistant">
            <p>
                Identifiquei <strong><Editable id="ops.gargalosCount" defaultValue="3" /> gargalos principais</strong> no fluxo P2P da{' '}
                {level4Open
                    ? <Var k="empresa" vars={vars} level4Open={true} />
                    : <span className="var-resolved">{empresa}</span>}
                {' '}no último trimestre. O maior impacto está na etapa de{' '}
                <strong>aprovação de PO acima de <Editable id="ops.aprovLimit" defaultValue="R$ 250k" /></strong>, com lead-time médio
                de <strong><Editable id="ops.leadTime" defaultValue="11d 6h" /></strong>, <Editable id="ops.vsMeta" defaultValue="2,4×" /> a meta do setor.
            </p>
            <div className="grid-3" style={{ marginTop: 16 }}>
                <KPICard
                    label={<Editable id="kpi.lt.label" defaultValue="LEAD TIME PO > 250K" />}
                    value={<Editable id="kpi.lt.value" defaultValue="11d 6h" />}
                    sentiment="danger"
                    delta={{ value: <><Editable id="kpi.lt.delta" defaultValue="38%" /> vs meta</>, positive: false }}
                    subtitle="último trimestre"
                />
                <KPICard
                    label={<Editable id="kpi.fora.label" defaultValue="POs FORA DO FLUXO" />}
                    value={<Editable id="kpi.fora.value" defaultValue="142" />}
                    sentiment="warning"
                    delta={{ value: <Editable id="kpi.fora.delta" defaultValue="12%" />, positive: false }}
                    subtitle={<><Editable id="kpi.fora.sub" defaultValue="11%" /> do total aprovado</>}
                />
                <KPICard
                    label={<Editable id="kpi.alc.label" defaultValue="VALOR EM ALÇADA" />}
                    value={<Editable id="kpi.alc.value" defaultValue="R$ 4,2M" />}
                    sentiment="info"
                    subtitle={<><Editable id="kpi.alc.sub" defaultValue={String(data.suppliers.length)} /> fornecedores impactados</>}
                />
            </div>
            <Card style={{ marginTop: 16 }}>
                <div className="kpi-label" style={{ marginBottom: 14 }}>
                    Etapas com maior lead-time · {empresa}
                </div>
                <Bottleneck step="Aprovação PO > R$ 250k" pct={92} value="11d 6h" tone="danger" />
                <Bottleneck step="Cotação multipla (≥3 fornecedores)" pct={68} value="6d 18h" tone="warning" />
                <Bottleneck step="Recebimento físico · Joinville" pct={42} value="2d 22h" tone="warning" />
                <Bottleneck step="Conferência fiscal" pct={22} value="0d 18h" />
                <Bottleneck step="Pagamento" pct={15} value="0d 06h" />
            </Card>
            <p style={{ marginTop: 16, color: 'var(--text-tertiary)', fontSize: 14 }}>
                <strong style={{ color: 'var(--text-primary)', fontWeight: 500 }}>
                    Próximo passo recomendado:
                </strong>{' '}
                automatizar o roteamento para aprovadores secundários quando o lead-time
                exceder 3 dias, estimativa de ganho de{' '}
                {level4Open
                    ? <Var k="meta_lead_time" vars={vars} level4Open={true} />
                    : <span className="var-resolved">{meta}</span>}{' '}
                no ciclo total.
            </p>
        </ChatMessage>
    );
}

/* Analyst answer — drill em cotações Wieland */
function AnalystAnswer({ state }) {
    const fornec = V(state, 'fornec_top', 'Wieland do Brasil');
    return (
        <ChatMessage role="assistant">
            <p>
                Encontrei <strong>7 cotações abertas</strong> com a <strong>{fornec}</strong>, todas
                acima de 5 dias sem retorno. Total previsto: <strong>R$ 1,82M</strong>.
            </p>
            <Card style={{ marginTop: 16 }}>
                <div className="kpi-label" style={{ marginBottom: 12 }}>
                    Cotações {fornec} · abertas
                </div>
                <table style={{ width: '100%', fontSize: 12.5, borderCollapse: 'collapse' }}>
                    <thead>
                        <tr style={{ color: 'var(--text-secondary)', fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                            <th style={{ textAlign: 'left',  padding: '6px 8px', borderBottom: '1px solid var(--surface-elevated)' }}>RFQ</th>
                            <th style={{ textAlign: 'left',  padding: '6px 8px', borderBottom: '1px solid var(--surface-elevated)' }}>Material</th>
                            <th style={{ textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid var(--surface-elevated)' }}>Valor previsto</th>
                            <th style={{ textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid var(--surface-elevated)' }}>Aberta há</th>
                        </tr>
                    </thead>
                    <tbody style={{ fontFamily: 'var(--font-mono)', fontSize: 12 }}>
                        {[
                            ['RFQ-2026-0489', 'Latão CW617N · 40kg/lote',      'R$ 482.300', '11 dias'],
                            ['RFQ-2026-0492', 'Latão CW602N · 25kg/lote',      'R$ 318.900', '9 dias'],
                            ['RFQ-2026-0501', 'Cobre eletrolítico C11000',      'R$ 264.500', '8 dias'],
                            ['RFQ-2026-0507', 'Latão CW724R · 18kg/lote',      'R$ 198.200', '7 dias'],
                            ['RFQ-2026-0513', 'Bronze SAE 660 · usinagem',     'R$ 184.700', '7 dias'],
                            ['RFQ-2026-0519', 'Vergalhão latão · diam. 25mm',  'R$ 218.400', '6 dias'],
                            ['RFQ-2026-0522', 'Tarugos latão · fund. semi',    'R$ 156.000', '5 dias'],
                        ].map((r, i) => (
                            <tr key={i} style={{ borderBottom: '1px solid #F3F4F6' }}>
                                <td style={{ padding: '7px 8px', color: 'var(--nous-purple)' }}>{r[0]}</td>
                                <td style={{ padding: '7px 8px', color: 'var(--text-primary)', fontFamily: 'var(--font-sans)' }}>{r[1]}</td>
                                <td style={{ padding: '7px 8px', textAlign: 'right' }}>{r[2]}</td>
                                <td style={{ padding: '7px 8px', textAlign: 'right', color: 'var(--danger)' }}>{r[3]}</td>
                            </tr>
                        ))}
                    </tbody>
                </table>
            </Card>
            <p style={{ marginTop: 16, color: 'var(--text-tertiary)', fontSize: 14 }}>
                <strong style={{ color: 'var(--text-primary)', fontWeight: 500 }}>
                    Sugestão:
                </strong>{' '}
                escalar para o gestor de categoria. Todas estas RFQs envolvem o mesmo
                comprador (Renato F.) e o histórico mostra resposta em 48h após escalação.
            </p>
        </ChatMessage>
    );
}

/* ==================================================================
   AGENTES — Docol-specific automations
   ================================================================== */
/* ==================================================================
   AGENTES — delegates to window.PreviewAgents (in agents-achados.jsx)
   ================================================================== */
/* Replaced by the version in agents-achados.jsx via window.PreviewAgents */

/* ==================================================================
   ROTINAS
   ================================================================== */
function PreviewRotinas({ state }) {
    const { cosmetic } = state;
    const empresa = V(state, 'empresa', cosmetic.company);
    const routines = [
        { name: 'Sync SAP MM · POs do dia',           when: 'A cada 30 min', last: 'há 12 min',  status: 'live',    desc: 'Importa POs criadas no SAP MM e atualiza o grafo de eventos do Process Mining.' },
        { name: 'Cálculo de KPIs · ciclo P2P',         when: 'Diário · 06:00', last: 'hoje 06:01', status: 'live',    desc: 'Recalcula lead-time, % aprovado fora do fluxo, glosa de NF e populariza o RoAI Value Tracker.' },
        { name: 'Notificação WhatsApp · gestores',     when: 'Diário · 08:30', last: 'hoje 08:30', status: 'live',    desc: 'Envia para Marcos Silveira + Camila Reis um snapshot do dia anterior com 3 KPIs.' },
        { name: 'Backup de eventos · Joinville',       when: 'Diário · 23:00', last: 'ontem 23:01',status: 'live',    desc: 'Snapshot dos eventos do polo de Joinville · 30 dias de retenção em S3.' },
        { name: 'Reindex semântico · base de dados',   when: 'Semanal · sábado',last: '17 mai',    status: 'paused',  desc: 'Reconstrói o índice semântico das descrições de material para melhorar a busca em pt-BR.' },
        { name: 'Sync Coupa · cotações abertas',       when: 'A cada 1h',      last: 'há 7 min',  status: 'live',    desc: 'Importa cotações abertas no Coupa para o pipeline de Achados.' },
    ];
    return (
        <div className="screen-pad nous-scroll">
            <h2 className="screen-title">Rotinas</h2>
            <p className="screen-sub">
                Tarefas agendadas que mantém o Nous em sincronia com SAP MM, Coupa e fontes
                de dados da {empresa}.
            </p>
            <Card padded={false}>
                <table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
                    <thead>
                        <tr style={{ color: 'var(--text-secondary)', fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                            <th style={{ textAlign: 'left',  padding: '12px 16px', borderBottom: '1px solid var(--surface-elevated)' }}>Rotina</th>
                            <th style={{ textAlign: 'left',  padding: '12px 16px', borderBottom: '1px solid var(--surface-elevated)' }}>Recorrência</th>
                            <th style={{ textAlign: 'left',  padding: '12px 16px', borderBottom: '1px solid var(--surface-elevated)' }}>Última execução</th>
                            <th style={{ textAlign: 'left',  padding: '12px 16px', borderBottom: '1px solid var(--surface-elevated)' }}>Estado</th>
                        </tr>
                    </thead>
                    <tbody>
                        {routines.map((r) => (
                            <tr key={r.name} style={{ borderBottom: '1px solid #F3F4F6' }}>
                                <td style={{ padding: '14px 16px' }}>
                                    <div style={{ fontWeight: 500, color: 'var(--text-primary)' }}>{r.name}</div>
                                    <div style={{ fontSize: 12, color: 'var(--text-secondary)', marginTop: 2 }}>{r.desc}</div>
                                </td>
                                <td style={{ padding: '14px 16px', color: 'var(--text-tertiary)', fontFamily: 'var(--font-mono)', fontSize: 12 }}>{r.when}</td>
                                <td style={{ padding: '14px 16px', color: 'var(--text-tertiary)', fontFamily: 'var(--font-mono)', fontSize: 12 }}>{r.last}</td>
                                <td style={{ padding: '14px 16px' }}>
                                    <StatusPill status={r.status} dot={r.status !== 'draft'}>
                                        {r.status === 'live' ? 'Ao vivo' : r.status === 'paused' ? 'Pausada' : 'Rascunho'}
                                    </StatusPill>
                                </td>
                            </tr>
                        ))}
                    </tbody>
                </table>
            </Card>
        </div>
    );
}

/* ==================================================================
   AGENTES DE CONHECIMENTO (Nous Brain) — base de conhecimento
   ================================================================== */
function PreviewBrain({ state }) {
    const empresa = V(state, 'empresa', state.cosmetic.company);
    const sources = [
        { name: 'SAP MM / Protheus', kind: 'ERP', status: 'Conectado', items: '1,2M eventos · P2P', icon: 'database' },
        { name: 'Notas fiscais (SEFAZ)', kind: 'Fiscal', status: 'Conectado', items: '184k NF-e indexadas', icon: 'file-text' },
        { name: 'Contratos e RFQs', kind: 'Documentos', status: 'Conectado', items: '2.310 documentos', icon: 'briefcase' },
        { name: 'Catálogo de materiais', kind: 'Mestre', status: 'Conectado', items: '47k SKUs', icon: 'layers' },
        { name: 'Benchmark de mercado', kind: 'Externo', status: 'Conectado', items: 'CEPEA · Conab · B3', icon: 'trending-up' },
        { name: 'Políticas de compra', kind: 'Governança', status: 'Indexando', items: '38 documentos', icon: 'shield' },
    ];
    return (
        <div className="screen-pad nous-scroll brain-screen">
            <h2 className="screen-title">Agentes de Conhecimento</h2>
            <p className="screen-sub">
                A camada que conecta e dá contexto a todas as fontes de dados da operação.
                É daqui que o Nous tira as respostas, sempre rastreáveis até a fonte.
            </p>
            <div className="brain-stats">
                <div className="brain-stat"><div className="brain-stat-v">6</div><div className="brain-stat-l">Fontes conectadas</div></div>
                <div className="brain-stat"><div className="brain-stat-v">1,4M</div><div className="brain-stat-l">Registros indexados</div></div>
                <div className="brain-stat"><div className="brain-stat-v">100%</div><div className="brain-stat-l">Respostas rastreáveis</div></div>
                <div className="brain-stat"><div className="brain-stat-v">pt-BR</div><div className="brain-stat-l">Busca semântica</div></div>
            </div>
            <div className="ag-section-title ag-section-title--first">Fontes de conhecimento</div>
            <div className="brain-grid">
                {sources.map((s) => (
                    <div key={s.name} className="brain-card">
                        <span className="brain-card-icon"><Icon name={s.icon} size={16} /></span>
                        <div className="brain-card-info">
                            <div className="brain-card-name">{s.name}</div>
                            <div className="brain-card-meta">{s.kind} · {s.items}</div>
                        </div>
                        <StatusPill status={s.status === 'Conectado' ? 'live' : 'draft'} dot={s.status === 'Conectado'}>
                            {s.status}
                        </StatusPill>
                    </div>
                ))}
            </div>
        </div>
    );
}

/* ==================================================================
   CONSUMO & COTAS — modelo de consumo
   ================================================================== */
function PreviewConsumo({ state }) {
    const tabs = ['Meu Consumo', 'Organização', 'Global', 'Cotas'];
    const [tab, setTab] = React.useState('Meu Consumo');
    const c = state.consumo || { used: 0, quota: 50 };
    const used = c.used || 0;
    const quota = c.quota || 50;
    const remaining = Math.max(0, quota - used);
    const pct = quota > 0 ? Math.min(100, (used / quota) * 100) : 0;
    const fmtPct = pct.toFixed(pct < 10 ? 1 : 0);
    const meses = [
        { m: 'Jan', v: 18 }, { m: 'Fev', v: 22 }, { m: 'Mar', v: 31 },
        { m: 'Abr', v: 27 }, { m: 'Mai', v: 34 }, { m: 'Jun', v: used },
    ];
    const maxV = Math.max(...meses.map(x => x.v), 1);
    return (
        <div className="screen-pad nous-scroll consumo-screen">
            <h2 className="screen-title">Consumo &amp; Cotas</h2>
            <p className="screen-sub">
                Modelo de consumo do Nous: você paga por <strong>análise executada</strong>, não por
                assento. Cada pergunta respondida, agente rodado ou rotina disparada consome 1 análise da cota.
            </p>

            <div className="consumo-tabs">
                {tabs.map(t => (
                    <button key={t} className={`consumo-tab${tab === t ? ' active' : ''}`} onClick={() => setTab(t)}>{t}</button>
                ))}
            </div>

            <div className="consumo-kpis">
                <div className="consumo-kpi">
                    <div className="consumo-kpi-label"><Icon name="zap" size={12} /> ANÁLISES USADAS</div>
                    <div className="consumo-kpi-v">{used} <span className="consumo-kpi-of">/ {quota}</span></div>
                    <div className="consumo-kpi-sub">{fmtPct}% da cota mensal</div>
                </div>
                <div className="consumo-kpi">
                    <div className="consumo-kpi-label"><Icon name="bar-chart-3" size={12} /> MÉDIA DIÁRIA</div>
                    <div className="consumo-kpi-v">{(used / 6).toFixed(1)}</div>
                    <div className="consumo-kpi-sub">análises/dia</div>
                </div>
                <div className="consumo-kpi">
                    <div className="consumo-kpi-label"><Icon name="trending-up" size={12} /> RESTANTES</div>
                    <div className={`consumo-kpi-v${remaining > 0 ? ' consumo-kpi-v--ok' : ''}`} style={remaining === 0 ? { color: 'var(--danger)' } : null}>{remaining}</div>
                    <div className="consumo-kpi-sub">até 30 de jun</div>
                </div>
                <div className="consumo-kpi">
                    <div className="consumo-kpi-label"><Icon name="message-circle" size={12} /> INTERAÇÕES</div>
                    <div className="consumo-kpi-v">{used}</div>
                    <div className="consumo-kpi-sub">neste mês</div>
                </div>
            </div>

            <Card style={{ marginTop: 4 }}>
                <div className="vt-card-head">
                    <span className="vt-card-title">Progresso da Cota Mensal</span>
                    <span className="vt-card-total">{used} de {quota}</span>
                </div>
                <div className="consumo-progress"><span style={{ width: pct + '%' }} /></div>
                <div className="consumo-progress-foot">
                    <span>{used} usadas</span>
                    <span>{remaining} restantes</span>
                </div>
            </Card>

            <Card style={{ marginTop: 14 }}>
                <div className="vt-card-head">
                    <span className="vt-card-title">Evolução Mensal</span>
                    <span className="vt-card-total">análises/mês</span>
                </div>
                <div className="consumo-chart">
                    {meses.map(x => (
                        <div key={x.m} className="consumo-bar-col">
                            <span className="consumo-bar-v">{x.v}</span>
                            <span className="consumo-bar" style={{ height: (x.v / maxV * 130) + 'px' }} />
                            <span className="consumo-bar-m">{x.m}</span>
                        </div>
                    ))}
                </div>
            </Card>

            <Card style={{ marginTop: 14 }}>
                <div className="vt-card-head">
                    <span className="vt-card-title">Como funciona o modelo de consumo</span>
                </div>
                <div className="consumo-model">
                    <div className="consumo-model-row">
                        <span className="consumo-model-icon"><Icon name="message-circle" size={14} /></span>
                        <div><strong>1 análise</strong> por pergunta respondida no chat com dados do processo</div>
                    </div>
                    <div className="consumo-model-row">
                        <span className="consumo-model-icon"><Icon name="briefcase" size={14} /></span>
                        <div><strong>1 análise</strong> por execução de agente autônomo (priorizador, conciliador, etc.)</div>
                    </div>
                    <div className="consumo-model-row">
                        <span className="consumo-model-icon"><Icon name="clock" size={14} /></span>
                        <div><strong>1 análise</strong> por rotina agendada disparada</div>
                    </div>
                    <div className="consumo-model-row consumo-model-row--free">
                        <span className="consumo-model-icon"><Icon name="check-circle" size={14} /></span>
                        <div>Navegar dashboards, abrir Achados e editar planos <strong>não consome</strong> cota</div>
                    </div>
                </div>
            </Card>
        </div>
    );
}

/* ==================================================================
   Top-level preview — picks the screen + overlays hotspots
   ================================================================== */
function NousPreview({ state, onNavigate, level4Open, onOpenFullMap, onSetState }) {
    let body;
    switch (state.activeScreen) {
        case 'mining':   body = <PreviewProcessMining state={state} onOpenFullMap={onOpenFullMap} />; break;
        case 'agents':   body = React.createElement(window.PreviewAgents, { state, onSetState }); break;
        case 'achados':  body = React.createElement(window.PreviewAchados, { state, onSetState }); break;
        case 'rotinas':  body = <PreviewRotinas state={state} />; break;
        case 'roai':     body = React.createElement(window.PreviewRoAI, { state, onSetState }); break;
        case 'brain':    body = <PreviewBrain state={state} />; break;
        case 'consumo':  body = <PreviewConsumo state={state} />; break;
        case 'chat':
        default:         body = <PreviewChat    state={state} level4Open={level4Open} onSetState={onSetState} />; break;
    }

    return (
        <div className="nous-app">
            <PreviewSidebar state={state} onNavigate={onNavigate} />
            <div className="nous-main">{body}</div>
            {window.DemoBadge && <window.DemoBadge state={state} />}
            {window.DrilldownPanel && <window.DrilldownPanel state={state} onSetState={onSetState} />}
        </div>
    );
}

Object.assign(window, { NousPreview });
