/* ============================================================
   Omnitok Analytics — RetailsView (mantenedor de retails)
   Asocia hostnames del ingest a un nombre de retail + país y controla
   su visibilidad (oculto = fuera de TODOS los cálculos). Solo admin.
   Backend: worker materializer /admin/retails (misma D1); escrituras
   con la X-API-Key compartida (localStorage oa_admin_key).
   ============================================================ */
const { useState:rS, useEffect:rE, useRef:rR } = React;

function RetailsView(){
  const es = OA.lang==='es';
  const [rows,setRows]=rS([]); const [total,setTotal]=rS(0);
  const [loading,setLoading]=rS(true);
  const [filter,setFilter]=rS('pending');     // all | pending | named | hidden
  const [q,setQ]=rS(''); const [qLive,setQLive]=rS('');
  const [colF,setColF]=rS({host:'',name:'',country:''});       // filtros de columna aplicados
  const [colLive,setColLive]=rS({host:'',name:'',country:''}); // en tipeo (debounce 350ms)
  const [page,setPage]=rS(0);
  const [edits,setEdits]=rS({});              // {host:{name,country}}
  const [busy,setBusy]=rS('');                // host en guardado
  const [syncing,setSyncing]=rS(false); const [syncMsg,setSyncMsg]=rS(null);
  const [keyVal,setKeyVal]=rS(OA.adminKey());
  const [err,setErr]=rS('');
  const [names,setNames]=rS([]);              // universo de nombres existentes [{name,country}]
  // ---- Asistente IA (drafts por host, wizard, historial, impacto) ----
  const [aiDrafts,setAiDrafts]=rS({});        // host -> draft activo
  const [aiOpen,setAiOpen]=rS({});            // host -> preview expandido
  const [aiBusy,setAiBusy]=rS('');
  const [showWizard,setShowWizard]=rS(false);
  const [showHistory,setShowHistory]=rS(false);
  const [lastImpact,setLastImpact]=rS(null);
  const [sect,setSect]=rS('hosts');           // hosts | scope (Alcance del cliente, fase 1 Monitor)
  const PAGE=25;
  const seq=rR(0);

  const inp={padding:'8px 11px',border:'1px solid #E5E5E5',borderRadius:8,fontSize:13,boxSizing:'border-box'};
  const RED='#B91C1C', TEAL='#0E7490', AMBER='#B45309';
  const wErr=(e)=> setErr(e&&e.status===401 ? (es?'X-API-Key inválida o no configurada (candado del encabezado).':'Invalid or missing X-API-Key (see header).')
    : String((e&&e.message)||e));

  const load=(opts)=>{ const o=opts||{}; const c=o.cols||colF; const my=++seq.current; setLoading(true);
    OA.loadRetailsAdmin({q:(o.q!=null?o.q:q), filter:(o.filter!=null?o.filter:filter)==='all'?'':(o.filter!=null?o.filter:filter), host:c.host, name:c.name, country:c.country, sync:o.sync})
      .then(r=>{ if(my!==seq.current) return;
        setRows(r.rows||[]); setTotal(r.total||0); setLoading(false);
        if(o.sync && r.synced) setSyncMsg(es
          ? ('Detección lista: '+r.synced.hosts_ingest+' hosts activos en 30 días, '+r.synced.nuevos+' nuevos agregados como pending.')
          : ('Sync done: '+r.synced.hosts_ingest+' active hosts in 30 days, '+r.synced.nuevos+' new added as pending.'));
      })
      .catch(e=>{ if(my===seq.current){ setRows([]); setTotal(0); setLoading(false); wErr(e); } });
  };
  // universo de nombres para el selector (todas las filas, sin filtro)
  const loadNames=()=>{ OA.loadRetailsAdmin({}).then(r=>{
    const m={};
    (r.rows||[]).forEach(x=>{ if(!x.pending && !x.hidden && x.name){   // ocultos fuera del selector
      if(!m[x.name]) m[x.name]={country:x.country||''};
      else if(!m[x.name].country && x.country) m[x.name].country=x.country; }});
    setNames(Object.keys(m).sort((a,b)=>a.localeCompare(b)).map(k=>({name:k,country:m[k].country})));
  }).catch(()=>{}); };
  const loadAiDrafts=()=>{ OA.aiDrafts('status=draft,edited').then(d=>{
    const m={}; (d.rows||[]).forEach(x=>{ m[x.host]=x; }); setAiDrafts(m);
  }).catch(()=>{}); };
  rE(()=>{ loadNames(); loadAiDrafts(); },[]);
  rE(()=>{ load({page:0}); },[filter]);
  rE(()=>{ const t=setTimeout(()=>{ if(qLive!==q){ setQ(qLive); setPage(0); load({q:qLive}); } },350); return ()=>clearTimeout(t); },[qLive]);
  rE(()=>{ const t=setTimeout(()=>{ if(colLive.host!==colF.host||colLive.name!==colF.name||colLive.country!==colF.country){
    setColF(colLive); setPage(0); load({cols:colLive}); } },350); return ()=>clearTimeout(t); },[colLive]);

  const doSync=()=>{ setSyncing(true); setSyncMsg(null); setErr('');
    OA.loadRetailsAdmin({filter:filter==='all'?'':filter, host:colF.host, name:colF.name, country:colF.country, sync:true})
      .then(r=>{ setSyncing(false); setRows(r.rows||[]); setTotal(r.total||0);
        if(r.synced) setSyncMsg(es
          ? ('Detección lista: '+r.synced.hosts_ingest+' hosts con eventos en 30 días · '+r.synced.nuevos+' nuevos agregados como pending.')
          : ('Sync done: '+r.synced.hosts_ingest+' hosts with events in 30 days · '+r.synced.nuevos+' new added as pending.')); })
      .catch(e=>{ setSyncing(false); wErr(e); }); };

  const ed=(host)=> edits[host]||{};
  const setEd=(host,f,v)=> setEdits(p=>({...p,[host]:{...(p[host]||{}),[f]:v}}));
  const dirty=(r)=>{ const e=ed(r.host);
    return (e.name!=null && e.name!==(r.pending?'':(r.name||''))) || (e.country!=null && e.country!==(r.country||'')); };
  const save=(r,extra)=>{ const e=ed(r.host); setBusy(r.host); setErr('');
    const fields=Object.assign({},extra||{});
    if(e.name!=null) fields.name=e.name;
    if(e.country!=null) fields.country=e.country;
    OA.setRetail(r.host, fields)
      .then(()=>{ setBusy(''); setEdits(p=>{ const n={...p}; delete n[r.host]; return n; }); load(); loadNames(); })
      .catch(er=>{ setBusy('');
        // regla de nombres: el nombre EXACTO existe con OTRO país => 409 con
        // sugerencia de sufijos; ofrecer aplicar sufijo propio + renombrar el
        // grupo existente (rename_group en su host canónico)
        const coll=er&&er.payload&&er.payload.collision;
        if(er&&er.status===409&&coll){
          const ex=coll.existing&&coll.existing[0];
          const msg=es
            ?('"'+coll.yours.name+'" ya existe en otro país. ¿Usar "'+coll.yours.suggested+'" y renombrar el existente a "'+(ex?ex.suggested:'')+'"?')
            :('"'+coll.yours.name+'" exists in another country. Use "'+coll.yours.suggested+'" and rename the existing one to "'+(ex?ex.suggested:'')+'"?');
          if(window.confirm(msg)){
            setBusy(r.host);
            const renames = (coll.existing||[]).filter(x=>x.country&&x.suggested!==x.name)
              .map(x=>OA.setRetail(x.canonical_host,{name:x.suggested,force_name:true,rename_group:true}));
            Promise.all(renames)
              .then(()=>{ setEd(r.host,'name',coll.yours.suggested); return OA.setRetail(r.host,{name:coll.yours.suggested,country:coll.yours.country,force_name:true}); })
              .then(()=>{ setBusy(''); setEdits(p=>{ const n={...p}; delete n[r.host]; return n; }); load(); loadNames(); })
              .catch(e2=>{ setBusy(''); wErr(e2); });
          }
          return;
        }
        wErr(er); }); };
  const toggle=(r)=>{ setBusy(r.host); setErr('');
    OA.setRetail(r.host,{status: r.hidden?'active':'hidden'})
      .then(()=>{ setBusy(''); load(); })
      .catch(er=>{ setBusy(''); wErr(er); }); };

  // ---- Asistente IA: recomendación individual (draft persistente hasta aplicar) ----
  const aiRecommend=(host)=>{ setAiBusy(host); setErr('');
    OA.aiRecommend(host)
      .then(()=>{ setAiBusy(''); loadAiDrafts(); setAiOpen(p=>({...p,[host]:true})); })
      .catch(er=>{ setAiBusy(''); wErr(er); }); };
  const aiEdit=(host,f,v)=>{ const d=aiDrafts[host]; if(!d) return;
    OA.aiDraftEdit(d.id,{[f]:v}).then(res=>setAiDrafts(p=>({...p,[host]:{...p[host],...res.draft}})))
      .catch(er=>wErr(er)); };
  const aiApplyOne=(host)=>{ const d=aiDrafts[host]; if(!d) return;
    setAiBusy(host);
    OA.aiApply({draft_ids:[d.id]})
      .then(res=>{ setAiBusy(''); setAiOpen(p=>({...p,[host]:false}));
        if(res.skipped&&res.skipped.length&&res.skipped[0].reason==='conflict'){
          if(window.confirm(es?'El host fue editado a mano después del draft. ¿Forzar la recomendación IA?':'Row changed after draft. Force AI recommendation?')){
            setAiBusy(host);
            OA.aiApply({draft_ids:[d.id],force:true}).then(r2=>{ setAiBusy(''); setLastImpact(r2.impact); load(); loadNames(); loadAiDrafts(); })
              .catch(er=>{ setAiBusy(''); wErr(er); });
          }
          return;
        }
        setLastImpact(res.impact); load(); loadNames(); loadAiDrafts(); })
      .catch(er=>{ setAiBusy(''); wErr(er); }); };
  const aiReject=(host)=>{ const d=aiDrafts[host]; if(!d) return;
    OA.aiDraftEdit(d.id,{status:'rejected'}).then(()=>{ loadAiDrafts(); setAiOpen(p=>({...p,[host]:false})); })
      .catch(er=>wErr(er)); };

  const pages=Math.max(1,Math.ceil(rows.length/PAGE)); const pg=Math.min(page,pages-1);
  const slice=rows.slice(pg*PAGE,(pg+1)*PAGE);

  const chip=(v,l,ct)=>React.createElement('button',{key:v,className:filter===v?'on':'',onClick:()=>{setFilter(v);setPage(0);}},l+(ct!=null?(' ('+ct+')'):''));
  const header=React.createElement('div',{style:{display:'flex',gap:10,alignItems:'center',flexWrap:'wrap',marginBottom:12}},
    React.createElement('div',{className:'seg'},
      chip('pending',es?'Pending':'Pending'),
      chip('named',es?'Asociados':'Named'),
      chip('hidden',es?'Ocultos':'Hidden'),
      chip('all',es?'Todos':'All')),
    React.createElement('input',{placeholder:es?'Buscar hostname o nombre…':'Search hostname or name…',
      value:qLive,onChange:e=>setQLive(e.target.value),style:{...inp,minWidth:240,flex:1}}),
    React.createElement('div',{style:{display:'flex',gap:6,alignItems:'center'}},
      React.createElement('span',{title:es?'X-API-Key para editar':'X-API-Key to edit',style:{fontSize:14}},'🔑'),
      React.createElement('input',{type:'password',placeholder:'X-API-Key',value:keyVal,
        onChange:e=>setKeyVal(e.target.value),onBlur:()=>OA.setAdminKey(keyVal.trim()),
        style:{...inp,width:130,padding:'6px 9px',fontSize:12}})),
    React.createElement('button',{onClick:()=>setShowWizard(true),
      style:{padding:'8px 12px',border:'1px solid #4D4A9D',borderRadius:8,fontSize:13,fontWeight:700,background:'#4D4A9D',color:'#fff',cursor:'pointer'},
      title:es?'Recomendaciones IA en lote (drafts hasta confirmar)':'Batch AI recommendations (drafts until confirmed)'},
      es?'✨ Asistente IA':'✨ AI assistant'),
    React.createElement('button',{onClick:()=>setShowHistory(true),
      style:{padding:'8px 12px',border:'1px solid #E5E5E5',borderRadius:8,fontSize:13,fontWeight:600,background:'#fff',color:'#525252',cursor:'pointer'},
      title:es?'Auditoría de cambios con revert por host y por lote':'Change audit with revert'},
      '🕘 '+(es?'Historial':'History')),
    React.createElement('button',{onClick:doSync,disabled:syncing,
      style:{padding:'8px 12px',border:'1px solid #E5E5E5',borderRadius:8,fontSize:13,fontWeight:600,background:syncing?'#F5F5F5':'#fff',color:'#525252',cursor:syncing?'default':'pointer'},
      title:es?'Escanea el ingest (30 días) y agrega hostnames nuevos como pending':'Scan ingest (30 days) and add new hostnames as pending'},
      syncing?(es?'Detectando…':'Scanning…'):(es?'⟳ Detectar hosts nuevos':'⟳ Detect new hosts')));

  const note=React.createElement('div',{className:'note-bar',style:{marginBottom:12}},React.createElement(Icon.info,null),
    React.createElement('span',null, es
      ? 'Asocia cada hostname del ingest a su retail (nombre + país del retail) o márcalo oculto (staging/bots: sale de TODOS los cálculos). El país de las visitas no se toca: siempre es el del visitante. Los cambios aplican al histórico y al futuro; las vistas en vivo los toman al instante y los rollups de 30 días en la próxima materialización (≤1h).'
      : 'Associate each ingest hostname to its retail (name + retail country) or mark it hidden (staging/bots: excluded from ALL metrics). Visit country is untouched (always the visitor\'s). Changes apply to history and future; live views pick them up instantly, 30d rollups on next materialization (≤1h).'));

  const errBar= err && React.createElement('div',{style:{padding:'8px 12px',background:'#FEF2F2',color:RED,borderRadius:8,fontSize:13,marginBottom:10}},err,
    React.createElement('button',{onClick:()=>setErr(''),style:{marginLeft:10,border:'none',background:'none',color:RED,cursor:'pointer',fontWeight:700}},'×'));
  const syncBar= syncMsg && React.createElement('div',{style:{padding:'8px 12px',background:'#F0FDF4',color:'#15803D',borderRadius:8,fontSize:13,marginBottom:10}},syncMsg,
    React.createElement('button',{onClick:()=>setSyncMsg(null),style:{marginLeft:10,border:'none',background:'none',color:'#15803D',cursor:'pointer',fontWeight:700}},'×'));

  const table=React.createElement('table',{className:'tbl'},
    React.createElement('thead',null,
      React.createElement('tr',null,
        ['Hostname',es?'Nombre del retail':'Retail name',es?'País retail':'Retail country',es?'Eventos 30d':'Events 30d',es?'Estado':'State',''].map((h,i)=>
          React.createElement('th',{key:i,className:i===3?'num':''},h))),
      // fila de filtros por columna (server-side, AND con la búsqueda global y los chips)
      React.createElement('tr',null,
        ['host','name','country'].map(k=>React.createElement('th',{key:k},
          React.createElement('input',{value:colLive[k],placeholder:'⌕',
            title:es?'Filtrar por columna':'Filter column',
            onChange:e=>{ const v=e.target.value; setColLive(p=>({...p,[k]:v})); },
            style:{...inp,width:k==='country'?64:'100%',fontWeight:400}}))),
        React.createElement('th',null),React.createElement('th',null),React.createElement('th',null))),
    React.createElement('tbody',null,
      slice.map(r=>{ const e=ed(r.host); const isBusy=busy===r.host;
        const nameVal=e.name!=null?e.name:(r.pending?'':(r.name||''));
        const coVal=e.country!=null?e.country:(r.country||'');
        const dAi=aiDrafts[r.host];
        const mainTr=React.createElement('tr',{key:'r',style:r.hidden?{opacity:0.55}:null},
          React.createElement('td',null,React.createElement('span',{className:'mono',style:{fontSize:12.5}},r.host)),
          React.createElement('td',null,
            // buscador con autocomplete (datalist compartido); texto libre = crear
            // un retail nuevo. Matchear un nombre existente canonicaliza el casing
            // (evita duplicados) y autocompleta el país si está vacío.
            (()=>{ const tv=nameVal.trim(); const isNew=!!tv && !names.some(n=>n.name.toLowerCase()===tv.toLowerCase());
              return React.createElement('span',{style:{display:'inline-flex',flexDirection:'column'}},
                React.createElement('input',{list:'oa-retail-names',value:nameVal,
                  placeholder:es?'— pending — escribe o elige':'— pending — type or pick',
                  onChange:ev=>{ const v=ev.target.value;
                    const nm=names.find(n=>n.name.toLowerCase()===v.trim().toLowerCase());
                    setEd(r.host,'name',nm?nm.name:v);
                    if(nm && nm.country && !(e.country!=null?e.country:(r.country||''))) setEd(r.host,'country',nm.country); },
                  style:{...inp,width:170,padding:'5px 8px',fontSize:12.5,color:nameVal?'#262626':AMBER,
                    borderColor:isNew?AMBER:'#E5E5E5'}}),
                isNew&&React.createElement('span',{style:{fontSize:10.5,color:AMBER,fontWeight:700,marginTop:2}},
                  es?'➕ se creará un retail nuevo':'➕ a new retail will be created'));
            })()),
          // país SIEMPRE de catálogo ISO (regla 2026-07-30): nunca texto libre
          React.createElement('td',null,React.createElement(window.AiCountry,{value:coVal||null,width:126,
            onPick:c=>setEd(r.host,'country',c||'')})),
          React.createElement('td',{className:'num'},OA.fmt(r.events_count||0)),
          React.createElement('td',null,React.createElement('span',{style:{fontSize:11.5,fontWeight:800,color:r.hidden?'#9CA3AF':(r.pending?AMBER:TEAL)}},
            r.hidden?(es?'oculto':'hidden'):(r.pending?'pending':(es?'asociado':'named')))),
          React.createElement('td',{style:{whiteSpace:'nowrap'}},
            dAi&&React.createElement('button',{onClick:()=>setAiOpen(p=>({...p,[r.host]:!p[r.host]})),
              title:es?'Ver recomendación IA pendiente':'View pending AI draft',
              style:{padding:'2px 8px',border:'none',background:'#EDE9FE',color:'#7C3AED',borderRadius:99,fontSize:10.5,fontWeight:800,cursor:'pointer',marginRight:6}},
              'IA '+Math.round((dAi.confidence||0)*100)+'%'),
            React.createElement('button',{disabled:aiBusy===r.host,onClick:()=>aiRecommend(r.host),
              title:es?'Recomendar con IA (queda como draft hasta aplicar)':'AI recommendation (draft until applied)',
              style:{padding:'4px 9px',border:'1px solid #E5E5E5',background:'#fff',borderRadius:7,fontSize:12,cursor:'pointer',marginRight:6}},
              aiBusy===r.host?'…':'✨'),
            dirty(r)&&React.createElement('button',{disabled:isBusy,onClick:()=>save(r),
              style:{padding:'4px 10px',border:'1px solid '+TEAL,color:'#fff',background:TEAL,borderRadius:7,fontSize:12,fontWeight:700,cursor:'pointer',marginRight:6}},
              isBusy?'…':(es?'Guardar':'Save')),
            React.createElement('button',{disabled:isBusy,onClick:()=>toggle(r),
              title:r.hidden?(es?'Volver a incluir en las métricas':'Include in metrics again'):(es?'Excluir de TODAS las métricas (staging/bot)':'Exclude from ALL metrics (staging/bot)'),
              style:{padding:'4px 10px',border:'1px solid #E5E5E5',color:'#525252',background:'#fff',borderRadius:7,fontSize:12,fontWeight:600,cursor:'pointer'}},
              r.hidden?(es?'Mostrar':'Show'):(es?'Ocultar':'Hide'))));
        // fila expandida del draft IA (preview editable: nada se aplica sin confirmar)
        let aiTr=null;
        if(dAi && aiOpen[r.host]){
          const showFields=dAi.action==='assign'||dAi.action==='create'||dAi.action==='change';
          const existsInUni=(nm)=>!!nm&&names.some(n=>n.name.toLowerCase()===String(nm).trim().toLowerCase());
          aiTr=React.createElement('tr',{key:'ai'},
            React.createElement('td',{colSpan:6,style:{background:'#F8F6FF',borderBottom:'1px solid #E4DEFA'}},
              React.createElement('div',{style:{display:'flex',gap:12,alignItems:'center',flexWrap:'wrap',padding:'4px 2px'}},
                // acción SIEMPRE editable: un 'ocultar'/'sin acción' puede pasar
                // a asociar/crear con retail y país manuales
                React.createElement('select',{value:dAi.action,
                  style:{...inp,padding:'4px 6px',fontSize:11.5,fontWeight:700},
                  onChange:ev=>aiEdit(r.host,'action',ev.target.value)},
                  [['assign',es?'asociar':'assign'],['create',es?'crear retail':'create'],['change',es?'cambiar':'change'],
                   ['hide',es?'ocultar':'hide'],['none',es?'sin acción':'none']].map(a=>
                    React.createElement('option',{key:a[0],value:a[0]},a[1]))),
                React.createElement(window.AiConf,{c:dAi.confidence||0}),
                showFields&&React.createElement('span',{style:{display:'inline-flex',alignItems:'center'}},
                  React.createElement('input',{list:'oa-retail-names',defaultValue:dAi.name||'',
                    placeholder:es?'elige o escribe retail…':'pick or type retail…',
                    style:{...inp,width:180,padding:'5px 8px',fontSize:12.5},
                    onBlur:ev=>{ if(ev.target.value!==(dAi.name||'')) aiEdit(r.host,'name',ev.target.value); }}),
                  dAi.name?React.createElement('span',{style:{fontSize:10.5,fontWeight:800,marginLeft:5,whiteSpace:'nowrap',
                    color:existsInUni(dAi.name)?TEAL:AMBER}},
                    existsInUni(dAi.name)?('✓ '+(es?'EXISTENTE':'EXISTING')):('➕ '+(es?'NUEVO':'NEW'))):null),
                showFields&&React.createElement(window.AiCountry,{value:dAi.country,width:126,
                  onPick:c=>aiEdit(r.host,'country',c)}),
                React.createElement('span',{style:{fontSize:11.5,color:'#737373'}},
                  (es?'actual: ':'current: '),React.createElement('b',{style:{color:'#262626'}},(r.pending?'pending':(r.name||''))+(r.country?(' · '+r.country):'')),
                  ' → ',(es?'propuesto: ':'proposed: '),
                  React.createElement('b',{style:{color:'#262626'}},dAi.action==='hide'?(es?'OCULTAR':'HIDE'):(dAi.action==='none'?(es?'sin cambio':'no change'):((dAi.name||'')+' · '+(dAi.country||'—'))))),
                dAi.status==='edited'&&React.createElement('span',{style:{fontSize:10,fontWeight:800,color:'#854D0E',background:'#FEF9C3',borderRadius:99,padding:'1px 7px'}},es?'editado':'edited'),
                React.createElement('span',{style:{flex:1}}),
                dAi.action!=='none'&&React.createElement('button',{disabled:aiBusy===r.host,onClick:()=>aiApplyOne(r.host),
                  style:{padding:'4px 12px',border:'1px solid '+TEAL,background:TEAL,color:'#fff',borderRadius:7,fontSize:12,fontWeight:700,cursor:'pointer'}},
                  aiBusy===r.host?'…':(es?'Aplicar':'Apply')),
                React.createElement('button',{onClick:()=>aiReject(r.host),
                  style:{padding:'4px 10px',border:'1px solid #E5E5E5',background:'#fff',borderRadius:7,fontSize:12,cursor:'pointer'}},es?'Rechazar':'Reject'),
                React.createElement('button',{onClick:()=>setAiOpen(p=>({...p,[r.host]:false})),
                  style:{border:'none',background:'none',fontWeight:800,cursor:'pointer',color:'#737373'}},'×')),
              React.createElement('div',{style:{fontSize:11.5,color:'#5B5470',margin:'2px 2px 5px'}},
                '🧠 '+(dAi.reasoning||'')+(dAi.model?(' · '+dAi.model):''))));
        }
        return React.createElement(React.Fragment,{key:r.host}, mainTr, aiTr);
      })));

  const pager= pages>1 && React.createElement('div',{style:{display:'flex',gap:8,alignItems:'center',justifyContent:'center',marginTop:10,fontSize:12,color:'#737373'}},
    React.createElement('button',{disabled:pg<=0,onClick:()=>setPage(pg-1),style:{padding:'3px 9px',border:'1px solid #E5E5E5',borderRadius:6,background:pg<=0?'#F5F5F5':'#fff',cursor:pg<=0?'default':'pointer'}},'‹'),
    React.createElement('span',null,(pg+1)+' / '+pages+' · '+rows.length+(es?' hosts':' hosts')),
    React.createElement('button',{disabled:pg>=pages-1,onClick:()=>setPage(pg+1),style:{padding:'3px 9px',border:'1px solid #E5E5E5',borderRadius:6,background:pg>=pages-1?'#F5F5F5':'#fff',cursor:pg>=pages-1?'default':'pointer'}},'›'));

  // datalists compartidos: nombres del universo + catálogo de países ISO
  const nameList=React.createElement('datalist',{id:'oa-retail-names'},
    names.map(n=>React.createElement('option',{key:n.name,value:n.name})));
  const countryList=React.createElement('datalist',{id:'oa-country-list'},
    OA.COUNTRIES.map(c=>React.createElement('option',{key:c[0],value:c[0]+' — '+c[1]})));

  const impactBar= lastImpact && React.createElement('div',{style:{marginBottom:12}},
    React.createElement(window.AiImpact,{impact:lastImpact,onDone:()=>setLastImpact(null)}));

  // switch de sección: Hostnames (mantenedor clásico) | Alcance del cliente (fase 1 Monitor)
  const sectSeg=React.createElement('div',{className:'seg',style:{marginBottom:12}},
    React.createElement('button',{className:sect==='hosts'?'on':'',onClick:()=>setSect('hosts')},es?'Hostnames':'Hostnames'),
    React.createElement('button',{className:sect==='scope'?'on':'',onClick:()=>setSect('scope')},es?'Alcance del cliente':'Client scope'));

  if(sect==='scope') return React.createElement('div',null, sectSeg, errBar,
    React.createElement(window.ClientScopeSection,{onErr:wErr}));

  return React.createElement('div',null, sectSeg, header, note, errBar, syncBar, impactBar, nameList, countryList,
    React.createElement(Card,{pad:false},
      loading ? React.createElement('div',{className:'empty'},React.createElement('div',{className:'spinner'}),(es?'Cargando…':'Loading…'))
      : rows.length===0 ? React.createElement('div',{className:'empty'},es?'Sin resultados':'No results')
      : table),
    pager,
    showWizard&&React.createElement(window.RetailsAIWizard,{
      names:names,
      onClose:()=>{ setShowWizard(false); load(); loadNames(); loadAiDrafts(); },
      onApplied:()=>{ load(); loadNames(); loadAiDrafts(); }}),
    showHistory&&React.createElement(window.RetailsAIHistory,{
      onClose:()=>setShowHistory(false),
      onChanged:()=>{ load(); loadNames(); loadAiDrafts(); }}));
}
window.RetailsView=RetailsView;
