// ===== Elejemais — Modal screens =====

const { useState: useState_m, useEffect: useEffect_m, useRef: useRef_m } = React;

/* ── Hook reutilizável para carregar municípios ── */
function useMunicipalities() {
  const [municipios, setMunicipios] = useState_m([]);
  useEffect_m(() => {
    window.API.getMunicipalities().then(setMunicipios).catch(console.error);
  }, []);
  return municipios;
}

/* ── Modal base ── */
function Modal({ title, eyebrow, onClose, width = 640, children, footer }) {
  useEffect_m(() => {
    const k = (e) => e.key === "Escape" && onClose();
    window.addEventListener("keydown", k);
    return () => window.removeEventListener("keydown", k);
  }, [onClose]);
  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal" style={{ maxWidth: width }} onClick={e => e.stopPropagation()}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 18 }}>
          <div>
            {eyebrow && <div className="eyebrow">{eyebrow}</div>}
            <h3 className="serif" style={{ fontSize: 26, margin: "4px 0 0", letterSpacing: "-0.01em" }}>{title}</h3>
          </div>
          <button className="btn btn--ghost btn--icon" onClick={onClose} title="Fechar"><Icon.X size={14} /></button>
        </div>
        <div>{children}</div>
        {footer && (
          <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 20, paddingTop: 18, borderTop: "1px solid var(--line)" }}>
            {footer}
          </div>
        )}
      </div>
    </div>
  );
}

/* ── EXPORTAR ── */
function ExportarModal({ onClose, campaign }) {
  const [formato, setFormato] = useState_m("xlsx");
  const [escopo,  setEscopo]  = useState_m("apoiadores");
  const [periodo, setPeriodo] = useState_m("30d");
  const [done,    setDone]    = useState_m(false);

  return (
    <Modal eyebrow="Exportar dados" title="Baixar relatório de dados" onClose={onClose} width={620}
      footer={done ? (
        <button className="btn btn--primary" onClick={onClose}>Fechar</button>
      ) : (
        <>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn btn--primary" onClick={() => setDone(true)}>
            <Icon.Download size={13} /> Gerar arquivo
          </button>
        </>
      )}>
      {done ? (
        <div style={{ textAlign: "center", padding: "26px 0" }}>
          <div style={{ width: 48, height: 48, borderRadius: 999, background: "var(--accent-soft)", display: "grid", placeItems: "center", margin: "0 auto 14px" }}>
            <Icon.Check size={22} />
          </div>
          <h4 className="serif" style={{ fontSize: 22, margin: "0 0 6px" }}>Arquivo gerado</h4>
          <p className="dim" style={{ margin: "0 0 16px" }}>elejemais_{escopo}_{periodo}.{formato}</p>
          <a className="btn btn--primary" download href="#" onClick={e => e.preventDefault()}>
            <Icon.Download size={13} /> Baixar arquivo
          </a>
        </div>
      ) : (
        <div style={{ display: "grid", gap: 18 }}>
          <Field label="Escopo">
            <SegRadio value={escopo} onChange={setEscopo} options={[
              { v: "apoiadores", l: "Apoiadores" },
              { v: "liderancas", l: "Lideranças" },
              { v: "tudo",       l: "Tudo" },
            ]} />
          </Field>
          <Field label="Período">
            <SegRadio value={periodo} onChange={setPeriodo} options={[
              { v: "7d",   l: "7 dias" },
              { v: "30d",  l: "30 dias" },
              { v: "90d",  l: "90 dias" },
              { v: "tudo", l: "Toda campanha" },
            ]} />
          </Field>
          <Field label="Formato">
            <SegRadio value={formato} onChange={setFormato} options={[
              { v: "xlsx", l: "Excel (.xlsx)" },
              { v: "csv",  l: "CSV" },
              { v: "pdf",  l: "PDF" },
            ]} />
          </Field>
          <div className="callout">
            <span className="dim">Campanha:</span> <strong>{campaign.candidato}</strong> •
            <strong className="mono"> {window.fmt(campaign.apoiadores)}</strong> apoiadores
          </div>
        </div>
      )}
    </Modal>
  );
}

