// app.jsx — Main application: state management, table, header, layout
const { useState, useEffect, useCallback, useRef, useMemo } = React;

/* ═══════════════════ CONSTANTS ═══════════════════ */
const MAX_LEVELS = 4;
const PAGE_SIZE = 20;
const STORAGE_KEY_SAVED = 'famalegis_saved_searches';
const STORAGE_KEY_RECENT = 'famalegis_recent';

/* ═══════════════════ PERSISTENCE ═══════════════════ */
function loadLS(key, fallback) { try { return JSON.parse(localStorage.getItem(key)) || fallback; } catch { return fallback; } }
function saveLS(key, val) { try { localStorage.setItem(key, JSON.stringify(val)); } catch {} }

/* ═══════════════════ URL HASH STATE ═══════════════════ */
function parseHash() {
  const hash = window.location.hash.replace(/^#/, '');
  if (!hash) return null;
  const params = new URLSearchParams(hash);
  const terms = params.get('q')?.split(',').filter(Boolean) || [];
  const filters = {
    ano: params.get('ano')?.split(',').filter(Boolean).map(Number) || [],
    tipo: params.get('tipo')?.split(',').filter(Boolean) || [],
    sigla: params.get('sigla')?.split(',').filter(Boolean) || [],
    situacao: params.get('situacao')?.split(',').filter(Boolean) || []
  };
  return { terms, filters };
}

/* ═══════════════════ RESULTS TABLE ═══════════════════ */
function ResultsTable({ results, loading, total, page, totalPages, onPageChange, sortBy, sortDir, onSort, selectedId, onSelect, isMobile }) {
  const columns = [
    { key:'sigla', label:'Sigla', w:90, mono:true, render: r => `${r.sigla} ${r.numero}` },
    { key:'ementa', label:'Ementa', w:null, render: r => r.ementa },
    { key:'ano', label:'Ano', w:52, mono:true, render: r => r.ano },
    { key:'situacao', label:'Situação', w:200, render: r => r.situacao }
  ];

  const tS = {
    wrap: { flex:1, display:'flex', flexDirection:'column', overflow:'hidden' },
    header: {
      display:'flex', alignItems:'center', borderBottom:'1px solid var(--border-0)',
      background:'var(--bg-2)', position:'sticky', top:0, zIndex:2
    },
    th: (w) => ({
      padding:'var(--sp-2) var(--sp-3)', fontSize:'0.77rem', fontWeight:600,
      color:'var(--text-2)', cursor:'pointer', userSelect:'none',
      width: w || undefined, flex: w ? undefined : 1, display:'flex', alignItems:'center', gap:4,
      whiteSpace:'nowrap', borderRight:'1px solid var(--border-1)'
    }),
    body: { flex:1, overflowY:'auto' },
    row: (isSelected) => ({
      display:'flex', alignItems:'stretch', borderBottom:'1px solid var(--border-1)',
      cursor:'pointer', transition:'background var(--tr-fast)',
      background: isSelected ? 'var(--row-selected)' : 'transparent'
    }),
    td: (w, mono) => ({
      padding:'var(--cell-py, 6px) var(--sp-3)', fontSize:'0.846rem',
      color:'var(--text-1)', width: w || undefined, flex: w ? undefined : 1,
      fontFamily: mono ? 'var(--font-mono)' : 'inherit',
      overflow:'hidden', textOverflow:'ellipsis',
      display:'-webkit-box', WebkitLineClamp:2, WebkitBoxOrient:'vertical',
      lineHeight:1.45, borderRight:'1px solid var(--border-1)',
      wordBreak: 'break-word'
    }),
    footer: {
      display:'flex', alignItems:'center', justifyContent:'space-between',
      padding:'var(--sp-3) var(--sp-4)', borderTop:'1px solid var(--border-0)',
      background:'var(--bg-2)', fontSize:'0.923rem', color:'var(--text-2)', flexShrink:0,
      position:'relative'
    },
    pageBtn: (active) => ({
      padding:'5px 11px', cursor:'pointer', borderRadius:'var(--radius-sm)',
      background: active ? 'var(--accent)' : 'transparent',
      color: active ? '#fff' : 'var(--text-2)', fontFamily:'var(--font-mono)',
      fontWeight: active ? 600 : 400, fontSize:'0.923rem', border:'none', minWidth:28
    })
  };

  const pageNums = useMemo(() => {
    const pages = [];
    const start = Math.max(1, page - 2);
    const end = Math.min(totalPages, page + 2);
    if (start > 1) { pages.push(1); if (start > 2) pages.push('...'); }
    for (let i = start; i <= end; i++) pages.push(i);
    if (end < totalPages) { if (end < totalPages - 1) pages.push('...'); pages.push(totalPages); }
    return pages;
  }, [page, totalPages]);

  return (
    <div style={tS.wrap}>
      {/* Mobile cards layout */}
      {isMobile ? (
        <div style={{flex:1, overflowY:'auto', background:'var(--bg-0)'}}>
          {loading ? (
            <div style={{padding:'var(--sp-3)'}}>
              {Array.from({length:5}).map((_,i) => (
                <div key={i} className="skeleton" style={{height:96, marginBottom:'var(--sp-2)', borderRadius:'var(--radius)'}}/>
              ))}
            </div>
          ) : results.length === 0 ? (
            <EmptyState message="Nenhuma proposição encontrada" sub="Tente outros termos ou ajuste os filtros"/>
          ) : (
            <div style={{padding:'var(--sp-2) var(--sp-3)', display:'flex', flexDirection:'column', gap:'var(--sp-2)'}}>
              {results.map(r => (
                <div key={r.id}
                  style={{
                    background: selectedId === r.id ? 'var(--row-selected)' : 'var(--bg-1)',
                    border:'1px solid ' + (selectedId === r.id ? 'var(--accent-muted)' : 'var(--border-1)'),
                    borderRadius:'var(--radius)',
                    padding:'var(--sp-3)', cursor:'pointer',
                    display:'flex', flexDirection:'column', gap:'var(--sp-2)'
                  }}
                  onClick={() => onSelect(r)}>
                  <div style={{display:'flex', alignItems:'center', justifyContent:'space-between', gap:'var(--sp-2)'}}>
                    <span style={{display:'flex', alignItems:'baseline', gap:6}}>
                      <span className="badge badge-accent" style={{fontSize:'0.77rem'}}>{r.sigla}</span>
                      <span style={{fontFamily:'var(--font-mono)', fontWeight:600, color:'var(--text-0)', fontSize:'0.923rem'}}>{r.numero}/{r.ano}</span>
                    </span>
                    <span style={{fontSize:'0.7rem', color:'var(--text-3)', fontFamily:'var(--font-mono)'}}>{r.id}</span>
                  </div>
                  <div style={{
                    fontSize:'0.846rem', color:'var(--text-1)', lineHeight:1.45,
                    display:'-webkit-box', WebkitLineClamp:3, WebkitBoxOrient:'vertical', overflow:'hidden'
                  }}>{r.ementa}</div>
                  <div style={{display:'flex', alignItems:'center', gap:'var(--sp-2)', flexWrap:'wrap'}}>
                    {r.situacao && <span style={{fontSize:'0.77rem', color:'var(--text-2)', background:'var(--bg-2)', padding:'1px 6px', borderRadius:'var(--radius-sm)'}}>{r.situacao}</span>}
                    {r.orgao && <span style={{fontSize:'0.7rem', color:'var(--text-3)', fontFamily:'var(--font-mono)'}}>{r.orgao}</span>}
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      ) : (
        <>
          {/* Column headers */}
          <div style={tS.header}>
            {columns.map(col => (
              <div key={col.key} style={tS.th(col.w)} onClick={() => onSort(col.key)}>
                {col.label}
                {sortBy === col.key && <IcoSort dir={sortDir} size={10}/>}
              </div>
            ))}
          </div>
          {/* Body */}
          <div style={tS.body}>
            {loading ? <SkeletonRows count={10}/> :
             results.length === 0 ? <EmptyState message="Nenhuma proposição encontrada" sub="Tente outros termos ou ajuste os filtros"/> :
             results.map((r) => (
              <div key={r.id} style={tS.row(selectedId === r.id)}
                onClick={() => onSelect(r)}
                onMouseEnter={e => { if (selectedId !== r.id) e.currentTarget.style.background='var(--row-hover)'; }}
                onMouseLeave={e => { if (selectedId !== r.id) e.currentTarget.style.background='transparent'; }}>
                {columns.map(col => (
                  <div key={col.key} style={tS.td(col.w, col.mono)} title={col.key==='ementa'? r.ementa : undefined}>
                    {col.render(r)}
                  </div>
                ))}
              </div>
            ))}
          </div>
        </>
      )}
      {/* Pagination */}
      {totalPages > 1 && (
        isMobile ? (
          <div style={{...tS.footer, justifyContent:'center', flexDirection:'column', gap:6, padding:'var(--sp-3) var(--sp-3)'}}>
            <div style={{display:'flex', gap:4, alignItems:'center'}}>
              <button style={tS.pageBtn(false)} onClick={() => page > 1 && onPageChange(page-1)} disabled={page<=1}>‹</button>
              {pageNums.map((p, i) => p === '...' ? <span key={`e${i}`} style={{color:'var(--text-3)', padding:'0 6px'}}>…</span> :
                <button key={p} style={tS.pageBtn(p === page)} onClick={() => onPageChange(p)}>{p}</button>
              )}
              <button style={tS.pageBtn(false)} onClick={() => page < totalPages && onPageChange(page+1)} disabled={page>=totalPages}>›</button>
            </div>
            <span style={{fontSize:'0.77rem', color:'var(--text-3)'}}>{fmtNum(total)} resultados · Página {page} de {fmtNum(totalPages)}</span>
          </div>
        ) : (
          <div style={tS.footer}>
            <span style={{minWidth:0, flex:'0 0 auto'}}>{fmtNum(total)} resultados · Página {page} de {fmtNum(totalPages)}</span>
            <div style={{display:'flex', gap:4, alignItems:'center', position:'absolute', left:'50%', transform:'translateX(-50%)'}}>
              <button style={tS.pageBtn(false)} onClick={() => page > 1 && onPageChange(page-1)} disabled={page<=1}>‹</button>
              {pageNums.map((p, i) => p === '...' ? <span key={`e${i}`} style={{color:'var(--text-3)', padding:'0 4px'}}>…</span> :
                <button key={p} style={tS.pageBtn(p === page)} onClick={() => onPageChange(p)}>{p}</button>
              )}
              <button style={tS.pageBtn(false)} onClick={() => page < totalPages && onPageChange(page+1)} disabled={page>=totalPages}>›</button>
            </div>
            <span style={{flex:'0 0 auto'}}/>
          </div>
        )
      )}
    </div>
  );
}

/* ═══════════════════ SHARE BUTTON ═══════════════════ */
function ShareButton({ levels, filters, compact }) {
  const [copied, setCopied] = useState(false);
  const [fallbackUrl, setFallbackUrl] = useState(null);
  const fallbackInputRef = useRef(null);

  const buildUrl = () => {
    const params = new URLSearchParams();
    if (levels.length > 0) params.set('q', levels.map(l => l.termo).join(','));
    if (filters.ano?.length) params.set('ano', filters.ano.join(','));
    if (filters.tipo?.length) params.set('tipo', filters.tipo.join(','));
    if (filters.sigla?.length) params.set('sigla', filters.sigla.join(','));
    if (filters.situacao?.length) params.set('situacao', filters.situacao.join(','));
    const base = window.location.origin + window.location.pathname;
    return `${base}#${params.toString()}`;
  };

  const onClick = async () => {
    const url = buildUrl();
    try {
      await navigator.clipboard.writeText(url);
      window.location.hash = url.split('#')[1] || '';
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch (e) {
      // Clipboard API unavailable (insecure context, permissions, etc).
      // Show the URL in a modal so the user can copy manually.
      setFallbackUrl(url);
    }
  };

  // Auto-select the URL when the fallback modal opens
  useEffect(() => {
    if (fallbackUrl && fallbackInputRef.current) {
      const t = setTimeout(() => {
        fallbackInputRef.current && fallbackInputRef.current.focus();
        fallbackInputRef.current && fallbackInputRef.current.select();
      }, 40);
      return () => clearTimeout(t);
    }
  }, [fallbackUrl]);

  return (
    <div style={{position:'relative'}}>
      <button className={`btn ${compact ? 'btn-sm' : ''}`} onClick={onClick} title="Copiar link compartilhável">
        <IcoShare size={compact ? 11 : 13}/> {compact ? '' : 'Compartilhar'}
      </button>
      {copied && (
        <div style={{
          position:'absolute', right:0, top:'calc(100% + 4px)', zIndex:20,
          background:'var(--text-0)', color:'var(--bg-1)', padding:'4px 10px',
          borderRadius:'var(--radius)', fontSize:'0.77rem', fontWeight:500,
          whiteSpace:'nowrap', boxShadow:'0 4px 12px rgba(0,0,0,.15)',
          display:'flex', alignItems:'center', gap:5
        }}>
          <IcoCheck size={10}/> Link copiado
        </div>
      )}
      <Modal open={!!fallbackUrl} title="Copie o link manualmente" onClose={() => setFallbackUrl(null)}
        footer={<button className="btn btn-primary" onClick={() => setFallbackUrl(null)}>Fechar</button>}>
        <div style={{fontSize:'0.923rem', color:'var(--text-2)', marginBottom:'var(--sp-3)', lineHeight:1.5}}>
          Não consegui copiar automaticamente (a API do navegador bloqueou). Selecione e copie o link abaixo:
        </div>
        <input
          ref={fallbackInputRef}
          className="input"
          readOnly
          value={fallbackUrl || ''}
          onClick={e => e.target.select()}
          style={{width:'100%', padding:'10px 12px', fontFamily:'var(--font-mono)', fontSize:'0.846rem'}}
        />
      </Modal>
    </div>
  );
}

/* ═══════════════════ EXPORT MENU ═══════════════════ */
function ExportMenu({ onExport, compact }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, []);
  const formats = [
    { key:'csv', label:'CSV', sub:'.csv' },
    { key:'xlsx', label:'Excel', sub:'.xls' },
    { key:'json', label:'JSON', sub:'.json' }
  ];
  const itemS = {
    display:'flex', alignItems:'center', justifyContent:'space-between',
    padding:'5px 10px', cursor:'pointer', fontSize:'0.846rem',
    color:'var(--text-1)', borderRadius:'var(--radius-sm)'
  };
  return (
    <div ref={ref} style={{position:'relative'}}>
      <button className={`btn ${compact ? 'btn-sm' : ''}`} onClick={() => setOpen(o => !o)}>
        <IcoExport size={compact ? 11 : 13}/> {compact ? '' : 'Exportar'} <IcoChevDown size={compact ? 9 : 11} style={{marginLeft:2,opacity:0.7}}/>
      </button>
      {open && (
        <div style={{position:'absolute', right:0, top:'100%', marginTop:4, zIndex:20,
          background:'var(--bg-1)', border:'1px solid var(--border-0)',
          borderRadius:'var(--radius-md)', boxShadow:'0 6px 18px rgba(0,0,0,.12)',
          padding:'var(--sp-1)', minWidth:140}}>
          {formats.map(f => (
            <div key={f.key} style={itemS} onClick={() => { onExport(f.key); setOpen(false); }}
              onMouseEnter={e => e.currentTarget.style.background='var(--bg-hover)'}
              onMouseLeave={e => e.currentTarget.style.background='transparent'}>
              <span style={{fontWeight:500}}>{f.label}</span>
              <span style={{fontFamily:'var(--font-mono)', fontSize:'0.77rem', color:'var(--text-3)'}}>{f.sub}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

/* ═══════════════════ HEADER ═══════════════════ */
function AppHeader({ theme, onToggleTheme, levels, filters, onExport, isMobile, onShowFilters }) {
  const headerS = {
    wrap: {
      height:'var(--header-h)', display:'flex', alignItems:'center', justifyContent:'space-between',
      padding: isMobile ? '0 var(--sp-3)' : '0 var(--sp-4)', background:'var(--bg-1)', borderBottom:'1px solid var(--border-0)',
      flexShrink:0
    },
    left: { display:'flex', alignItems:'center', gap: isMobile ? 'var(--sp-3)' : 'var(--sp-4)' },
    title: { fontWeight:700, fontSize: isMobile ? '0.923rem' : '1.23rem', color:'var(--text-0)', letterSpacing:'-0.01em' },
    subtitle: { fontSize: isMobile ? '0.77rem' : '1rem', color:'var(--text-3)', fontWeight:400 },
    right: { display:'flex', alignItems:'center', gap: isMobile ? 'var(--sp-1)' : 'var(--sp-3)' }
  };

  return (
    <div style={headerS.wrap}>
      <div style={headerS.left}>
        <span style={headerS.title}>Proposições</span>
        {!isMobile && <span style={headerS.subtitle}>Câmara dos Deputados</span>}
        {isMobile && levels.length > 0 && (
          <button className="btn btn-ghost btn-sm" onClick={onShowFilters}
            style={{padding:'4px 8px', fontSize:'0.77rem'}}>
            <IcoFilter size={11}/> Filtros
          </button>
        )}
      </div>
      <div style={headerS.right}>
        {levels.length > 0 && <ShareButton levels={levels} filters={filters} compact={isMobile}/>}
        {levels.length > 0 && <ExportMenu onExport={onExport} compact={isMobile}/>}
        <button className={`btn btn-ghost ${isMobile ? 'btn-sm' : ''}`} onClick={onToggleTheme} title="Alternar tema"
          style={{fontFamily:'var(--font-mono)', fontSize: isMobile ? '0.77rem' : '1rem', minWidth: isMobile ? 24 : 34}}>
          {theme === 'dark' ? '☀' : '☾'}
        </button>
      </div>
    </div>
  );
}

/* ═══════════════════ WELCOME SCREEN ═══════════════════ */
function WelcomeScreen({ onSearch }) {
  const [val, setVal] = useState('');
  const inputRef = useRef(null);
  useEffect(() => { if (inputRef.current) inputRef.current.focus(); }, []);

  const suggestions = ['cannabis medicinal','fitoterápico','suplemento alimentar','ANVISA','medicamento genérico','farmácia magistral'];
  const isDesktop = typeof window !== 'undefined' && window.innerWidth >= 1024;

  const wS = {
    wrap: { flex:1, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', padding:'var(--sp-10)', gap: isDesktop ? 'var(--sp-8)' : 'var(--sp-6)' },
    count: { fontFamily:'var(--font-mono)', fontSize: isDesktop ? '4.5rem' : '2.4rem', fontWeight:700, color:'var(--text-0)', letterSpacing:'-0.03em', lineHeight:1 },
    label: { fontSize: isDesktop ? '1.385rem' : '1.077rem', color:'var(--text-2)' },
    inputWrap: { display:'flex', gap:'var(--sp-3)', width:'100%', maxWidth: isDesktop ? 680 : 480 },
    input: { flex:1, padding: isDesktop ? '14px 18px' : '8px 12px', fontSize: isDesktop ? '1.23rem' : '1rem' },
    btn: isDesktop ? { padding:'14px 26px', fontSize:'1.077rem' } : undefined,
    suggestWrap: { display:'flex', flexWrap:'wrap', gap:'var(--sp-2)', justifyContent:'center', maxWidth: isDesktop ? 720 : 500 },
    suggest: { padding: isDesktop ? '7px 14px' : '3px 10px', background:'var(--bg-2)', border:'1px solid var(--border-0)', borderRadius:'var(--radius)', fontSize: isDesktop ? '1rem' : '0.846rem', color:'var(--text-1)', cursor:'pointer', transition:'all var(--tr-fast)' }
  };

  return (
    <div style={wS.wrap}>
      <div style={wS.count}>{fmtNum(MockAPI.BASE_TOTAL)}</div>
      <div style={wS.label}>proposições indexadas</div>
      <div style={wS.inputWrap}>
        <input ref={inputRef} className="input" value={val} onChange={e=>setVal(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter' && val.trim()) onSearch(val.trim()); }}
          placeholder="Buscar por termo para iniciar exploração..."
          style={wS.input}/>
        <button className="btn btn-primary" style={wS.btn}
          onClick={() => val.trim() && onSearch(val.trim())} disabled={!val.trim()}>Buscar</button>
      </div>
      <div style={wS.suggestWrap}>
        {suggestions.map(s => (
          <span key={s} style={wS.suggest} onClick={() => onSearch(s)}
            onMouseEnter={e => { e.currentTarget.style.background='var(--accent-subtle)'; e.currentTarget.style.borderColor='var(--accent-muted)'; }}
            onMouseLeave={e => { e.currentTarget.style.background='var(--bg-2)'; e.currentTarget.style.borderColor='var(--border-0)'; }}>
            {s}
          </span>
        ))}
      </div>
    </div>
  );
}

/* ═══════════════════ ACTION BAR ═══════════════════ */
function ActionBar({ total, loading, levels, activeLevel, analyticsVisible, onToggleAnalytics }) {
  const abS = {
    wrap: {
      display:'flex', alignItems:'center', justifyContent:'space-between',
      padding:'var(--sp-3) var(--sp-4)', borderBottom:'1px solid var(--border-1)',
      background:'var(--bg-2)', flexShrink:0, gap:'var(--sp-3)'
    },
    left: { display:'flex', alignItems:'center', gap:'var(--sp-3)', fontSize:'1.077rem', color:'var(--text-1)', flex:1, minWidth:0 },
    right: { display:'flex', alignItems:'center', gap:'var(--sp-2)', flexShrink:0 }
  };

  return (
    <div style={abS.wrap}>
      <div style={abS.left}>
        <span style={{fontFamily:'var(--font-mono)', fontWeight:700, color:'var(--text-0)', fontSize:'1.23rem', letterSpacing:'-0.01em'}}>
          {loading ? '...' : fmtNum(total)}
        </span>
        <span>resultados</span>
        {levels.length > 0 && (
          <span style={{color:'var(--text-3)'}}>
            · Nível {activeLevel + 1} de {levels.length}
          </span>
        )}
      </div>
      <div style={abS.right}>
        <button className={`btn ${analyticsVisible ? 'btn-primary' : ''}`} onClick={onToggleAnalytics}>
          <IcoChart size={14}/> Análise
        </button>
      </div>
    </div>
  );
}

/* ═══════════════════ MAIN APP ═══════════════════ */
function App() {
  const [darkMode, setDarkMode] = useState(() => loadLS('famalegis_dark', false));
  useEffect(() => saveLS('famalegis_dark', darkMode), [darkMode]);

  const [isMobile, setIsMobile] = useState(() => typeof window !== 'undefined' && window.innerWidth < 768);
  useEffect(() => {
    const onResize = () => { setIsMobile(window.innerWidth < 768); };
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  useEffect(() => {
    document.documentElement.setAttribute('data-theme', darkMode ? 'dark' : 'light');
    document.documentElement.setAttribute('data-palette', 'steel');
    document.body.setAttribute('data-density', 'normal');
  }, [darkMode]);

  // Core state — initialize from URL hash if present
  const initialHash = useMemo(() => parseHash(), []);
  const [levels, setLevels] = useState(() => (initialHash?.terms || []).map(t => ({ termo: t, total: 0 })));
  const [levelTotals, setLevelTotals] = useState([]);
  const [activeLevel, setActiveLevel] = useState(() => (initialHash?.terms?.length || 0) - 1);
  const [filters, setFilters] = useState(() => initialHash?.filters || { ano:[], tipo:[], sigla:[], situacao:[] });
  const [results, setResults] = useState([]);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(1);
  const [totalPages, setTotalPages] = useState(1);
  const [loading, setLoading] = useState(false);
  const [sortBy, setSortBy] = useState(null);
  const [sortDir, setSortDir] = useState('asc');
  const [esgotado, setEsgotado] = useState(false);

  // Panels
  const [selectedProp, setSelectedProp] = useState(null);
  const [analyticsVisible, setAnalyticsVisible] = useState(false);
  const [stats, setStats] = useState(null);
  const [statsLoading, setStatsLoading] = useState(false);
  const [filterValues, setFilterValues] = useState({ anos:[], tipos:[], siglas:[], situacoes:[] });

  // UI state
  const [savedSearches, setSavedSearches] = useState(() => loadLS(STORAGE_KEY_SAVED, []));
  const [recentSearches, setRecentSearches] = useState(() => loadLS(STORAGE_KEY_RECENT, []));
  const [sidebarCollapsed, setSidebarCollapsed] = useState(() => loadLS('famalegis_sidebar_collapsed', false));
  const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false);

  useEffect(() => saveLS('famalegis_sidebar_collapsed', sidebarCollapsed), [sidebarCollapsed]);
  useEffect(() => saveLS(STORAGE_KEY_SAVED, savedSearches), [savedSearches]);
  useEffect(() => saveLS(STORAGE_KEY_RECENT, recentSearches), [recentSearches]);

  // Custom dialog system (replaces window.confirm / window.prompt)
  const dialog = useDialog();

  // Load filter values once on mount (global; backend doesn't scope by cascade)
  useEffect(() => {
    MockAPI.getFilters([]).then(setFilterValues).catch(console.error);
  }, []);

  // Fetch results
  const fetchResults = useCallback(async (lvls, flts, pg) => {
    setLoading(true);
    const terms = lvls.map(l => l.termo);
    try {
      const res = await MockAPI.search(terms, flts, pg, PAGE_SIZE);
      setResults(res.resultados);
      setTotal(res.total);
      setTotalPages(res.totalPaginas);
      setEsgotado(res.esgotado);
      if (res.niveis && res.niveis.length > 0) {
        setLevelTotals(res.niveis.map(n => n.total));
      } else {
        setLevelTotals(lvls.map(() => 0));
      }
    } catch(e) { console.error(e); }
    setLoading(false);
  }, []);

  // Fetch stats
  const fetchStats = useCallback(async (lvls, flts) => {
    setStatsLoading(true);
    try {
      const s = await MockAPI.getStats(lvls.map(l=>l.termo), flts);
      setStats(s);
    } catch(e) { console.error(e); }
    setStatsLoading(false);
  }, []);

  // Core search trigger
  useEffect(() => {
    if (levels.length > 0) {
      const activeLvls = levels.slice(0, activeLevel + 1);
      fetchResults(activeLvls, filters, page);
      fetchStats(activeLvls, filters);
    }
  }, [levels, activeLevel, filters, page, fetchResults, fetchStats]);

  // When user selects a row, fetch full detail (with ementa_detalhada, etc.)
  const handleSelectRow = useCallback(async (r) => {
    if (!r) { setSelectedProp(null); return; }
    setSelectedProp(r); // immediate response with what we have
    try {
      const full = await MockAPI.getDetail(r.id);
      if (full) setSelectedProp(prev => prev && prev.id === r.id ? { ...prev, ...full } : prev);
    } catch (e) { console.warn('detail fetch failed', e); }
  }, []);

  // Cascade actions
  const addLevel = useCallback((term) => {
    if (levels.length >= MAX_LEVELS || esgotado) return;
    const newLevels = [...levels, { termo: term, total: 0 }];
    setLevels(newLevels);
    setActiveLevel(newLevels.length - 1);
    setPage(1);
    setSelectedProp(null);
    const terms = newLevels.map(l => l.termo);
    setRecentSearches(prev => {
      return [{ terms, ts: Date.now() }, ...prev.filter(r => r.terms.join('|') !== terms.join('|'))].slice(0, 20);
    });
  }, [levels, esgotado]);

  const navigateLevel = useCallback((idx) => {
    if (idx < 0) {
      setLevels([]);
      setLevelTotals([]);
      setActiveLevel(-1);
      setResults([]);
      setTotal(0);
      setTotalPages(1);
      setEsgotado(false);
      setSelectedProp(null);
      setStats(null);
      return;
    }
    setActiveLevel(idx);
    setPage(1);
  }, []);

  const editLevel = useCallback((idx, newTerm) => {
    const newLevels = levels.slice(0, idx);
    newLevels.push({ termo: newTerm, total: 0 });
    setLevels(newLevels);
    setActiveLevel(idx);
    setPage(1);
    setEsgotado(false);
  }, [levels]);

  const removeLevel = useCallback((idx) => {
    if (idx === 0) { navigateLevel(-1); return; }
    const newLevels = levels.slice(0, idx);
    setLevels(newLevels);
    setActiveLevel(newLevels.length - 1);
    setPage(1);
    setEsgotado(false);
  }, [levels, navigateLevel]);

  const startSearch = useCallback((term) => {
    const newLevels = [{ termo: term, total: 0 }];
    setLevels(newLevels);
    setActiveLevel(0);
    setPage(1);
    setRecentSearches(prev => {
      const terms = [term];
      return [{ terms, ts: Date.now() }, ...prev.filter(r => r.terms.join('|') !== terms.join('|'))].slice(0, 20);
    });
  }, []);

  const loadSearch = useCallback((s) => {
    const newLevels = s.terms.map(t => ({ termo: t, total: 0 }));
    setLevels(newLevels);
    setActiveLevel(newLevels.length - 1);
    setPage(1);
    setFilters({ ano:[], tipo:[], sigla:[], situacao:[] });
    setEsgotado(false);
    setSelectedProp(null);
  }, []);

  const saveSearch = useCallback(async () => {
    if (levels.length === 0) return;
    const terms = levels.map(l => l.termo);
    const name = await dialog.prompt({
      title: 'Salvar busca',
      message: 'Dê um nome para essa busca — você poderá reabri-la depois pela seção "Buscas salvas".',
      defaultValue: terms.join(' → '),
      placeholder: 'Nome da busca',
      confirmLabel: 'Salvar'
    });
    if (!name) return;
    setSavedSearches(prev => [...prev, { name, terms, ts: Date.now() }]);
  }, [levels, dialog]);

  const deleteSearch = useCallback((idx) => {
    setSavedSearches(prev => prev.filter((_, i) => i !== idx));
  }, []);

  const handleSort = useCallback((col) => {
    if (sortBy === col) { setSortDir(d => d === 'asc' ? 'desc' : 'asc'); }
    else { setSortBy(col); setSortDir('asc'); }
  }, [sortBy]);

  const sortedResults = useMemo(() => {
    if (!sortBy) return results;
    return [...results].sort((a, b) => {
      let va = a[sortBy], vb = b[sortBy];
      if (typeof va === 'string') { va = va.toLowerCase(); vb = (vb||'').toLowerCase(); }
      if (va < vb) return sortDir === 'asc' ? -1 : 1;
      if (va > vb) return sortDir === 'asc' ? 1 : -1;
      return 0;
    });
  }, [results, sortBy, sortDir]);

  const clearSaved = useCallback(async () => {
    const ok = await dialog.confirm({
      title: 'Limpar buscas salvas',
      message: 'Todas as buscas salvas serão removidas. Essa ação não pode ser desfeita.',
      confirmLabel: 'Limpar',
      destructive: true
    });
    if (ok) setSavedSearches([]);
  }, [dialog]);
  const clearRecent = useCallback(async () => {
    const ok = await dialog.confirm({
      title: 'Limpar histórico recente',
      message: 'O histórico das últimas buscas desta sessão será removido.',
      confirmLabel: 'Limpar',
      destructive: true
    });
    if (ok) setRecentSearches([]);
  }, [dialog]);

  // Export — CSV/JSON via backend (full result up to 10k), xlsx generated client-side from current page
  const doExport = useCallback(async (format = 'csv') => {
    const terms = levels.map(l => l.termo);
    const fname = `proposicoes_${terms.join('_') || 'export'}`;

    if (format === 'csv' || format === 'json') {
      // Hit backend export endpoint → user downloads up to 10k rows of the current cascade+filters
      const url = MockAPI.buildExportUrl(terms, filters, format);
      const a = document.createElement('a');
      a.href = url;
      a.download = `${fname}.${format}`;
      a.click();
      return;
    }

    if (format === 'xlsx') {
      // SpreadsheetML 2003 — opens natively in Excel/LibreOffice as .xls
      // Generated client-side from current page's results (backend doesn't expose xlsx)
      const esc = s => String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
      const cell = (v, type='String') => `<Cell><Data ss:Type="${type}">${esc(v)}</Data></Cell>`;
      const rows = results.map(r =>
        `<Row>${cell(r.id)}${cell(r.sigla)}${cell(r.numero)}${cell(r.ano,'Number')}${cell(r.tipo)}${cell(r.ementa)}${cell(r.situacao)}</Row>`
      ).join('');
      const header = `<Row>${['ID','Sigla','Número','Ano','Tipo','Ementa','Situação'].map(h=>cell(h)).join('')}</Row>`;
      const content = `<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<Worksheet ss:Name="Proposições"><Table>${header}${rows}</Table></Worksheet></Workbook>`;
      const blob = new Blob([content], { type: 'application/vnd.ms-excel' });
      const a = document.createElement('a');
      a.href = URL.createObjectURL(blob);
      a.download = `${fname}.xls`;
      a.click();
      setTimeout(() => URL.revokeObjectURL(a.href), 1000);
    }
  }, [levels, filters, results]);

  const toggleTheme = useCallback(() => setDarkMode(d => !d), []);

  // Esc — close panels
  useEffect(() => {
    const handler = (e) => {
      if (e.key !== 'Escape') return;
      const tag = e.target.tagName;
      const isInput = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
      if (isInput) { e.target.blur(); return; }
      if (selectedProp) { setSelectedProp(null); return; }
      if (analyticsVisible) { setAnalyticsVisible(false); return; }
      if (mobileFiltersOpen) { setMobileFiltersOpen(false); return; }
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [selectedProp, analyticsVisible, mobileFiltersOpen]);

  // Merge levels with their totals for display
  const levelsWithTotals = useMemo(
    () => levels.map((l, i) => ({ ...l, total: levelTotals[i] || 0 })),
    [levels, levelTotals]
  );

  const isExploring = levels.length > 0;

  const appS = {
    shell: { height:'100vh', display:'flex', flexDirection:'column', overflow:'hidden' },
    main: { flex:1, display:'flex', overflow:'hidden' },
    content: { flex:1, display:'flex', flexDirection:'column', overflow:'hidden', minWidth:0 }
  };

  return (
    <div style={appS.shell}>
      <AppHeader theme={darkMode ? 'dark' : 'light'} onToggleTheme={toggleTheme}
        levels={levels} filters={filters} onExport={doExport} isMobile={isMobile}
        onShowFilters={() => setMobileFiltersOpen(true)}/>

      {isExploring && (
        <Cascade style={isMobile ? 'stack' : 'drill'} levels={levelsWithTotals} activeLevel={activeLevel}
          onNavigate={navigateLevel} onEdit={editLevel} onRemove={removeLevel}
          onAdd={addLevel} maxLevels={MAX_LEVELS} esgotado={esgotado}/>
      )}

      <div style={appS.main}>
        {isExploring && !isMobile && (
          <FiltersSidebar filterValues={filterValues} filters={filters}
            onFilterChange={f => { setFilters(f); setPage(1); }}
            onClearFilters={() => { setFilters({ano:[],tipo:[],sigla:[],situacao:[]}); setPage(1); }}
            savedSearches={savedSearches} recentSearches={recentSearches}
            onLoadSearch={loadSearch} onDeleteSearch={deleteSearch}
            onSaveSearch={saveSearch} onClearSaved={clearSaved} onClearRecent={clearRecent}
            levels={levels}
            collapsed={sidebarCollapsed} onToggleCollapse={() => setSidebarCollapsed(c => !c)}/>
        )}

        <div style={appS.content}>
          {!isExploring ? (
            <WelcomeScreen onSearch={startSearch}/>
          ) : (
            <>
              <ActionBar total={total} loading={loading} levels={levels} activeLevel={activeLevel}
                analyticsVisible={analyticsVisible}
                onToggleAnalytics={() => setAnalyticsVisible(v => !v)}/>

              {analyticsVisible ? (
                <AnalyticsPanel stats={stats} loading={statsLoading} placement="page"
                  onClose={() => setAnalyticsVisible(false)}/>
              ) : (
                <div style={{flex:1, display:'flex', overflow:'hidden'}}>
                  <ResultsTable results={sortedResults} loading={loading} total={total}
                    page={page} totalPages={totalPages} onPageChange={setPage}
                    sortBy={sortBy} sortDir={sortDir} onSort={handleSort}
                    selectedId={selectedProp?.id} onSelect={handleSelectRow}
                    isMobile={isMobile}/>

                  {selectedProp && (
                    <DetailPanel proposition={selectedProp} onClose={() => setSelectedProp(null)}
                      isMobile={isMobile}/>
                  )}
                </div>
              )}
            </>
          )}
        </div>
      </div>

      {isMobile && mobileFiltersOpen && (
        <>
          <div className="sheet-backdrop" onClick={() => setMobileFiltersOpen(false)}/>
          <div className="sheet" style={{maxHeight:'85vh', display:'flex', flexDirection:'column'}}>
            <div className="sheet-handle"/>
            <div style={{display:'flex', alignItems:'center', justifyContent:'space-between', padding:'4px var(--sp-4) var(--sp-2)', borderBottom:'1px solid var(--border-1)'}}>
              <span style={{fontSize:'0.923rem', fontWeight:600, color:'var(--text-0)'}}>Filtros</span>
              <button className="btn btn-ghost btn-sm" onClick={() => setMobileFiltersOpen(false)}>
                <IcoClose size={12}/>
              </button>
            </div>
            <div style={{flex:1, overflow:'hidden'}}>
              <FiltersSidebar filterValues={filterValues} filters={filters}
                onFilterChange={f => { setFilters(f); setPage(1); }}
                onClearFilters={() => { setFilters({ano:[],tipo:[],sigla:[],situacao:[]}); setPage(1); }}
                savedSearches={savedSearches} recentSearches={recentSearches}
                onLoadSearch={s => { loadSearch(s); setMobileFiltersOpen(false); }}
                onDeleteSearch={deleteSearch}
                onSaveSearch={saveSearch} onClearSaved={clearSaved} onClearRecent={clearRecent}
                levels={levels} mobileMode/>
            </div>
          </div>
        </>
      )}

      {/* Custom dialogs (confirm/prompt) — replaces browser native window.confirm/prompt */}
      {dialog.render}
    </div>
  );
}

/* ═══════════════════ RENDER ═══════════════════ */
// Wait for BASE_TOTAL fetch so the welcome screen and cascade base node display the real count.
MockAPI.init().finally(() => {
  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(<App/>);
});