/* ── PLANEJAR AÇÃO ── */
function PlanejarAcaoModal({ onClose, campaign }) {
  const municipios  = useMunicipalities();
  const [tipo, setTipo]             = useState_m("evento");
  const [data, setData]             = useState_m("");
  const [municipiosSel, setMunSel]  = useState_m([]);
  const [titulo, setTitulo]         = useState_m("");
  const [loading, setLoading]       = useState_m(false);
  const [done, setDone]             = useState_m(false);

  const toggleMun = (id) => setMunSel(s => s.includes(id) ? s.filter(x => x !== id) : [...s, id]);

  const handleSave = async () => {
    if (!titulo || !data || municipiosSel.length === 0) return;
    setLoading(true);
    try {
      await window.API.createAction(campaign.id, {
        titulo,
        tipo,
        data,
        municipioId: municipiosSel[0] || null,
      });
      setDone(true);
    } catch (err) {
      console.error(err);
      alert("Erro ao salvar ação: " + err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <Modal eyebrow="Plano de campanha" title="Nova ação em campo" onClose={onClose} width={700}
      footer={done ? (
        <button className="btn btn--primary" onClick={onClose}>Fechar</button>
      ) : (
        <>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn btn--primary" disabled={loading} onClick={handleSave}>
            {loading ? "Salvando…" : "Agendar ação"}
          </button>
        </>
      )}>
      {done ? (
        <div style={{ textAlign: "center", padding: "24px 0" }}>
          <Icon.Check size={32} />
          <div className="serif" style={{ fontSize: 22, marginTop: 10 }}>Ação agendada!</div>
          <div className="dim" style={{ marginTop: 6 }}>{titulo} — {data}</div>
        </div>
      ) : (
        <div style={{ display: "grid", gap: 16 }}>
          <Field label="Tipo de ação">
            <SegRadio value={tipo} onChange={setTipo} options={[
              { v: "evento",   l: "Evento público" },
              { v: "panfletagem", l: "Porta a porta" },
              { v: "comicio",  l: "Comício" },
              { v: "reuniao",  l: "Reunião" },
            ]} />
          </Field>
          <Field label="Título da ação">
            <input className="input" placeholder="Ex: Caminhada com lideranças"
              value={titulo} onChange={e => setTitulo(e.target.value)} />
          </Field>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <Field label="Data"><input className="input" type="date" value={data} onChange={e => setData(e.target.value)} /></Field>
            <Field label="Horário"><input className="input" type="time" defaultValue="18:00" /></Field>
          </div>
          <Field label="Municípios envolvidos">
            <div className="chip-pick">
              {municipios.slice(0, 12).map(m => (
                <button key={m.id} className={"chip " + (municipiosSel.includes(m.id) ? "chip--on" : "")} onClick={() => toggleMun(m.id)}>
                  {municipiosSel.includes(m.id) && <Icon.Check size={11} />} {m.name}
                </button>
              ))}
            </div>
          </Field>
        </div>
      )}
    </Modal>
  );
}

/* ── NOVA LIDERANÇA ── */
function NovaLiderancaModal({ onClose, contexto, campaign }) {
  const municipios  = useMunicipalities();
  const fileInputRef = React.useRef(null);
  const [aba, setAba]           = useState_m("individual");
  const [nome, setNome]                   = useState_m("");
  const [telefone, setTel]                = useState_m("");
  const [funcao, setFuncao]               = useState_m("lideranca_municipal");
  const [municipioId, setMunId]           = useState_m("");
  const [estimativaVotos, setEstimativa]  = useState_m("");
  const [arquivo, setArquivo]             = useState_m(null);
  const [csvRows, setCsvRows]             = useState_m([]);
  const [loteStep, setLoteStep]           = useState_m("select"); // select | preview | done
  const [loteResults, setLoteResults]     = useState_m(null);
  const [loading, setLoading]             = useState_m(false);
  const [done, setDone]                   = useState_m(false);

  const FUNCAO_LABELS = {
    coordenador_regional:  'Coord. Regional',
    lideranca_municipal:   'Líder Municipal',
    lideranca_comunitaria: 'Líder Comunitário',
    coord_midia:           'Coord. de Mídia',
    coord_juventude:       'Coord. Juventude',
  };

  // Normaliza variações de nomes de função do CSV para os valores aceitos pelo banco
  const FUNCAO_MAP = {
    lider_comunitario:     'lideranca_comunitaria',
    lider_municipal:       'lideranca_municipal',
    coord_regional:        'coordenador_regional',
    lideranca_comunitaria: 'lideranca_comunitaria',
    lideranca_municipal:   'lideranca_municipal',
    coordenador_regional:  'coordenador_regional',
    coord_midia:           'coord_midia',
    coord_juventude:       'coord_juventude',
  };

  const baixarTemplate = () => {
    // Usa BOM + ponto-e-vírgula para compatibilidade com Excel BR
    const bom = '﻿';
    const csv = bom + "nome;telefone;email;funcao;municipio;vote_estimate\nEx: Maria Silva;(79) 99999-0000;maria@exemplo.com;lideranca_municipal;Aracaju;500\n";
    const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
    const url  = URL.createObjectURL(blob);
    const a    = document.createElement("a");
    a.href = url; a.download = "template_liderancas.csv"; a.click();
    URL.revokeObjectURL(url);
  };

  const handleFileChange = (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    const nome_arq = file.name;
    const reader = new FileReader();
    reader.onerror = function() { alert("Erro ao ler o arquivo."); };
    reader.onload = function(ev) {
      try {
        // Remove BOM UTF-8 (comum em arquivos salvos pelo Excel)
        const text = ev.target.result.replace(/^﻿/, '');
        const lines = text.trim().split(/\r?\n/);
        if (lines.length < 2) { alert("CSV vazio ou sem dados."); return; }

        // Detecta delimitador: Excel BR usa ";" por padrão, outros usam ","
        const firstLine = lines[0];
        const nSemicolon = (firstLine.match(/;/g) || []).length;
        const nComma     = (firstLine.match(/,/g) || []).length;
        const delim = nSemicolon >= nComma ? ';' : ',';

        const headers = firstLine.split(delim).map(function(h) {
          return h.trim().toLowerCase().replace(/"/g, '');
        });

        const rows = [];
        for (var i = 1; i < lines.length; i++) {
          var parts = lines[i].split(delim).map(function(v) {
            return v.trim().replace(/^"|"$/g, '');
          });
          var obj = {};
          headers.forEach(function(h, idx) { obj[h] = parts[idx] || ''; });
          if (obj.nome && obj.nome.trim()) rows.push(obj);
        }

        setArquivo(nome_arq);
        setCsvRows(rows);
        setLoteStep(rows.length > 0 ? "preview" : "select");
        if (rows.length === 0) {
          alert(
            "Nenhuma linha válida encontrada.\n" +
            "Delimitador detectado: " + (delim === ';' ? 'ponto-e-vírgula (;)' : 'vírgula (,)') + "\n" +
            "Colunas encontradas: " + headers.join(', ') + "\n" +
            "Certifique-se de que a coluna 'nome' existe no cabeçalho."
          );
        }
      } catch(err) {
        alert("Erro ao processar CSV: " + err.message);
      }
    };
    reader.readAsText(file, 'UTF-8');
  };

  const handleSave = async () => {
    if (!nome) return;
    setLoading(true);
    try {
      await window.API.createLeader(campaign.id, { nome, telefone, funcao, municipioId: municipioId || null, estimativaVotos: estimativaVotos || null });
      setDone(true);
    } catch (err) {
      console.error(err);
      alert("Erro ao salvar liderança: " + err.message);
    } finally {
      setLoading(false);
    }
  };

  const handleImport = async () => {
    if (csvRows.length === 0) return;
    setLoading(true);

    // Cache: nome_lower → id (evita criar ou buscar o mesmo município várias vezes)
    const munCache = {};
    municipios.forEach(function(m) { munCache[m.name.toLowerCase()] = m.id; });

    let success = 0;
    const criados = [];
    const erros   = [];

    for (var i = 0; i < csvRows.length; i++) {
      var row = csvRows[i];
      try {
        // Resolve município: busca no cache; se não encontrar, cria no banco
        var munId = null;
        var munNome = (row.municipio || '').trim();
        if (munNome) {
          var key = munNome.toLowerCase();
          if (munCache[key] !== undefined) {
            munId = munCache[key];
          } else {
            try {
              var novo = await window.API.createMunicipality({ name: munNome, state: 'SE' });
              munId = novo.id;
              munCache[key] = munId;
              criados.push(munNome);
            } catch (munErr) {
              // Município pode ter sido criado por outra linha simultânea — ignorar ausência de vínculo
              console.warn('Município não vinculado:', munNome, munErr.message);
              munId = null;
              munCache[key] = null;
            }
          }
        }

        await window.API.createLeader(campaign.id, {
          nome:            row.nome,
          telefone:        row.telefone || null,
          funcao:          FUNCAO_MAP[row.funcao] || row.funcao || 'lideranca_comunitaria',
          municipioId:     munId,
          estimativaVotos: row.vote_estimate && row.vote_estimate.trim() ? parseInt(row.vote_estimate) : null,
        });
        success++;
      } catch (err) {
        erros.push({ nome: row.nome, erro: err.message });
      }
    }

    setLoteResults({ success, criados, erros });
    setLoteStep("done");
    setLoading(false);
  };

  const resetLote = () => { setCsvRows([]); setArquivo(null); setLoteStep("select"); };

  const isLoteDone = aba === "lote" && loteStep === "done";

  return (
    <Modal eyebrow="Nova liderança" title={aba === "individual" ? "Cadastrar liderança" : "Importar em lote"} onClose={onClose} width={760}
      footer={done || isLoteDone ? (
        <button className="btn btn--primary" onClick={onClose}>Fechar</button>
      ) : (
        <>
          <button className="btn" onClick={onClose}>Cancelar</button>
          {aba === "individual" && (
            <button className="btn btn--primary" disabled={loading || !nome} onClick={handleSave}>
              {loading ? "Salvando…" : "Salvar liderança"}
            </button>
          )}
          {aba === "lote" && loteStep === "preview" && (
            <button className="btn btn--primary" disabled={loading} onClick={handleImport}>
              {loading ? "Importando…" : `Importar ${csvRows.length} liderança${csvRows.length !== 1 ? "s" : ""}`}
            </button>
          )}
        </>
      )}>
      {done ? (
        <div style={{ textAlign: "center", padding: "24px 0" }}>
          <Icon.Check size={32} />
          <div className="serif" style={{ fontSize: 22, marginTop: 10 }}>Liderança cadastrada!</div>
          <div className="dim" style={{ marginTop: 6 }}>{nome}</div>
        </div>
      ) : (
        <>
          <div className="tabs" style={{ marginBottom: 18 }}>
            <button className={"tab " + (aba === "individual" ? "is-active" : "")} onClick={() => setAba("individual")}>Cadastro individual</button>
            <button className={"tab " + (aba === "lote" ? "is-active" : "")} onClick={() => setAba("lote")}>Importar em lote</button>
          </div>

          {aba === "individual" ? (
            <div style={{ display: "grid", gap: 14 }}>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
                <Field label="Nome completo">
                  <input className="input" placeholder="Ex: Maria Silva" value={nome} onChange={e => setNome(e.target.value)} autoFocus />
                </Field>
                <Field label="Função">
                  <select className="input" value={funcao} onChange={e => setFuncao(e.target.value)}>
                    <option value="coordenador_regional">Coordenador Regional</option>
                    <option value="lideranca_municipal">Líder Municipal</option>
                    <option value="lideranca_comunitaria">Líder Comunitário</option>
                    <option value="coord_midia">Coord. de Mídia</option>
                    <option value="coord_juventude">Coord. Juventude</option>
                  </select>
                </Field>
              </div>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
                <Field label="Telefone">
                  <input className="input mono" placeholder="(79) 9 0000-0000" value={telefone} onChange={e => setTel(e.target.value)} />
                </Field>
                <Field label="Município">
                  <select className="input" value={municipioId} onChange={e => setMunId(e.target.value)}>
                    <option value="">— Selecionar município —</option>
                    {municipios.map(m => <option key={m.id} value={m.id}>{m.name}</option>)}
                  </select>
                </Field>
              </div>
              <Field label="Estimativa de votos">
                <input className="input mono" type="number" min="0" placeholder="Ex: 500"
                  value={estimativaVotos} onChange={e => setEstimativa(e.target.value)} />
              </Field>
            </div>

          ) : isLoteDone ? (
            <div style={{ textAlign: "center", padding: "24px 0" }}>
              <div style={{ width: 48, height: 48, borderRadius: 999, background: "var(--accent-soft)", display: "grid", placeItems: "center", margin: "0 auto 14px" }}>
                <Icon.Check size={22} />
              </div>
              <div className="serif" style={{ fontSize: 22, marginTop: 10 }}>
                {loteResults.success} liderança{loteResults.success !== 1 ? "s" : ""} importada{loteResults.success !== 1 ? "s" : ""}!
              </div>
              {loteResults.criados && loteResults.criados.length > 0 && (
                <div style={{ marginTop: 14, textAlign: "left", background: "var(--accent-soft)", borderRadius: 8, padding: "12px 14px" }}>
                  <div style={{ fontSize: 12, color: "var(--accent-strong)", marginBottom: 6, fontWeight: 500 }}>
                    <Icon.Plus size={12} /> {loteResults.criados.length} município{loteResults.criados.length !== 1 ? "s" : ""} criado{loteResults.criados.length !== 1 ? "s" : ""} automaticamente:
                  </div>
                  <div style={{ fontSize: 12, color: "var(--accent-strong)" }}>
                    {loteResults.criados.join(" · ")}
                  </div>
                </div>
              )}
              {loteResults.erros.length > 0 && (
                <div style={{ marginTop: 10, textAlign: "left", background: "var(--surface-2)", borderRadius: 8, padding: "12px 14px" }}>
                  <div className="dim" style={{ fontSize: 12, marginBottom: 6 }}>{loteResults.erros.length} linha{loteResults.erros.length !== 1 ? "s" : ""} com erro:</div>
                  {loteResults.erros.map((e, i) => (
                    <div key={i} style={{ fontSize: 12, color: "oklch(0.55 0.18 30)", padding: "2px 0" }}>
                      <strong>{e.nome}</strong>: {e.erro}
                    </div>
                  ))}
                </div>
              )}
            </div>

          ) : (
            <div style={{ display: "grid", gap: 16 }}>
              <div className="callout" style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
                <div>
                  <strong>Baixe o template antes de subir</strong>
                  <div className="dim" style={{ fontSize: 12, marginTop: 2 }}>CSV com as colunas: nome, telefone, funcao, municipio.</div>
                </div>
                <button className="btn" onClick={baixarTemplate}><Icon.Download size={13} /> Baixar template</button>
              </div>

              {loteStep === "select" && (
                <Field label="Arquivo .csv">
                  <input ref={fileInputRef} type="file" accept=".csv,.txt"
                    style={{ display: "none" }} onChange={handleFileChange} />
                  <div className="dropzone" onClick={() => fileInputRef.current && fileInputRef.current.click()}>
                    <Icon.Download size={20} style={{ transform: "rotate(180deg)" }} />
                    <strong style={{ marginTop: 8 }}>Clique para selecionar ou arraste aqui</strong>
                    <span className="dim" style={{ fontSize: 12 }}>Formato .csv — máximo 10.000 linhas</span>
                  </div>
                </Field>
              )}

              {loteStep === "preview" && csvRows.length > 0 && (
                <>
                  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
                    <div>
                      <strong style={{ fontSize: 14 }}>{csvRows.length} liderança{csvRows.length !== 1 ? "s" : ""} encontrada{csvRows.length !== 1 ? "s" : ""}</strong>
                      <span className="dim" style={{ fontSize: 12, marginLeft: 8 }}>{arquivo}</span>
                    </div>
                    <button className="btn btn--ghost btn--sm" onClick={resetLote}>Trocar arquivo</button>
                  </div>
                  <div style={{ maxHeight: 300, overflowY: "auto", borderRadius: 8, border: "1px solid var(--line)" }}>
                    <table className="tbl">
                      <thead>
                        <tr>
                          <th style={{ width: 28 }}>#</th>
                          <th>Nome</th>
                          <th>Telefone</th>
                          <th>Função</th>
                          <th>Município</th>
                          <th className="num">Est. Votos</th>
                        </tr>
                      </thead>
                      <tbody>
                        {csvRows.map((r, i) => {
                          const munOk = municipios.some(m => m.name.toLowerCase() === (r.municipio || '').toLowerCase().trim());
                          const funcaoNorm = FUNCAO_MAP[r.funcao] || r.funcao;
                          return (
                            <tr key={i}>
                              <td className="mono dim" style={{ fontSize: 11 }}>{i + 1}</td>
                              <td><strong>{r.nome}</strong></td>
                              <td className="mono dim">{r.telefone || "—"}</td>
                              <td className="dim">{FUNCAO_LABELS[funcaoNorm] || r.funcao || "—"}</td>
                              <td>
                                {r.municipio
                                  ? munOk
                                    ? r.municipio
                                    : <span style={{ color: "var(--accent)", fontSize: 12 }}>{r.municipio} <span className="dim">(será criado)</span></span>
                                  : <span className="dim">—</span>}
                              </td>
                              <td className="num mono dim">{r.vote_estimate && r.vote_estimate.trim() ? window.fmt(parseInt(r.vote_estimate)) : "—"}</td>
                            </tr>
                          );
                        })}
                      </tbody>
                    </table>
                  </div>
                  {csvRows.some(r => r.municipio && !municipios.some(m => m.name.toLowerCase() === r.municipio.toLowerCase().trim())) && (
                    <div className="callout" style={{ fontSize: 12 }}>
                      Municípios marcados como <strong>"será criado"</strong> não estão no cadastro e serão criados automaticamente durante a importação.
                    </div>
                  )}
                </>
              )}
            </div>
          )}
        </>
      )}
    </Modal>
  );
}

/* ── CADASTRAR APOIADOR ── */
function CadastrarApoiadorModal({ onClose, campaign }) {
  const municipios = useMunicipalities();
  const [lideres, setLideres]       = useState_m([]);
  const [nome, setNome]             = useState_m("");
  const [telefone, setTel]          = useState_m("");
  const [municipioId, setMunId]     = useState_m("");
  const [liderId, setLiderId]       = useState_m("");
  const [origem, setOrigem]         = useState_m("lideranca");
  const [loading, setLoading]       = useState_m(false);
  const [count, setCount]           = useState_m(0);

  useEffect_m(() => {
    if (!campaign?.id) return;
    window.API.getLeaders(campaign.id).then(setLideres).catch(console.error);
  }, [campaign?.id]);

  const handleSave = async () => {
    if (!nome) return;
    setLoading(true);
    try {
      await window.API.createSupporter(campaign.id, { nome, telefone, municipioId: municipioId || null, liderId: liderId || null, origem });
      setNome(""); setTel(""); setCount(c => c + 1);
    } catch (err) {
      console.error(err);
      alert("Erro ao cadastrar: " + err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <Modal eyebrow="Novo apoiador" title="Cadastrar apoiador" onClose={onClose} width={680}
      footer={
        <>
          <button className="btn" onClick={onClose}>
            {count > 0 ? `Fechar (${count} cadastrado${count > 1 ? "s" : ""})` : "Cancelar"}
          </button>
          <button className="btn btn--primary" disabled={loading || !nome} onClick={handleSave}>
            {loading ? "Salvando…" : "Salvar e cadastrar outro"}
          </button>
        </>
      }>
      <div style={{ display: "grid", gap: 14 }}>
        <Field label="Nome completo">
          <input className="input" placeholder="Ex: João Pereira" value={nome} onChange={e => setNome(e.target.value)} autoFocus />
        </Field>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <Field label="Telefone (WhatsApp)">
            <input className="input mono" placeholder="(79) 9 0000-0000" value={telefone} onChange={e => setTel(e.target.value)} />
          </Field>
          <Field label="Município">
            <select className="input" value={municipioId} onChange={e => setMunId(e.target.value)}>
              <option value="">— Selecionar município —</option>
              {municipios.map(m => <option key={m.id} value={m.id}>{m.name}</option>)}
            </select>
          </Field>
        </div>
        <Field label="Liderança responsável">
          <select className="input" value={liderId} onChange={e => setLiderId(e.target.value)}>
            <option value="">— Sem liderança ainda —</option>
            {lideres.map(l => <option key={l.id} value={l.id}>{l.nome} · {l.regiao}</option>)}
          </select>
        </Field>
        <Field label="Como conheceu a campanha">
          <SegRadio value={origem} onChange={setOrigem} options={[
            { v: "lideranca",    l: "Via liderança" },
            { v: "indicacao",    l: "Indicação" },
            { v: "evento",       l: "Evento" },
            { v: "redes_sociais", l: "Redes sociais" },
          ]} />
        </Field>
        {count > 0 && (
          <div style={{ padding: "10px 14px", borderRadius: 8, background: "var(--accent-soft)", color: "var(--accent-strong)", fontSize: 13 }}>
            <Icon.Check size={13} /> {count} apoiador{count > 1 ? "es" : ""} cadastrado{count > 1 ? "s" : ""} nesta sessão.
          </div>
        )}
      </div>
    </Modal>
  );
}

/* ── RELATÓRIO CUSTOMIZADO ── */
function RelatorioCustomModal({ onClose }) {
  const municipios = useMunicipalities();
  const [cols, setCols] = useState_m(["nome", "telefone", "municipio", "lider"]);
  const TODOS = [
    { v: "nome", l: "Nome" }, { v: "telefone", l: "Telefone" }, { v: "email", l: "E-mail" },
    { v: "municipio", l: "Município" }, { v: "lider", l: "Liderança" },
    { v: "data", l: "Data cadastro" }, { v: "origem", l: "Origem" },
  ];
  const tog = (v) => setCols(s => s.includes(v) ? s.filter(x => x !== v) : [...s, v]);

  return (
    <Modal eyebrow="Relatório customizado" title="Montar relatório sob medida" onClose={onClose} width={720}
      footer={<>
        <button className="btn" onClick={onClose}>Cancelar</button>
        <button className="btn btn--primary" onClick={onClose}><Icon.Download size={13} /> Gerar e baixar</button>
      </>}>
      <div style={{ display: "grid", gap: 18 }}>
        <Field label="Base de dados">
          <SegRadio value="apoiadores" onChange={() => {}} options={[
            { v: "apoiadores", l: "Apoiadores" },
            { v: "liderancas", l: "Lideranças" },
          ]} />
        </Field>
        <Field label="Colunas">
          <div className="chip-pick">
            {TODOS.map(c => (
              <button key={c.v} className={"chip " + (cols.includes(c.v) ? "chip--on" : "")} onClick={() => tog(c.v)}>
                {cols.includes(c.v) && <Icon.Check size={11} />} {c.l}
              </button>
            ))}
          </div>
        </Field>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <Field label="Filtrar por município">
            <select className="input">
              <option value="">Todos</option>
              {municipios.map(m => <option key={m.id} value={m.id}>{m.name}</option>)}
            </select>
          </Field>
          <Field label="Período">
            <select className="input">
              <option>Últimos 30 dias</option>
              <option>Últimos 90 dias</option>
              <option>Toda campanha</option>
            </select>
          </Field>
        </div>
        <Field label="Formato">
          <SegRadio value="xlsx" onChange={() => {}} options={[
            { v: "xlsx", l: "Excel" }, { v: "csv", l: "CSV" }, { v: "pdf", l: "PDF" },
          ]} />
        </Field>
        <div className="callout">
          <span className="dim">Colunas selecionadas:</span> <strong className="mono">{cols.length}</strong>
        </div>
      </div>
    </Modal>
  );
}

/* ── LIDERANÇAS POR REGIÃO ── */
function LiderancasRegiaoModal({ onClose, contexto, campaign }) {
  const [lideres, setLideres] = useState_m([]);
  const [search,  setSearch]  = useState_m("");
  const [loading, setLoading] = useState_m(true);

  useEffect_m(() => {
    if (!campaign?.id || !contexto?.nome) return;
    setLoading(true);
    window.API.getLeaders(campaign.id)
      .then(data => { setLideres(data.filter(l => l.regiao === contexto.nome)); setLoading(false); })
      .catch(err => { console.error(err); setLoading(false); });
  }, [campaign?.id, contexto?.nome]);

  const q = search.trim().toLowerCase();
  const filtered = q
    ? lideres.filter(l => l.nome.toLowerCase().includes(q) || (l.funcao || '').toLowerCase().includes(q))
    : lideres;

  return (
    <Modal eyebrow="Lideranças do município" title={contexto?.nome || "Município"} onClose={onClose} width={700}
      footer={<button className="btn btn--primary" onClick={onClose}>Fechar</button>}>
      {loading ? (
        <div style={{ textAlign: "center", padding: 32 }} className="dim">Carregando…</div>
      ) : lideres.length === 0 ? (
        <div style={{ textAlign: "center", padding: 32 }} className="dim">
          Nenhuma liderança neste município ainda.
        </div>
      ) : (
        <>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12 }}>
            <input className="input" placeholder="Filtrar por nome ou função…"
              value={search} onChange={e => setSearch(e.target.value)} style={{ flex: 1 }} />
            <span className="dim" style={{ fontSize: 12, whiteSpace: "nowrap" }}>
              {filtered.length} de {lideres.length}
            </span>
          </div>
          <div style={{ maxHeight: 440, overflowY: "auto", borderRadius: 8, border: "1px solid var(--line)" }}>
            <table className="tbl">
              <thead>
                <tr><th>Nome</th><th>Função</th><th className="num">Apoiadores</th><th>Status</th></tr>
              </thead>
              <tbody>
                {filtered.map(l => (
                  <tr key={l.id}>
                    <td>
                      <strong>{l.nome}</strong>
                      {l.telefone && <div className="dim mono" style={{ fontSize: 11 }}>{l.telefone}</div>}
                    </td>
                    <td className="dim">{l.funcao}</td>
                    <td className="num mono">{window.fmt(l.apoio)}</td>
                    <td><span className={"badge " + (l.status === "ativa" ? "badge--accent" : "")}>{l.status}</span></td>
                  </tr>
                ))}
                {filtered.length === 0 && (
                  <tr><td colSpan={4} style={{ textAlign: "center", padding: 24 }} className="dim">Nenhum resultado.</td></tr>
                )}
              </tbody>
            </table>
          </div>
        </>
      )}
    </Modal>
  );
}

/* ── CALENDÁRIO ── */
function CalendarioModal({ onClose, campaign }) {
  const [actions, setActions] = useState_m([]);
  const [view, setView]       = useState_m(new Date(2026, 4, 1));

  useEffect_m(() => {
    if (!campaign?.id) return;
    window.API.getActions(campaign.id).then(setActions).catch(console.error);
  }, [campaign?.id]);

  const month = view.getMonth(), year = view.getFullYear();
  const first = new Date(year, month, 1).getDay();
  const days  = new Date(year, month + 1, 0).getDate();
  const mname = view.toLocaleDateString("pt-BR", { month: "long", year: "numeric" });

  const eventsByDate = {};
  actions.forEach(a => {
    if (!a.date) return;
    const k = a.date.slice(0, 10);
    if (!eventsByDate[k]) eventsByDate[k] = [];
    eventsByDate[k].push(a);
  });

  const today = new Date();
  const isToday = (d) => year === today.getFullYear() && month === today.getMonth() && d === today.getDate();

  return (
    <Modal eyebrow="Agenda da campanha" title="Calendário" onClose={onClose} width={760}
      footer={<>
        <button className="btn" onClick={onClose}>Fechar</button>
        <button className="btn btn--primary" onClick={onClose}><Icon.Plus size={13} /> Nova ação</button>
      </>}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
        <button className="btn btn--ghost btn--icon" onClick={() => setView(new Date(year, month - 1, 1))}>
          <Icon.Arrow size={13} style={{ transform: "rotate(180deg)" }} />
        </button>
        <strong className="serif" style={{ fontSize: 20, textTransform: "capitalize" }}>{mname}</strong>
        <button className="btn btn--ghost btn--icon" onClick={() => setView(new Date(year, month + 1, 1))}>
          <Icon.Arrow size={13} />
        </button>
      </div>
      <div className="cal-grid">
        {["D","S","T","Q","Q","S","S"].map((d, i) => (
          <div key={i} className="cal-head dim">{d}</div>
        ))}
        {Array.from({ length: first }).map((_, i) => <div key={"b"+i} />)}
        {Array.from({ length: days }).map((_, i) => {
          const dnum = i + 1;
          const key  = `${year}-${String(month+1).padStart(2,"0")}-${String(dnum).padStart(2,"0")}`;
          const evs  = eventsByDate[key] || [];
          return (
            <div key={key} className={"cal-cell " + (isToday(dnum) ? "is-today" : "")}>
              <span className="mono" style={{ fontSize: 12, fontWeight: isToday(dnum) ? 600 : 400 }}>{dnum}</span>
              {evs.map((e, j) => (
                <div key={j} className="cal-ev" style={{ background: "var(--accent)" }}>{e.title}</div>
              ))}
            </div>
          );
        })}
      </div>
    </Modal>
  );
}

/* ── PERFIL ── */
function PerfilModal({ onClose, user, campaign, onSignOut }) {
  const [nome,    setNome]    = useState_m(user.nome || "");
  const [saving,  setSaving]  = useState_m(false);
  const [feedback, setFeedback] = useState_m(null); // { type: 'success'|'error', msg }

  const dirty = nome.trim() !== (user.nome || "").trim();

  const handleSave = async () => {
    if (!dirty || saving) return;
    setSaving(true);
    setFeedback(null);
    try {
      const { error } = await window.sb.auth.updateUser({ data: { full_name: nome.trim() } });
      if (error) throw error;
      setFeedback({ type: "success", msg: "Nome atualizado com sucesso." });
      // Reload page so app picks up new user metadata
      setTimeout(() => window.location.reload(), 1200);
    } catch (err) {
      setFeedback({ type: "error", msg: err.message || "Erro ao atualizar. Tente novamente." });
    } finally {
      setSaving(false);
    }
  };

  return (
    <Modal eyebrow="Conta" title="Informações do perfil" onClose={onClose} width={580}
      footer={<>
        <button className="btn btn--danger" onClick={() => { onClose(); onSignOut && onSignOut(); }}>
          <Icon.Arrow size={13} style={{ transform: "rotate(180deg)" }} /> Sair da conta
        </button>
        <span style={{ flex: 1 }} />
        {dirty ? (
          <button className="btn btn--primary" disabled={saving} onClick={handleSave}>
            {saving ? "Salvando…" : "Atualizar agora"}
          </button>
        ) : (
          <button className="btn" onClick={onClose}>Fechar</button>
        )}
      </>}>
      <div style={{ display: "flex", gap: 16, alignItems: "center", marginBottom: 22, paddingBottom: 18, borderBottom: "1px solid var(--line)" }}>
        <div className="avatar avatar--lg">{user.iniciais}</div>
        <div>
          <h4 style={{ margin: 0, fontSize: 18, fontWeight: 600 }}>{nome || user.nome}</h4>
          <div className="dim" style={{ fontSize: 13 }}>{user.email} · {campaign.role}</div>
        </div>
      </div>
      <div style={{ display: "grid", gap: 14 }}>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <Field label="Nome">
            <input
              className="input"
              value={nome}
              onChange={e => { setNome(e.target.value); setFeedback(null); }}
            />
          </Field>
          <Field label="E-mail">
            <input className="input" value={user.email} readOnly style={{ opacity: 0.6, cursor: "not-allowed" }} />
          </Field>
        </div>
        {feedback && (
          <div style={{
            padding: "10px 14px", borderRadius: 8, fontSize: 13, lineHeight: 1.5,
            background: feedback.type === "success" ? "var(--accent-soft)" : "oklch(0.97 0.02 20)",
            color: feedback.type === "success" ? "var(--accent-strong)" : "oklch(0.42 0.18 20)",
          }}>
            {feedback.msg}
          </div>
        )}
        <Field label="Notificações">
          <div style={{ display: "grid", gap: 8 }}>
            <label className="toggle"><input type="checkbox" defaultChecked /> Resumo diário por e-mail</label>
            <label className="toggle"><input type="checkbox" defaultChecked /> Alerta de meta atingida</label>
            <label className="toggle"><input type="checkbox" /> Push de novos apoiadores</label>
          </div>
        </Field>
      </div>
    </Modal>
  );
}

/* ── MUNICÍPIOS ── */
function MunicipiosModal({ onClose, campaign }) {
  const [municipios, setMunicipios] = useState_m([]);
  const [search,  setSearch]  = useState_m("");
  const [nome,    setNome]    = useState_m("");
  const [uf,      setUf]      = useState_m(campaign?.uf || "SE");
  const [code,    setCode]    = useState_m("");
  const [pop,     setPop]     = useState_m("");
  const [loading, setLoading] = useState_m(false);
  const [error,   setError]   = useState_m("");
  const [saved,   setSaved]   = useState_m(false);

  // Inline edit
  const [editingMunId, setEditingMunId] = useState_m(null);
  const [editNome,     setEditNome]     = useState_m("");
  const [editUf,       setEditUf]       = useState_m("");
  const [editCode,     setEditCode]     = useState_m("");
  const [editPop,      setEditPop]      = useState_m("");
  const [editSaving,   setEditSaving]   = useState_m(false);

  const startEdit = (m) => {
    setEditingMunId(m.id);
    setEditNome(m.name);
    setEditUf(m.state);
    setEditCode(m.ibge_code || "");
    setEditPop(m.population ? String(m.population) : "");
  };

  const handleEditSave = async () => {
    if (!editNome.trim()) return;
    setEditSaving(true);
    try {
      const updated = await window.API.updateMunicipality(editingMunId, {
        name: editNome, state: editUf, ibge_code: editCode || null, population: editPop || null,
      });
      setMunicipios(prev => prev.map(m => m.id === editingMunId ? updated : m).sort((a, b) => a.name.localeCompare(b.name, "pt-BR")));
      setEditingMunId(null);
    } catch (err) {
      alert("Erro ao atualizar: " + err.message);
    } finally {
      setEditSaving(false);
    }
  };

  // Import em massa
  const [importMode,    setImportMode]    = useState_m(false);
  const [importStep,    setImportStep]    = useState_m("select"); // select | preview | done
  const [importRows,    setImportRows]    = useState_m([]);
  const [importFile,    setImportFile]    = useState_m(null);
  const [importLoading, setImportLoading] = useState_m(false);
  const [importResults, setImportResults] = useState_m(null);
  const importFileRef = useRef_m(null);

  useEffect_m(() => {
    window.API.getMunicipalities().then(setMunicipios).catch(console.error);
  }, []);

  const filtered = municipios.filter(m =>
    m.name.toLowerCase().includes(search.toLowerCase()) ||
    m.state.toLowerCase().includes(search.toLowerCase()) ||
    (m.ibge_code || '').includes(search)
  );

  const handleAdd = async () => {
    if (!nome.trim()) return;
    setLoading(true); setError(""); setSaved(false);
    try {
      const novo = await window.API.createMunicipality({ name: nome, state: uf, ibge_code: code, population: pop });
      setMunicipios(prev => [...prev, novo].sort((a, b) => a.name.localeCompare(b.name, "pt-BR")));
      setNome(""); setCode(""); setPop("");
      setSaved(true);
      setTimeout(() => setSaved(false), 3000);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  // ── CSV import helpers ──
  const downloadTemplate = () => {
    const bom = '﻿';
    const csv = bom + "nome;uf;ibge_code;population\nAracaju;SE;2800308;664908\nLagarto;SE;2801500;100623\n";
    const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
    const url  = URL.createObjectURL(blob);
    const a    = document.createElement("a");
    a.href = url; a.download = "template_municipios.csv"; a.click();
    URL.revokeObjectURL(url);
  };

  const handleImportFile = (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    setImportFile(file.name);
    const reader = new FileReader();
    reader.onload = function(ev) {
      try {
        const text  = ev.target.result.replace(/^﻿/, '');
        const lines = text.trim().split(/\r?\n/);
        if (lines.length < 2) { alert("CSV vazio ou sem dados."); return; }

        const firstLine = lines[0];
        const nSemi  = (firstLine.match(/;/g) || []).length;
        const nComma = (firstLine.match(/,/g) || []).length;
        const delim  = nSemi >= nComma ? ';' : ',';

        const headers = firstLine.split(delim).map(h => h.trim().toLowerCase().replace(/"/g, ''));

        // Lookup existentes para calcular status no preview
        const existMap = {};
        municipios.forEach(function(m) {
          existMap[m.state + '|' + m.name.toLowerCase()] = m;
        });

        const rows = [];
        for (var i = 1; i < lines.length; i++) {
          var parts = lines[i].split(delim).map(v => v.trim().replace(/^"|"$/g, ''));
          var obj = {};
          headers.forEach(function(h, idx) { obj[h] = parts[idx] || ''; });

          var rowNome = (obj.nome || obj.name || '').trim();
          var rowUf   = (obj.uf   || obj.state || '').toUpperCase().trim() || 'SE';
          var rowCode = (obj.ibge_code || obj.codigo_ibge || obj.ibge || '').trim();
          var rowPop  = parseInt(obj.population || obj.populacao || '') || null;
          if (!rowNome) continue;

          var key     = rowUf + '|' + rowNome.toLowerCase();
          var existente = existMap[key] || null;
          var status;
          if (!existente) {
            status = "novo";
          } else if ((rowCode && !existente.ibge_code) || (rowPop && !existente.population)) {
            status = "atualizar";
          } else {
            status = "igual";
          }

          rows.push({ nome: rowNome, uf: rowUf, ibge_code: rowCode || null, population: rowPop, status });
        }

        if (rows.length === 0) {
          alert("Nenhuma linha válida. Verifique se há coluna 'nome' no cabeçalho.\nColunas encontradas: " + headers.join(', '));
          return;
        }
        setImportRows(rows);
        setImportStep("preview");
      } catch(err) {
        alert("Erro ao processar CSV: " + err.message);
      }
    };
    reader.readAsText(file, 'UTF-8');
    e.target.value = '';
  };

  const handleRunImport = async () => {
    const toProcess = importRows.filter(r => r.status !== 'igual');
    if (toProcess.length === 0) { setImportStep("done"); setImportResults({ criados: 0, atualizados: 0, ignorados: importRows.length }); return; }
    setImportLoading(true);
    var criados = 0, atualizados = 0, erros = [];
    for (var i = 0; i < importRows.length; i++) {
      var row = importRows[i];
      if (row.status === 'igual') continue;
      try {
        await window.API.createMunicipality({ name: row.nome, state: row.uf, ibge_code: row.ibge_code, population: row.population });
        if (row.status === 'novo') criados++; else atualizados++;
      } catch(err) {
        erros.push(row.nome + ': ' + err.message);
      }
    }
    // Recarrega lista atualizada
    try { const lista = await window.API.getMunicipalities(); setMunicipios(lista); } catch(_) {}
    setImportResults({ criados, atualizados, ignorados: importRows.filter(r => r.status === 'igual').length, erros });
    setImportLoading(false);
    setImportStep("done");
  };

  const resetImport = () => {
    setImportMode(false); setImportStep("select");
    setImportRows([]); setImportFile(null); setImportResults(null);
  };

  const STATUS_BADGE = {
    novo:      { label: "Novo",         color: "var(--accent)",  bg: "var(--accent-soft)" },
    atualizar: { label: "Atualizar dados", color: "oklch(0.55 0.16 70)", bg: "oklch(0.96 0.04 70)" },
    igual:     { label: "Já cadastrado",  color: "var(--dim)",   bg: "var(--surface-2)" },
  };

  return (
    <Modal eyebrow="Municípios" title="Gerenciar municípios" onClose={onClose} width={820}
      footer={
        importMode && importStep === "preview"
          ? <div className="row" style={{ justifyContent: "space-between", width: "100%" }}>
              <button className="btn" onClick={() => { setImportStep("select"); setImportRows([]); setImportFile(null); }}>
                ← Voltar
              </button>
              <div className="row" style={{ gap: 8 }}>
                <span className="dim" style={{ fontSize: 13, alignSelf: "center" }}>
                  {importRows.filter(r => r.status !== 'igual').length} para processar
                  {importRows.filter(r => r.status === 'igual').length > 0 && <> · {importRows.filter(r => r.status === 'igual').length} já cadastrados (serão ignorados)</>}
                </span>
                <button className="btn btn--primary" onClick={handleRunImport} disabled={importLoading || importRows.filter(r => r.status !== 'igual').length === 0}>
                  {importLoading ? "Importando…" : <><Icon.Download size={13} /> Importar {importRows.filter(r => r.status !== 'igual').length} município{importRows.filter(r => r.status !== 'igual').length !== 1 ? 's' : ''}</>}
                </button>
              </div>
            </div>
          : importMode && importStep === "done"
          ? <button className="btn btn--primary" onClick={resetImport}>Concluir</button>
          : <button className="btn btn--primary" onClick={onClose}>Fechar</button>
      }>

      {/* ── Tela de import ── */}
      {importMode ? (<>

        {importStep === "select" && (
          <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 20, padding: "32px 0" }}>
            <div style={{ textAlign: "center" }}>
              <div className="serif" style={{ fontSize: 22, marginBottom: 6 }}>Importar municípios via CSV</div>
              <p className="dim" style={{ margin: 0, maxWidth: 420 }}>
                Faça upload de um CSV com colunas <strong>nome, uf, ibge_code, population</strong>. Municípios já cadastrados não serão duplicados — apenas os dados ausentes serão preenchidos.
              </p>
            </div>
            <div className="row" style={{ gap: 10 }}>
              <button className="btn" onClick={downloadTemplate}>
                <Icon.Download size={13} /> Baixar modelo CSV
              </button>
              <button className="btn btn--primary" onClick={() => importFileRef.current && importFileRef.current.click()}>
                <Icon.Plus size={13} /> Selecionar arquivo
              </button>
            </div>
            <input ref={importFileRef} type="file" accept=".csv,.txt" style={{ display: "none" }} onChange={handleImportFile} />
            <button className="btn" style={{ marginTop: 8 }} onClick={resetImport}>← Voltar ao cadastro manual</button>
          </div>
        )}

        {importStep === "preview" && (
          <>
            <div className="row" style={{ gap: 12, marginBottom: 14, flexWrap: "wrap" }}>
              {Object.entries({ novo: "Novo", atualizar: "Atualizar dados", igual: "Já cadastrado" }).map(([k, l]) => {
                const count = importRows.filter(r => r.status === k).length;
                if (!count) return null;
                const s = STATUS_BADGE[k];
                return (
                  <div key={k} style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 13 }}>
                    <span style={{ background: s.bg, color: s.color, borderRadius: 6, padding: "2px 8px", fontWeight: 600, fontSize: 12 }}>{l}</span>
                    <span className="dim">{count} município{count !== 1 ? 's' : ''}</span>
                  </div>
                );
              })}
              <span className="dim" style={{ fontSize: 12, marginLeft: "auto", alignSelf: "center" }}>Arquivo: {importFile}</span>
            </div>
            <div style={{ maxHeight: 380, overflowY: "auto", borderRadius: 8, border: "1px solid var(--line)" }}>
              <table className="tbl">
                <thead>
                  <tr><th>Nome</th><th>UF</th><th>Cód. IBGE</th><th className="num">População</th><th>Status</th></tr>
                </thead>
                <tbody>
                  {importRows.map((r, i) => {
                    const s = STATUS_BADGE[r.status];
                    return (
                      <tr key={i} style={{ opacity: r.status === 'igual' ? 0.5 : 1 }}>
                        <td><strong>{r.nome}</strong></td>
                        <td className="mono dim">{r.uf}</td>
                        <td className="mono dim">{r.ibge_code || "—"}</td>
                        <td className="num mono">{r.population ? window.fmt(r.population) : "—"}</td>
                        <td><span style={{ background: s.bg, color: s.color, borderRadius: 6, padding: "2px 8px", fontSize: 11, fontWeight: 600 }}>{s.label}</span></td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </>
        )}

        {importStep === "done" && importResults && (
          <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 16, padding: "32px 0" }}>
            <div style={{ color: "var(--accent)", fontSize: 40 }}><Icon.Check size={48} /></div>
            <div className="serif" style={{ fontSize: 24 }}>Importação concluída</div>
            <div style={{ display: "flex", gap: 16 }}>
              {importResults.criados > 0 && (
                <div style={{ textAlign: "center", background: "var(--accent-soft)", borderRadius: 10, padding: "14px 22px" }}>
                  <div style={{ fontSize: 28, fontWeight: 700, color: "var(--accent)" }}>{importResults.criados}</div>
                  <div className="dim" style={{ fontSize: 12 }}>criado{importResults.criados !== 1 ? 's' : ''}</div>
                </div>
              )}
              {importResults.atualizados > 0 && (
                <div style={{ textAlign: "center", background: "oklch(0.96 0.04 70)", borderRadius: 10, padding: "14px 22px" }}>
                  <div style={{ fontSize: 28, fontWeight: 700, color: "oklch(0.55 0.16 70)" }}>{importResults.atualizados}</div>
                  <div className="dim" style={{ fontSize: 12 }}>atualizado{importResults.atualizados !== 1 ? 's' : ''}</div>
                </div>
              )}
              {importResults.ignorados > 0 && (
                <div style={{ textAlign: "center", background: "var(--surface-2)", borderRadius: 10, padding: "14px 22px" }}>
                  <div style={{ fontSize: 28, fontWeight: 700, color: "var(--dim)" }}>{importResults.ignorados}</div>
                  <div className="dim" style={{ fontSize: 12 }}>ignorado{importResults.ignorados !== 1 ? 's' : ''}</div>
                </div>
              )}
            </div>
            {importResults.erros && importResults.erros.length > 0 && (
              <div style={{ background: "oklch(0.97 0.02 30)", borderRadius: 8, padding: "10px 14px", maxWidth: 480, width: "100%", fontSize: 13 }}>
                <strong style={{ color: "oklch(0.55 0.18 30)" }}>Erros ({importResults.erros.length}):</strong>
                <ul style={{ margin: "6px 0 0", paddingLeft: 18, color: "var(--dim)" }}>
                  {importResults.erros.map((e, i) => <li key={i}>{e}</li>)}
                </ul>
              </div>
            )}
          </div>
        )}

      </>) : (<>

        {/* ── Formulário manual + botão importar ── */}
        <div style={{ background: "var(--surface-2)", borderRadius: 10, padding: "14px 16px", marginBottom: 20 }}>
          <div className="row" style={{ justifyContent: "space-between", marginBottom: 10 }}>
            <div className="eyebrow">Adicionar município</div>
            <button className="btn" style={{ fontSize: 12 }} onClick={() => setImportMode(true)}>
              <Icon.Download size={12} /> Importar CSV
            </button>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "2fr 80px 1fr 1fr auto", gap: 10, alignItems: "end" }}>
            <Field label="Nome">
              <input className="input" placeholder="Ex: Aracaju" value={nome}
                onChange={e => { setNome(e.target.value); setError(""); }} autoFocus />
            </Field>
            <Field label="UF">
              <input className="input" placeholder="SE" maxLength={2} value={uf}
                onChange={e => setUf(e.target.value.toUpperCase())} />
            </Field>
            <Field label="Cód. IBGE">
              <input className="input mono" placeholder="2800308" value={code}
                onChange={e => setCode(e.target.value)} />
            </Field>
            <Field label="População">
              <input className="input mono" type="number" placeholder="0" value={pop}
                onChange={e => setPop(e.target.value)} />
            </Field>
            <div style={{ paddingBottom: 0 }}>
              <button className="btn btn--primary" style={{ width: "100%" }}
                disabled={loading || !nome.trim()} onClick={handleAdd}>
                <Icon.Plus size={13} /> {loading ? "…" : "Adicionar"}
              </button>
            </div>
          </div>
          {error && <div style={{ color: "oklch(0.55 0.18 30)", fontSize: 13, marginTop: 8 }}>{error}</div>}
          {saved && <div style={{ color: "var(--accent)", fontSize: 13, marginTop: 8 }}>
            <Icon.Check size={13} /> Município adicionado com sucesso!
          </div>}
        </div>

        <div style={{ marginBottom: 10 }}>
          <input className="input" placeholder="Filtrar por nome, UF ou código IBGE…"
            value={search} onChange={e => setSearch(e.target.value)} />
        </div>
        <div style={{ fontSize: 12, color: "var(--dim)", marginBottom: 8 }}>
          {filtered.length} de {municipios.length} municípios
        </div>
        <div style={{ maxHeight: 320, overflowY: "auto", borderRadius: 8, border: "1px solid var(--line)" }}>
          <table className="tbl">
            <thead>
              <tr><th>Nome</th><th>UF</th><th>Cód. IBGE</th><th className="num">População</th><th></th></tr>
            </thead>
            <tbody>
              {filtered.map(m => m.id === editingMunId ? (
                <tr key={m.id} style={{ background: "var(--surface-2)" }}>
                  <td>
                    <input className="input" value={editNome} autoFocus
                      onChange={e => setEditNome(e.target.value)}
                      style={{ padding: "4px 8px", fontSize: 13 }} />
                  </td>
                  <td>
                    <input className="input mono" value={editUf} maxLength={2}
                      onChange={e => setEditUf(e.target.value.toUpperCase())}
                      style={{ padding: "4px 8px", fontSize: 13, width: 48 }} />
                  </td>
                  <td>
                    <input className="input mono" value={editCode}
                      onChange={e => setEditCode(e.target.value)}
                      style={{ padding: "4px 8px", fontSize: 13 }} />
                  </td>
                  <td>
                    <input className="input mono" type="number" value={editPop}
                      onChange={e => setEditPop(e.target.value)}
                      style={{ padding: "4px 8px", fontSize: 13 }} />
                  </td>
                  <td>
                    <div className="row" style={{ gap: 4 }}>
                      <button className="btn btn--primary btn--sm" disabled={editSaving || !editNome.trim()} onClick={handleEditSave}>
                        {editSaving ? "…" : <Icon.Check size={12} />}
                      </button>
                      <button className="btn btn--sm" onClick={() => setEditingMunId(null)}>
                        <Icon.X size={12} />
                      </button>
                    </div>
                  </td>
                </tr>
              ) : (
                <tr key={m.id}>
                  <td><strong>{m.name}</strong></td>
                  <td className="mono dim">{m.state}</td>
                  <td className="mono dim">{m.ibge_code || "—"}</td>
                  <td className="num mono">{m.population ? window.fmt(m.population) : "—"}</td>
                  <td>
                    <button className="btn btn--ghost btn--icon" onClick={() => startEdit(m)} title="Editar">
                      <Icon.Edit size={13} />
                    </button>
                  </td>
                </tr>
              ))}
              {filtered.length === 0 && (
                <tr><td colSpan={5} style={{ textAlign: "center", padding: 24 }} className="dim">Nenhum resultado.</td></tr>
              )}
            </tbody>
          </table>
        </div>

      </>)}
    </Modal>
  );
}

/* ── EDITAR CAMPANHA ── */
function EditarCampanhaModal({ onClose, campaign, onSave }) {
  const UFS = ["AC","AL","AP","AM","BA","CE","DF","ES","GO","MA","MT","MS","MG","PA","PB","PR","PE","PI","RJ","RN","RS","RO","RR","SC","SP","SE","TO"];

  const [tab,         setTab]        = useState_m("dados");
  const [candidato,   setCandidato]  = useState_m(campaign.candidato || "");
  const [numero,      setNumero]     = useState_m(campaign.numero !== "—" ? campaign.numero : "");
  const [coligacao,   setColigacao]  = useState_m(campaign.coligacao !== "Independente" ? campaign.coligacao : "");
  const [tipo,        setTipo]       = useState_m(campaign.tipo || "vereador");
  const [cidade,      setCidade]     = useState_m(campaign.cidade || "");
  const [uf,          setUf]         = useState_m(campaign.uf || "SE");
  const [eleicaoData, setEleicao]    = useState_m(campaign.eleicaoDataRaw || "");
  const [metaVotos,   setMeta]       = useState_m(campaign.metaVotos || 0);
  const [status,      setStatus]     = useState_m(campaign.status || "rascunho");
  const [loading,     setLoading]    = useState_m(false);
  const [erro,        setErro]       = useState_m("");

  // Photo
  const photoInputRef               = React.useRef(null);
  const [photoFile,    setPhotoFile]   = useState_m(null);
  const [photoPreview, setPhotoPreview] = useState_m(campaign.photoUrl || null);

  // Landing page texts
  const lc = campaign.landingConfig || {};
  const DEFAULT_PILLARS = [
    { title: "Saúde e educação de qualidade", text: "UBS abertas em todos os bairros e creche em tempo integral para as famílias." },
    { title: "Transparência total",           text: "Gabinete aberto e prestação de contas pública mensal para todos os cidadãos." },
    { title: "Geração de emprego",            text: "Incentivos para pequenos negócios e capacitação profissional gratuita." },
  ];
  const [heroTitle, setHeroTitle] = useState_m(lc.hero_title || "");
  const [heroLead,  setHeroLead]  = useState_m(lc.hero_lead  || "");
  const [pillars,   setPillars]   = useState_m(lc.pillars    || DEFAULT_PILLARS);

  function updatePillar(i, field, value) {
    setPillars(prev => prev.map((p, idx) => idx === i ? { ...p, [field]: value } : p));
  }

  const handleSave = async () => {
    setLoading(true);
    setErro("");
    try {
      if (photoFile) {
        var url = await window.API.uploadCandidatePhoto(campaign.id, photoFile);
        setPhotoPreview(url);
        setPhotoFile(null);
      }
      await onSave(campaign.id, {
        candidate_name: candidato,
        number:         numero        || null,
        coalition:      coligacao     || null,
        type:           tipo,
        city:           cidade        || null,
        state:          uf            || 'SE',
        election_date:  eleicaoData   || null,
        goal_votes:     parseInt(metaVotos) || 0,
        status,
        landing_config: {
          hero_title: heroTitle.trim() || null,
          hero_lead:  heroLead.trim()  || null,
          pillars,
        },
      });
      onClose();
    } catch (err) {
      setErro(err.message);
    } finally {
      setLoading(false);
    }
  };

  const tabStyle = (t) => ({
    padding: "8px 16px", fontSize: 13, fontWeight: 500, border: "none", cursor: "pointer",
    borderBottom: tab === t ? "2px solid var(--accent)" : "2px solid transparent",
    color: tab === t ? "var(--accent)" : "var(--muted)",
    background: "transparent",
  });

  const iniciais = candidato.split(" ").map(p => p[0]).slice(0, 2).join("").toUpperCase() || "?";

  return (
    <Modal eyebrow="Configurações" title="Editar campanha" onClose={onClose} width={820}
      footer={<>
        <button className="btn" onClick={onClose}>Cancelar</button>
        <button className="btn btn--primary" disabled={loading || !candidato.trim()} onClick={handleSave}>
          {loading ? "Salvando…" : "Salvar alterações"}
        </button>
      </>}>

      {/* Tabs */}
      <div style={{ display: "flex", gap: 0, borderBottom: "1px solid var(--line)", marginBottom: 20, marginTop: -4 }}>
        {[["dados", "Dados"], ["visual", "Visual"], ["landing", "Página pública"]].map(([k, l]) => (
          <button key={k} style={tabStyle(k)} onClick={() => setTab(k)}>{l}</button>
        ))}
      </div>

      {/* ── TAB: Dados ── */}
      {tab === "dados" && (
        <div style={{ display: "grid", gap: 16 }}>
          <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr 2fr", gap: 12 }}>
            <Field label="Nome do candidato">
              <input className="input" autoFocus value={candidato}
                onChange={e => { setCandidato(e.target.value); setErro(""); }} />
            </Field>
            <Field label="Número da urna">
              <input className="input mono" value={numero} onChange={e => setNumero(e.target.value)} />
            </Field>
            <Field label="Coligação / partido">
              <input className="input" value={coligacao} onChange={e => setColigacao(e.target.value)} />
            </Field>
          </div>

          <Field label="Tipo de campanha">
            <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 8 }}>
              {window.TIPOS_CAMPANHA.map(t => (
                <button key={t.id} className={"tipo-card " + (tipo === t.id ? "is-selected" : "")}
                  onClick={() => setTipo(t.id)}>
                  <div className="eyebrow">{t.esfera}</div>
                  <div className="serif" style={{ fontSize: 18, marginTop: 2 }}>{t.label}</div>
                  {tipo === t.id && <span className="tipo-card__check"><Icon.Check size={12} /></span>}
                </button>
              ))}
            </div>
          </Field>

          <div style={{ display: "grid", gridTemplateColumns: "2fr 80px 1fr", gap: 12 }}>
            <Field label="Cidade / Abrangência">
              <input className="input" value={cidade} onChange={e => setCidade(e.target.value)} />
            </Field>
            <Field label="UF">
              <select className="input" value={uf} onChange={e => setUf(e.target.value)}>
                {UFS.map(u => <option key={u}>{u}</option>)}
              </select>
            </Field>
            <Field label="Data da eleição">
              <input className="input" type="date" value={eleicaoData} onChange={e => setEleicao(e.target.value)} />
            </Field>
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <Field label="Meta de votos">
              <input className="input mono" type="number" min="0" value={metaVotos}
                onChange={e => setMeta(parseInt(e.target.value) || 0)} />
              <div className="help" style={{ fontSize: 11.5, color: "var(--muted)", marginTop: 4 }}>
                Meta de segurança (+30%): <strong>{window.fmt(Math.round((parseInt(metaVotos) || 0) * 1.3))}</strong> votos
              </div>
            </Field>
            <Field label="Status">
              <div style={{ marginTop: 4 }}>
                <SegRadio value={status} onChange={setStatus} options={[
                  { v: "rascunho",  l: "Rascunho" },
                  { v: "ativa",     l: "Ativa" },
                  { v: "encerrada", l: "Encerrada" },
                ]} />
              </div>
            </Field>
          </div>
        </div>
      )}

      {/* ── TAB: Visual ── */}
      {tab === "visual" && (
        <div style={{ display: "grid", gap: 24 }}>
          <Field label="Foto do candidato">
            <div style={{ display: "flex", alignItems: "center", gap: 20, marginTop: 8 }}>
              <div style={{
                width: 96, height: 96, borderRadius: "50%",
                background: photoPreview ? "none" : "var(--accent-soft)",
                border: "3px solid var(--line)", overflow: "hidden", flexShrink: 0,
                display: "flex", alignItems: "center", justifyContent: "center",
              }}>
                {photoPreview
                  ? <img src={photoPreview} alt="Foto" style={{ width: "100%", height: "100%", objectFit: "cover" }} />
                  : <span className="serif" style={{ fontSize: 28, color: "var(--accent-strong)" }}>{iniciais}</span>
                }
              </div>
              <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                <button className="btn" onClick={() => photoInputRef.current && photoInputRef.current.click()}>
                  <Icon.Camera size={14} /> {photoPreview ? "Trocar foto" : "Carregar foto"}
                </button>
                {photoPreview && (
                  <button className="btn" style={{ color: "oklch(0.55 0.18 30)" }}
                    onClick={() => { setPhotoPreview(null); setPhotoFile(null); }}>
                    Remover foto
                  </button>
                )}
                <span style={{ fontSize: 12, color: "var(--muted)" }}>
                  JPG ou PNG · máx. 5 MB · proporção quadrada recomendada
                </span>
              </div>
              <input ref={photoInputRef} type="file" accept="image/*" style={{ display: "none" }}
                onChange={e => {
                  var f = e.target.files[0];
                  if (!f) return;
                  setPhotoFile(f);
                  setPhotoPreview(URL.createObjectURL(f));
                  e.target.value = '';
                }} />
            </div>
          </Field>

          <div style={{ padding: "12px 16px", background: "var(--surface-2)", borderRadius: 8, fontSize: 13, color: "var(--muted)" }}>
            <strong style={{ color: "var(--ink-2)" }}>Banner da campanha</strong><br />
            O banner pode ser alterado diretamente no painel da campanha, clicando em "Alterar banner" no topo da tela.
          </div>
        </div>
      )}

      {/* ── TAB: Página pública ── */}
      {tab === "landing" && (
        <div style={{ display: "grid", gap: 20 }}>
          <div style={{ fontSize: 13, color: "var(--muted)", padding: "10px 14px", background: "var(--surface-2)", borderRadius: 8 }}>
            Estes textos aparecem na página pública de cadastro de apoiadores. Deixe em branco para usar o texto padrão gerado automaticamente.
          </div>

          <Field label="Título do hero">
            <input className="input" value={heroTitle}
              placeholder={`Sua voz conta pelo nosso ${cidade || "município"}.`}
              onChange={e => setHeroTitle(e.target.value)} />
          </Field>

          <Field label="Subtítulo / chamada">
            <textarea className="input" rows={3} value={heroLead}
              placeholder={`Cadastre-se em 30 segundos para apoiar a candidatura de ${candidato || "candidato"}.`}
              onChange={e => setHeroLead(e.target.value)}
              style={{ resize: "vertical", fontFamily: "inherit", lineHeight: 1.5 }} />
          </Field>

          <div>
            <div className="eyebrow" style={{ marginBottom: 10 }}>Prioridades da campanha</div>
            <div style={{ display: "grid", gap: 12 }}>
              {pillars.map((p, i) => (
                <div key={i} style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gap: 10, padding: "12px 14px", border: "1px solid var(--line)", borderRadius: 8 }}>
                  <label style={{ display: "block" }}>
                    <div style={{ fontSize: 11, color: "var(--muted)", marginBottom: 4 }}>Título #{i + 1}</div>
                    <input className="input" value={p.title} onChange={e => updatePillar(i, "title", e.target.value)} />
                  </label>
                  <label style={{ display: "block" }}>
                    <div style={{ fontSize: 11, color: "var(--muted)", marginBottom: 4 }}>Descrição #{i + 1}</div>
                    <input className="input" value={p.text} onChange={e => updatePillar(i, "text", e.target.value)} />
                  </label>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}

      {erro && (
        <div style={{ fontSize: 13, padding: "8px 12px", marginTop: 16, background: "oklch(0.97 0.02 30)", borderRadius: 6, color: "oklch(0.50 0.18 30)" }}>
          {erro}
        </div>
      )}
    </Modal>
  );
}

/* ── Helpers ── */
function Field({ label, children }) {
  return (
    <label style={{ display: "block" }}>
      <div className="eyebrow" style={{ marginBottom: 6 }}>{label}</div>
      {children}
    </label>
  );
}

function SegRadio({ value, onChange, options }) {
  return (
    <div className="seg">
      {options.map(o => (
        <button key={o.v} className={"seg__btn " + (value === o.v ? "is-active" : "")} onClick={() => onChange(o.v)}>{o.l}</button>
      ))}
    </div>
  );
}

/* ── Router ── */
function ModalsRoot({ kind, contexto, onClose, campaign, user, onSignOut }) {
  if (!kind) return null;
  if (kind === "exportar")           return <ExportarModal onClose={onClose} campaign={campaign} />;
  if (kind === "planejar-acao")      return <PlanejarAcaoModal onClose={onClose} campaign={campaign} />;
  if (kind === "nova-lideranca")     return <NovaLiderancaModal onClose={onClose} contexto={contexto} campaign={campaign} />;
  if (kind === "cadastrar-apoiador") return <CadastrarApoiadorModal onClose={onClose} campaign={campaign} />;
  if (kind === "relatorio-custom")   return <RelatorioCustomModal onClose={onClose} />;
  if (kind === "liderancas-regiao")  return <LiderancasRegiaoModal onClose={onClose} contexto={contexto} campaign={campaign} />;
  if (kind === "calendario")         return <CalendarioModal onClose={onClose} campaign={campaign} />;
  if (kind === "municipios")         return <MunicipiosModal onClose={onClose} campaign={campaign} />;
  if (kind === "perfil")             return <PerfilModal onClose={onClose} user={user} campaign={campaign} onSignOut={onSignOut} />;
  return null;
}

Object.assign(window, { ModalsRoot, EditarCampanhaModal });
