// Mods Matrix — два таби: матриця версій і редактор по серверу

const { useLang } = window.CIE_I18N;

// ── CSS animations (inject once) ─────────────────────────────────────────────
;(function injectModsAnim() {
  if (document.getElementById('cie-mods-anim')) return;
  const s = document.createElement('style');
  s.id = 'cie-mods-anim';
  s.textContent = `
    @keyframes cie-spin     { to { transform: rotate(360deg); } }
    @keyframes cie-pop      { 0%{transform:scale(0);opacity:0} 65%{transform:scale(1.3)} 100%{transform:scale(1);opacity:1} }
    @keyframes cie-item-in  { from{opacity:0;transform:scale(0.96)} to{opacity:1;transform:scale(1)} }
    @keyframes cie-item-out { to{opacity:0;transform:scale(0.94)} }
    @keyframes cie-slide-up { from{opacity:0;transform:translateX(-50%) scale(0.96)} to{opacity:1;transform:translateX(-50%) scale(1)} }
    @keyframes cie-fade-in  { from{opacity:0} to{opacity:1} }
    @keyframes cie-pulse-glow {
      0%,100% { box-shadow:0 4px 24px rgba(0,0,0,0.45),0 0 0 0 rgba(80,150,255,0.3); }
      50%     { box-shadow:0 6px 30px rgba(0,0,0,0.55),0 0 0 6px rgba(80,150,255,0); }
    }
    .cie-staged-panel { animation: cie-slide-up 0.38s cubic-bezier(0.34,1.56,0.64,1); }
    .cie-item-removing { animation: cie-item-out 0.18s ease forwards !important; pointer-events:none; }
    .cie-staged-item { animation: cie-item-in 0.22s ease both; }
  `;
  document.head.appendChild(s);
})();

// ── Допоміжні компоненти ─────────────────────────────────────────────────────

function ProgressLog({ progress }) {
  if (!progress || Object.keys(progress).length === 0) return null;
  return (
    <div style={{display:'flex', flexDirection:'column', gap:5, fontFamily:'var(--mono)', fontSize:11}}>
      {Object.entries(progress).map(([sid, status], i) => (
        <div key={sid} style={{
          display:'flex', alignItems:'center', gap:8,
          animation: `cie-fade-in 0.2s ease ${i * 30}ms both`,
        }}>
          {/* key=status → remount on change → animation re-triggers */}
          <span key={status} style={{
            display:'inline-block', width:13, textAlign:'center',
            color: status==='ok' ? 'var(--ok)' : status==='error' ? 'var(--err)' : status==='saving' ? 'var(--accent)' : 'var(--text-faint)',
            animation: status==='saving'                   ? 'cie-spin 0.7s linear infinite'
                     : status==='ok' || status==='error'   ? 'cie-pop 0.35s cubic-bezier(0.34,1.56,0.64,1)'
                     : 'none',
            transition: 'color 0.2s ease',
          }}>
            {status==='ok' ? '✓' : status==='error' ? '✗' : status==='saving' ? '↻' : '·'}
          </span>
          <span style={{
            color: status==='pending' ? 'var(--text-faint)' : 'var(--text)',
            transition: 'color 0.25s ease',
          }}>
            {sid}
          </span>
        </div>
      ))}
    </div>
  );
}

function AddModModal({ onClose, onAdd, existingMods = [] }) {
  const { t } = useLang();
  const [urlInput, setUrlInput] = useState('');
  const [looking, setLooking]   = useState(false);
  const [form, setForm] = useState({ modId: '', name: '', version: '' });
  const [err, setErr]   = useState('');
  const set = (k, v) => setForm(p => ({ ...p, [k]: v }));

  const handleUrlChange = (val) => {
    setUrlInput(val);
    setErr('');
    // Якщо схоже на URL або слаг — запускаємо lookup
    if (val.includes('reforger.armaplatform.com') || /[0-9A-Fa-f]{8,}-\S+/.test(val.trim())) {
      setLooking(true);
      CIE_API.lookupMod(val.trim())
        .then(res => {
          if (res.mod_id) {
            setForm(p => ({ ...p, modId: res.mod_id, name: res.name || p.name }));
          } else {
            setErr(res.error || t('mods.lookup_failed'));
          }
        })
        .catch(() => setErr(t('mods.lookup_failed')))
        .finally(() => setLooking(false));
    }
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!form.modId.trim() || !form.name.trim()) {
      setErr(t('settings.required'));
      return;
    }
    if (existingMods.some(m => m.modId === form.modId.trim())) {
      setErr(t('mods.already_exists', { id: form.modId.trim() }));
      return;
    }
    onAdd({ modId: form.modId.trim(), name: form.name.trim(), version: form.version.trim() });
    onClose();
  };

  return (
    <div style={{
      position:'fixed', inset:0, background:'rgba(0,0,0,0.6)',
      display:'flex', alignItems:'center', justifyContent:'center', zIndex:100,
    }} onClick={onClose}>
      <div style={{
        background:'var(--surface-1)', border:'1px solid var(--line)',
        borderRadius:6, padding:28, width:440, maxWidth:'95vw',
      }} onClick={e => e.stopPropagation()}>
        <h3 style={{marginBottom:20}}>{t('mods.add_mod_title')}</h3>
        <form onSubmit={handleSubmit} style={{display:'flex', flexDirection:'column', gap:12}}>

          {/* URL вставка */}
          <div style={{
            background:'var(--surface-2)', border:'1px solid var(--line)',
            borderRadius:4, padding:'10px 12px',
          }}>
            <div className="label" style={{marginBottom:6, textTransform:'uppercase', letterSpacing:'0.08em', fontSize:10}}>
              {t('mods.field_url')}
            </div>
            <input
              value={urlInput}
              onChange={e => handleUrlChange(e.target.value)}
              placeholder="https://reforger.armaplatform.com/workshop/…"
              autoFocus
              style={{
                width:'100%', fontFamily:'var(--mono)', fontSize:11,
                background:'transparent', color:'var(--text)',
                borderBottom:'1px solid var(--border)',
                paddingBottom:4,
              }}
            />
            {looking && (
              <div style={{fontFamily:'var(--mono)', fontSize:10, color:'var(--text-mute)', marginTop:6}}>
                ⟳ {t('mods.looking_up')}
              </div>
            )}
            {!looking && form.modId && (
              <div style={{fontFamily:'var(--mono)', fontSize:10, color:'var(--ok)', marginTop:6}}>
                ✓ {form.modId}
              </div>
            )}
          </div>

          <div className="field">
            <span className="lbl">{t('mods.field_name')}<span className="req">*</span></span>
            <span className="ctl"><input value={form.name} onChange={e => set('name', e.target.value)} placeholder="ACE3" /></span>
          </div>
          <div className="field">
            <span className="lbl">{t('mods.field_workshop')}</span>
            <span className="ctl"><input value={form.modId} onChange={e => set('modId', e.target.value)} placeholder="5CA125536EE7430A" /></span>
          </div>
          <div className="field">
            <span className="lbl">{t('mods.field_version')}<span className="desc">{t('mods.version_placeholder')}</span></span>
            <span className="ctl"><input value={form.version} onChange={e => set('version', e.target.value)} placeholder={t('mods.version_placeholder')} /></span>
          </div>

          {err && <div style={{fontFamily:'var(--mono)', fontSize:11, color:'var(--err)'}}>{err}</div>}
          <div style={{display:'flex', gap:8, marginTop:4}}>
            <button className="btn primary sm" type="submit" disabled={looking}>{t('mods.add_mod_title')}</button>
            <button className="btn ghost sm" type="button" onClick={onClose}>{t('cancel')}</button>
          </div>
        </form>
      </div>
    </div>
  );
}

// ── Tab 1: Матриця ────────────────────────────────────────────────────────────

function MatrixTab({ matrix, onMatrixUpdate }) {
  const { t } = useLang();

  const [filterDiff, setFilterDiff] = useState(false);
  const [search, setSearch]         = useState('');
  const [selectedMod, setSelectedMod]     = useState(null);
  const [targetVersion, setTargetVersion] = useState('');
  const [checkedServers, setCheckedServers] = useState(new Set());
  const [staged, setStaged]             = useState({}); // mod_id → {type,mod_id,name,version,server_ids}
  const [removingMods, setRemovingMods] = useState(new Set());
  const [publishing, setPublishing]     = useState(false);
  const [publishProgress, setPublishProgress] = useState({}); // server_id → 'pending'|'saving'|'ok'|'error'

  const servers = matrix?.servers || [];
  const diffCount = matrix ? matrix.mods.filter(m => !m.in_sync).length : 0;

  const [visibleServers, setVisibleServers] = useState(null); // null = всі
  const shownServers = useMemo(
    () => visibleServers === null ? servers : servers.filter(s => visibleServers.has(s)),
    [servers, visibleServers]
  );
  const toggleServerCol = sid => {
    setVisibleServers(prev => {
      const next = new Set(prev === null ? servers : prev);
      next.has(sid) ? next.delete(sid) : next.add(sid);
      return next.size === servers.length ? null : next;
    });
  };

  const visibleMods = useMemo(() => {
    if (!matrix) return [];
    return matrix.mods.filter(m => {
      if (!shownServers.some(sid => m.versions[sid] !== undefined)) return false;
      if (filterDiff && m.in_sync) return false;
      if (search && !m.name.toLowerCase().includes(search.toLowerCase()) && !m.mod_id.includes(search)) return false;
      return true;
    });
  }, [matrix, search, filterDiff, shownServers]);

  const selectMod = (mod) => {
    if (selectedMod?.mod_id === mod.mod_id) { setSelectedMod(null); return; }
    setSelectedMod(mod);
    setTargetVersion(mod.consensus || '');
    setSyncProgress({});
    setRemoveProgress({});
    // Пре-вибираємо сервери де версія відрізняється або мод відсутній
    const outOfSync = servers.filter(sid => {
      const v = mod.versions[sid];
      return v === undefined || (v !== '' && v !== mod.consensus);
    });
    setCheckedServers(new Set(outOfSync.length > 0 ? outOfSync : servers));
  };

  const toggleServer = (sid) => setCheckedServers(prev => {
    const next = new Set(prev);
    next.has(sid) ? next.delete(sid) : next.add(sid);
    return next;
  });

  const handleStage = () => {
    if (!selectedMod || checkedServers.size === 0) return;
    const ver = targetVersion.trim();
    setStaged(prev => ({
      ...prev,
      [selectedMod.mod_id]: {
        type: 'sync', mod_id: selectedMod.mod_id,
        name: selectedMod.name, version: ver,
        server_ids: [...checkedServers],
      },
    }));
    setPublishProgress({});
  };

  const handleStageRemove = () => {
    if (!selectedMod || checkedServers.size === 0) return;
    if (!window.confirm(`Remove "${selectedMod.name}" from ${checkedServers.size} server(s)?`)) return;
    setStaged(prev => ({
      ...prev,
      [selectedMod.mod_id]: {
        type: 'remove', mod_id: selectedMod.mod_id,
        name: selectedMod.name, server_ids: [...checkedServers],
      },
    }));
    setPublishProgress({});
  };

  const handleUnstage = (mod_id) => {
    setRemovingMods(prev => new Set([...prev, mod_id]));
    setTimeout(() => {
      setStaged(prev => { const n = {...prev}; delete n[mod_id]; return n; });
      setRemovingMods(prev => { const n = new Set(prev); n.delete(mod_id); return n; });
    }, 200);
  };

  const handlePublish = async () => {
    const changes = Object.values(staged);
    if (changes.length === 0) return;

    // Унікальні сервери, відсортовані
    const allServers = [...new Set(changes.flatMap(c => c.server_ids))].sort();
    setPublishing(true);
    setPublishProgress(Object.fromEntries(allServers.map(s => [s, 'pending'])));

    const allUpdated = new Set();
    const allErrors  = {};

    // Per-server loop — після кожного сервера UI оновлюється одразу
    for (const sid of allServers) {
      setPublishProgress(p => ({ ...p, [sid]: 'saving' }));

      // Всі зміни для цього сервера, server_ids=[sid]
      const changesForServer = changes
        .filter(ch => ch.server_ids.includes(sid))
        .map(ch => ({ ...ch, server_ids: [sid] }));

      let lastErr = null;
      for (let attempt = 0; attempt < 2; attempt++) {
        if (attempt > 0) await new Promise(r => setTimeout(r, 1500));
        try {
          const result = await CIE_API.publishMods({ changes: changesForServer });
          if (result.updated?.includes(sid)) {
            allUpdated.add(sid);
            setPublishProgress(p => ({ ...p, [sid]: 'ok' }));
            lastErr = null;
            break;
          } else {
            lastErr = result.errors?.[sid] || 'unknown';
          }
        } catch (e) {
          lastErr = e.message;
        }
      }
      if (lastErr !== null) {
        allErrors[sid] = lastErr;
        setPublishProgress(p => ({ ...p, [sid]: 'error' }));
      }
    }

    // Оновлюємо матрицю одноразово після всіх серверів
    onMatrixUpdate(prev => {
      let mods = [...prev.mods];
      changes.forEach(ch => {
        mods = mods.map(m => {
          if (m.mod_id !== ch.mod_id) return m;
          const v = { ...m.versions };
          if (ch.type === 'remove') {
            ch.server_ids.filter(s => allUpdated.has(s)).forEach(s => { delete v[s]; });
          } else {
            ch.server_ids.filter(s => allUpdated.has(s)).forEach(s => { v[s] = ch.version; });
          }
          const vals = Object.values(v);
          const counts = vals.reduce((a, x) => { a[x] = (a[x]||0)+1; return a; }, {});
          const consensus = Object.entries(counts).sort((a,b)=>b[1]-a[1])[0]?.[0] || '';
          return { ...m, versions: v, consensus, in_sync: vals.every(x=>x===consensus) && vals.length===prev.servers.length };
        }).filter(m => Object.keys(m.versions).length > 0);
      });
      return { ...prev, mods };
    });

    if (Object.keys(allErrors).length === 0) setStaged({});
    setPublishing(false);
  };

  // ── Render ──

  const installedServers = selectedMod ? servers.filter(sid => selectedMod.versions[sid] !== undefined) : [];
  const missingServers   = selectedMod ? servers.filter(sid => selectedMod.versions[sid] === undefined) : [];

  return (
    <div style={{display:'flex', flex:1, minHeight:0, overflow:'hidden', position:'relative'}}>

      {/* Таблиця */}
      <div style={{flex:1, display:'flex', flexDirection:'column', minWidth:0, overflow:'hidden'}}>
        {/* Filterbar */}
        <div className="filterbar" style={{marginBottom:0, flexShrink:0, padding:'10px 0 12px'}}>
          <div className="group">
            <div className={`seg ${filterDiff?'on':''}`} onClick={() => setFilterDiff(v=>!v)}>
              {t('mods.filter_diff')}{diffCount > 0 && <span className="count">{diffCount}</span>}
            </div>
          </div>
          <div className="search">
            <span className="ico">⌕</span>
            <input value={search} onChange={e => setSearch(e.target.value)} placeholder={t('mods.search')} />
          </div>
        </div>

        {/* Server chips */}
        {servers.length > 0 && (
          <div style={{
            display:'flex', flexWrap:'wrap', alignItems:'center', gap:5,
            padding:'7px 0 8px', borderBottom:'1px solid var(--border)', flexShrink:0,
          }}>
            <span style={{fontFamily:'var(--mono)', fontSize:9.5, color:'var(--text-faint)', letterSpacing:'0.12em', textTransform:'uppercase', marginRight:2}}>
              {t('servers')}
            </span>
            {servers.map(sid => {
              const active = visibleServers === null || visibleServers.has(sid);
              return (
                <span key={sid} onClick={() => toggleServerCol(sid)} style={{
                  fontFamily:'var(--mono)', fontSize:10, letterSpacing:'0.04em',
                  padding:'2px 8px', borderRadius:3, cursor:'pointer',
                  border:'1px solid ' + (active ? 'var(--accent)' : 'var(--border)'),
                  background: active ? 'var(--accent-glow)' : 'transparent',
                  color: active ? 'var(--accent)' : 'var(--text-mute)',
                  opacity: active ? 1 : 0.4,
                  textDecoration: active ? 'none' : 'line-through',
                  userSelect:'none',
                  transition: 'all 0.2s ease',
                }}>{sid}</span>
              );
            })}
            {visibleServers !== null && (
              <span onClick={() => setVisibleServers(null)} style={{
                fontFamily:'var(--mono)', fontSize:9.5, color:'var(--text-faint)',
                cursor:'pointer', marginLeft:2, textDecoration:'underline',
              }}>{t('mods.select_all')}</span>
            )}
          </div>
        )}

        <div style={{flex:1, overflowX:'auto', overflowY:'auto'}}>
          <table className="tbl" style={{marginTop:0, width:'auto', minWidth: 330 + shownServers.length * 90}}>
            <thead>
              <tr>
                <th style={{width:200, minWidth:200, maxWidth:200, position:'sticky', left:0, background:'var(--surface)', zIndex:2}}>{t('mods.col_mod')}</th>
                <th style={{width:160, minWidth:160, maxWidth:160}}>{t('mods.col_id')}</th>
                {shownServers.map(sid => (
                  <th key={sid} style={{width:90, textAlign:'center', fontSize:10, letterSpacing:'0.04em'}}>{sid}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {visibleMods.length === 0 && (
                <tr><td colSpan={2+servers.length} className="empty" style={{padding:32}}>{t('mods.no_mods')}</td></tr>
              )}
              {visibleMods.map(mod => {
                const isSelected = selectedMod?.mod_id === mod.mod_id;
                return (
                  <tr key={mod.mod_id} onClick={() => selectMod(mod)}
                    style={{cursor:'pointer', background: isSelected ? 'var(--surface-2)' : undefined}}>
                    <td style={{
                      fontWeight:500, position:'sticky', left:0, zIndex:1,
                      background: isSelected ? 'var(--surface-2)' : 'var(--surface)',
                      borderRight:'1px solid var(--line)',
                    }}>
                      <div style={{display:'flex', alignItems:'center', gap:8}}>
                        {!mod.in_sync && <span style={{width:6,height:6,borderRadius:'50%',background:'var(--warn)',flexShrink:0}} />}
                        {mod.name}
                      </div>
                    </td>
                    <td className="mono dim" style={{fontSize:10.5}}>{mod.mod_id}</td>
                    {shownServers.map(sid => {
                      // undefined = не встановлено, "" = авто/latest, "x.y.z" = конкретна версія
                      const ver        = mod.versions[sid];
                      const notInstalled = ver === undefined;
                      const isAuto       = !notInstalled && (ver === '' || ver === null);
                      const outdated     = !notInstalled && !isAuto && ver !== mod.consensus;

                      return (
                        <td key={sid} onClick={e => {
                          e.stopPropagation();
                          if (selectedMod?.mod_id !== mod.mod_id) {
                            setSelectedMod(mod);
                            setTargetVersion(mod.consensus || '');
                          }
                          setCheckedServers(new Set([sid]));
                        }} style={{
                          textAlign:'center', padding:'6px 4px',
                          transition: 'background 0.25s ease',
                          background: notInstalled ? 'rgba(255,70,70,0.10)'
                                    : isAuto       ? 'rgba(80,160,255,0.08)'
                                    : outdated     ? 'rgba(255,165,0,0.12)'
                                    :                'rgba(60,200,100,0.07)',
                        }}>
                          {notInstalled ? (
                            <span style={{
                              display:'inline-block',
                              width:18, height:18, lineHeight:'18px',
                              borderRadius:3,
                              background:'rgba(255,70,70,0.18)',
                              color:'rgba(255,100,100,0.7)',
                              fontSize:12, fontWeight:700,
                            }}>✕</span>
                          ) : isAuto ? (
                            <span style={{
                              fontFamily:'var(--mono)', fontSize:10,
                              color:'rgba(100,170,255,0.85)',
                              letterSpacing:'0.02em',
                            }}>auto</span>
                          ) : (
                            <span style={{
                              fontFamily:'var(--mono)', fontSize:10.5,
                              color: outdated ? 'var(--warn)' : 'rgba(80,220,120,0.9)',
                            }}>{ver}</span>
                          )}
                        </td>
                      );
                    })}
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      {/* Панель оновлення */}
      {selectedMod && (
        <div style={{
          width:300, flexShrink:0,
          borderLeft:'1px solid var(--line)',
          background:'var(--surface-1)',
          display:'flex', flexDirection:'column',
          overflow:'hidden',
        }}>
          {/* Заголовок */}
          <div style={{padding:'16px 18px', borderBottom:'1px solid var(--line)', flexShrink:0}}>
            <div style={{fontWeight:600, marginBottom:2}}>{selectedMod.name}</div>
            <div className="mono dim" style={{fontSize:10.5}}>{selectedMod.mod_id}</div>
          </div>

          {/* Версія + прокручуваний список серверів */}
          <div style={{flex:1, minHeight:0, overflowY:'auto', padding:'14px 18px', display:'flex', flexDirection:'column', gap:14}}>

            {/* Версія */}
            <div style={{flexShrink:0}}>
              <div style={{display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:6}}>
                <div className="label" style={{textTransform:'uppercase', letterSpacing:'0.08em'}}>
                  {t('mods.target_version')}
                </div>
                <button
                  className="btn ghost sm"
                  style={{
                    fontSize:10, padding:'1px 8px',
                    color: targetVersion === '' ? 'rgba(100,170,255,0.9)' : 'var(--text-faint)',
                    borderColor: targetVersion === '' ? 'rgba(80,140,255,0.4)' : 'transparent',
                  }}
                  onClick={() => setTargetVersion('')}
                >auto</button>
              </div>
              <input
                value={targetVersion}
                onChange={e => setTargetVersion(e.target.value)}
                placeholder={t('mods.version_placeholder')}
                style={{
                  width:'100%', fontFamily:'var(--mono)', fontSize:13,
                  background:'var(--surface-2)', border:'1px solid var(--line)',
                  borderRadius:4, padding:'6px 10px', color:'var(--text)',
                  boxSizing:'border-box',
                  fontStyle: targetVersion === '' ? 'italic' : 'normal',
                  color: targetVersion === '' ? 'rgba(100,170,255,0.7)' : 'var(--text)',
                }}
              />
            </div>

            {/* Select all / none */}
            {(installedServers.length + missingServers.length) > 0 && (
              <div style={{flexShrink:0, display:'flex', justifyContent:'flex-end'}}>
                {(() => {
                  const all = [...installedServers, ...missingServers];
                  const allChecked = all.length > 0 && all.every(s => checkedServers.has(s));
                  return (
                    <button className="btn ghost sm" style={{fontSize:10, padding:'1px 8px'}}
                      onClick={() => setCheckedServers(allChecked ? new Set() : new Set(all))}>
                      {allChecked ? t('mods.select_none') : t('mods.select_all')}
                    </button>
                  );
                })()}
              </div>
            )}

            {/* Сервери де встановлено */}
            {installedServers.length > 0 && (
              <div style={{flexShrink:0}}>
                <div className="label" style={{marginBottom:6, textTransform:'uppercase', letterSpacing:'0.08em', color:'var(--text-dim)'}}>
                  {t('mods.installed')} ({installedServers.length})
                </div>
                <div style={{display:'flex', flexDirection:'column', gap:3}}>
                  {installedServers.map(sid => {
                    const ver    = selectedMod.versions[sid];
                    const isAuto = ver === '' || ver === null;
                    const outdated = !isAuto && ver !== selectedMod.consensus;
                    return (
                      <label key={sid} style={{display:'flex', alignItems:'center', gap:8, cursor:'pointer', padding:'3px 6px', borderRadius:3, background: checkedServers.has(sid)?'var(--surface-2)':'transparent'}}>
                        <input type="checkbox" checked={checkedServers.has(sid)} onChange={() => toggleServer(sid)} style={{accentColor:'var(--accent)'}} />
                        <span style={{flex:1, fontFamily:'var(--mono)', fontSize:11}}>{sid}</span>
                        <span style={{fontSize:10, fontFamily:'var(--mono)',
                          color: isAuto ? 'rgba(100,170,255,0.8)' : outdated ? 'var(--warn)' : 'var(--text-faint)',
                        }}>
                          {isAuto ? 'auto' : ver}
                        </span>
                      </label>
                    );
                  })}
                </div>
              </div>
            )}

            {/* Сервери де відсутній */}
            {missingServers.length > 0 && (
              <div style={{flexShrink:0}}>
                <div className="label" style={{marginBottom:6, textTransform:'uppercase', letterSpacing:'0.08em', color:'var(--text-faint)'}}>
                  {t('mods.missing')} ({missingServers.length})
                </div>
                <div style={{display:'flex', flexDirection:'column', gap:3}}>
                  {missingServers.map(sid => (
                    <label key={sid} style={{display:'flex', alignItems:'center', gap:8, cursor:'pointer', padding:'3px 6px', borderRadius:3, background: checkedServers.has(sid)?'var(--surface-2)':'transparent'}}>
                      <input type="checkbox" checked={checkedServers.has(sid)} onChange={() => toggleServer(sid)} style={{accentColor:'var(--accent)'}} />
                      <span style={{flex:1, fontFamily:'var(--mono)', fontSize:11, color:'var(--text-mute)'}}>{sid}</span>
                      <span style={{fontSize:10, color:'var(--text-faint)'}}>—</span>
                    </label>
                  ))}
                </div>
              </div>
            )}

            {/* Staged indicator для поточного моду */}
            {staged[selectedMod?.mod_id] && (
              <div style={{fontSize:11, fontFamily:'var(--mono)', color:'var(--warn)', display:'flex', alignItems:'center', gap:6}}>
                <span>● staged</span>
                <span style={{color:'var(--text-faint)'}}>
                  {staged[selectedMod.mod_id].type === 'remove' ? 'remove' : `→ "${staged[selectedMod.mod_id].version || 'auto'}"` }
                </span>
              </div>
            )}
          </div>

          {/* Кнопки — завжди знизу */}
          <div style={{padding:'12px 18px', borderTop:'1px solid var(--line)', flexShrink:0, display:'flex', flexDirection:'column', gap:6}}>
            <button
              className="btn primary sm"
              onClick={handleStage}
              disabled={publishing || checkedServers.size === 0}
              style={{justifyContent:'center'}}
            >
              {t('mods.stage_btn')}
            </button>
            <button
              className="btn danger sm"
              onClick={handleStageRemove}
              disabled={publishing || checkedServers.size === 0 || installedServers.filter(s=>checkedServers.has(s)).length === 0}
              style={{justifyContent:'center'}}
            >
              {t('mods.stage_remove_btn')}
            </button>
          </div>
        </div>
      )}

      {/* Staged changes + Publish панель */}
      {Object.keys(staged).length > 0 && (
        <div className="cie-staged-panel" style={{
          position:'absolute', bottom:16, left:'50%', transform:'translateX(-50%)',
          background:'var(--surface)', border:'1px solid var(--accent)',
          borderRadius:8,
          animation: publishing ? 'cie-pulse-glow 2s ease infinite' : undefined,
          boxShadow:'0 4px 24px rgba(0,0,0,0.45)',
          padding:'12px 16px', minWidth:330, maxWidth:540, zIndex:50,
        }}>
          <div style={{display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:10}}>
            <div style={{fontWeight:600, fontSize:12, letterSpacing:'0.03em'}}>
              {t('mods.staged_title', { n: Object.keys(staged).length })}
            </div>
            <button
              className="btn ghost sm"
              onClick={() => setStaged({})}
              style={{padding:'1px 8px', fontSize:11, opacity: publishing ? 0.4 : 1}}
              disabled={publishing}
            >
              {t('mods.staged_clear')}
            </button>
          </div>

          <div style={{display:'flex', flexDirection:'column', gap:2, marginBottom:10, maxHeight:140, overflowY:'auto'}}>
            {Object.values(staged).map((ch, i) => (
              <div
                key={ch.mod_id}
                className={`cie-staged-item${removingMods.has(ch.mod_id) ? ' cie-item-removing' : ''}`}
                style={{
                  display:'flex', alignItems:'center', gap:8,
                  fontSize:11, fontFamily:'var(--mono)',
                  padding:'3px 4px', borderRadius:3,
                  animationDelay: `${i * 40}ms`,
                  background: removingMods.has(ch.mod_id) ? 'transparent' : undefined,
                  transition: 'background 0.15s ease',
                }}
              >
                <span style={{
                  color: ch.type==='remove' ? 'var(--err)' : 'var(--ok)',
                  flexShrink:0, fontSize:12,
                }}>{ch.type==='remove' ? '✗' : '✓'}</span>
                <span style={{flex:1, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>
                  {ch.name}
                </span>
                <span style={{color:'var(--text-faint)', flexShrink:0}}>
                  {ch.type==='remove' ? 'remove' : (ch.version || 'auto')}
                </span>
                <span style={{
                  color:'var(--accent)', fontSize:10, flexShrink:0,
                  background:'rgba(80,140,255,0.1)', borderRadius:3, padding:'1px 5px',
                }}>
                  {ch.server_ids.length}s
                </span>
                <button
                  onClick={() => handleUnstage(ch.mod_id)}
                  disabled={publishing}
                  style={{
                    background:'none', border:'none', color:'var(--text-faint)',
                    cursor:'pointer', padding:'0 2px', fontSize:14,
                    lineHeight:1, flexShrink:0,
                    opacity: publishing ? 0.3 : 1,
                    transition: 'color 0.15s ease, opacity 0.15s ease',
                  }}
                  onMouseEnter={e => e.currentTarget.style.color='var(--err)'}
                  onMouseLeave={e => e.currentTarget.style.color='var(--text-faint)'}
                >×</button>
              </div>
            ))}
          </div>

          {Object.keys(publishProgress).length > 0 && (
            <div style={{
              marginBottom:10, padding:'8px 10px',
              background:'var(--surface-2)', borderRadius:5,
              border:'1px solid var(--line)',
              animation: 'cie-fade-in 0.2s ease',
            }}>
              <ProgressLog progress={publishProgress} />
            </div>
          )}

          <button
            className="btn primary sm"
            onClick={handlePublish}
            disabled={publishing}
            style={{
              width:'100%', justifyContent:'center',
              transition: 'opacity 0.2s ease',
            }}
          >
            {publishing
              ? <span style={{display:'flex', alignItems:'center', gap:6}}>
                  <span style={{display:'inline-block', animation:'cie-spin 0.7s linear infinite'}}>↻</span>
                  {t('mods.publishing')}
                </span>
              : t('mods.publish_btn', { n: Object.keys(staged).length })
            }
          </button>
        </div>
      )}
    </div>
  );
}

// ── Tab 2: По серверу ─────────────────────────────────────────────────────────

function ServerTab({ servers: serverList }) {
  const { t } = useLang();

  const [activeId, setActiveId]   = useState(serverList?.[0] || '');
  const [mods, setMods]           = useState(null);   // game.mods масив
  const [fullCfg, setFullCfg]     = useState(null);   // повний конфіг
  const [loading, setLoading]     = useState(false);
  const [loadErr, setLoadErr]     = useState('');
  const [dirty, setDirty]         = useState(false);
  const [saving, setSaving]       = useState(false);
  const [savedMsg, setSavedMsg]   = useState('');
  const [showAdd, setShowAdd]     = useState(false);

  // Drag & drop
  const dragIdx = useRef(null);
  const [overIdx, setOverIdx] = useState(null);

  const onDragStart = (idx) => { dragIdx.current = idx; };
  const onDragOver  = (e, idx) => { e.preventDefault(); setOverIdx(idx); };
  const onDragEnd   = () => { dragIdx.current = null; setOverIdx(null); };
  const onDrop      = (idx) => {
    const from = dragIdx.current;
    if (from === null || from === idx) { setOverIdx(null); return; }
    setMods(prev => {
      const next = [...prev];
      const [item] = next.splice(from, 1);
      next.splice(idx, 0, item);
      return next;
    });
    setDirty(true); setSavedMsg('');
    setOverIdx(null);
  };

  useEffect(() => {
    if (!activeId) return;
    setLoading(true); setLoadErr(''); setMods(null); setDirty(false); setSavedMsg('');
    CIE_API.getConfig(activeId)
      .then(cfg => { setFullCfg(cfg); setMods(cfg?.game?.mods || []); })
      .catch(err => setLoadErr(err.message))
      .finally(() => setLoading(false));
  }, [activeId]);

  const move = (idx, dir) => {
    setMods(prev => {
      const next = [...prev];
      const swap = idx + dir;
      if (swap < 0 || swap >= next.length) return prev;
      [next[idx], next[swap]] = [next[swap], next[idx]];
      return next;
    });
    setDirty(true); setSavedMsg('');
  };

  const remove = (idx) => {
    setMods(prev => prev.filter((_, i) => i !== idx));
    setDirty(true); setSavedMsg('');
  };

  const addMod = (mod) => {
    setMods(prev => [...prev, mod]);
    setDirty(true); setSavedMsg('');
  };

  const save = () => {
    if (!dirty || !fullCfg) return;
    setSaving(true); setSavedMsg('');
    const newCfg = { ...fullCfg, game: { ...fullCfg.game, mods } };
    CIE_API.putConfig(activeId, newCfg)
      .then(() => {
        setFullCfg(newCfg);
        setDirty(false);
        setSavedMsg(t('mods.saved'));
        setTimeout(() => setSavedMsg(''), 4000);
      })
      .catch(err => setSavedMsg('✗ ' + err.message))
      .finally(() => setSaving(false));
  };

  return (
    <div style={{display:'flex', flex:1, minHeight:0, overflow:'hidden'}}>

      {/* Список серверів зліва */}
      <div style={{width:180, flexShrink:0, borderRight:'1px solid var(--line)', overflowY:'auto'}}>
        {(serverList || []).map(sid => (
          <div
            key={sid}
            className={`target-row ${activeId===sid?'active':''}`}
            onClick={() => {
              if (dirty && !window.confirm(t('editor.unsaved_confirm'))) return;
              setActiveId(sid);
            }}
            style={{padding:'10px 14px', cursor:'pointer'}}
          >
            <span className="mono" style={{fontSize:11}}>{sid}</span>
          </div>
        ))}
      </div>

      {/* Список модів */}
      <div style={{flex:1, display:'flex', flexDirection:'column', minWidth:0, overflow:'hidden'}}>

        {/* Toolbar */}
        <div style={{
          padding:'8px 16px', borderBottom:'1px solid var(--line)',
          display:'flex', alignItems:'center', gap:10, flexShrink:0,
        }}>
          <span style={{fontFamily:'var(--mono)', fontSize:11, color:'var(--text-mute)', flex:1}}>
            {activeId} · {mods ? `${mods.length} mods` : ''}
          </span>
          {savedMsg && (
            <span style={{fontFamily:'var(--mono)', fontSize:11, color: savedMsg.startsWith('✗')?'var(--err)':'var(--ok)'}}>
              {savedMsg}
            </span>
          )}
          {dirty && !savedMsg && (
            <span style={{fontFamily:'var(--mono)', fontSize:11, color:'var(--warn)'}}>● {t('editor.state_unsaved')}</span>
          )}
          <button className="btn ghost sm" onClick={() => setShowAdd(true)} disabled={loading || !!loadErr}>
            {t('mods.add_mod')}
          </button>
          <button
            className="btn primary sm"
            onClick={save}
            disabled={!dirty || saving}
          >
            {saving ? t('saving') : t('mods.save_btn')}
          </button>
        </div>

        {/* Вміст */}
        <div style={{flex:1, overflowY:'auto'}}>
          {loading && <div className="empty" style={{paddingTop:60}}>{t('mods.loading')}</div>}
          {loadErr && <div className="empty" style={{paddingTop:60, color:'var(--err)'}}>✗ {loadErr}</div>}
          {!loading && !loadErr && mods && mods.length === 0 && (
            <div className="empty" style={{paddingTop:60}}>{t('mods.no_mods_server')}</div>
          )}
          {!loading && !loadErr && mods && mods.length > 0 && (
            <table className="tbl" style={{marginTop:0}}>
              <thead>
                <tr>
                  <th style={{width:36}}>{t('mods.col_order')}</th>
                  <th>{t('mods.col_mod')}</th>
                  <th style={{width:140}}>{t('mods.col_id')}</th>
                  <th style={{width:100}}>{t('mods.field_version')}</th>
                  <th style={{width:80}}></th>
                </tr>
              </thead>
              <tbody>
                {mods.map((mod, idx) => (
                  <tr
                    key={mod.modId + idx}
                    draggable
                    onDragStart={() => onDragStart(idx)}
                    onDragOver={e => onDragOver(e, idx)}
                    onDrop={() => onDrop(idx)}
                    onDragEnd={onDragEnd}
                    style={{
                      opacity: dragIdx.current === idx ? 0.4 : 1,
                      background: overIdx === idx ? 'rgba(120,200,130,0.08)' : undefined,
                      boxShadow: overIdx === idx ? 'inset 0 2px 0 var(--accent)' : undefined,
                    }}
                  >
                    <td className="num dim" style={{fontSize:11, cursor:'grab', userSelect:'none'}}>
                      <span title="Drag to reorder" style={{display:'inline-block', letterSpacing:'0.05em'}}>⠿</span>
                    </td>
                    <td style={{fontWeight:500}}>{mod.name}</td>
                    <td className="mono dim" style={{fontSize:10.5}}>{mod.modId}</td>
                    <td style={{padding:'4px 8px'}}>
                      <input
                        value={mod.version || ''}
                        onChange={e => {
                          const val = e.target.value;
                          setMods(prev => prev.map((m, i) => i === idx ? { ...m, version: val } : m));
                          setDirty(true); setSavedMsg('');
                        }}
                        placeholder="auto"
                        onMouseDown={e => e.stopPropagation()}
                        style={{
                          fontFamily:'var(--mono)', fontSize:11,
                          background:'transparent',
                          borderBottom: '1px solid var(--border)',
                          width:'100%', padding:'2px 4px',
                          color: (mod.version || '') === '' ? 'rgba(100,170,255,0.7)' : 'var(--text)',
                          fontStyle: (mod.version || '') === '' ? 'italic' : 'normal',
                        }}
                      />
                    </td>
                    <td>
                      <div style={{display:'flex', gap:4, justifyContent:'flex-end'}}>
                        <button
                          className="btn ghost sm"
                          style={{padding:'1px 7px', fontSize:10}}
                          onClick={() => move(idx, -1)}
                          disabled={idx === 0}
                        >▲</button>
                        <button
                          className="btn ghost sm"
                          style={{padding:'1px 7px', fontSize:10}}
                          onClick={() => move(idx, 1)}
                          disabled={idx === mods.length - 1}
                        >▼</button>
                        <button
                          className="btn danger sm"
                          style={{padding:'1px 7px'}}
                          onClick={() => remove(idx)}
                        >×</button>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      </div>

      {showAdd && (
        <AddModModal
          onClose={() => setShowAdd(false)}
          onAdd={addMod}
          existingMods={mods || []}
        />
      )}
    </div>
  );
}

// ── Головний компонент ────────────────────────────────────────────────────────

function ModsMatrix({ servers: rawServers }) {
  const { t } = useLang();
  const [tab, setTab] = useState('matrix');

  const [matrix, setMatrix]         = useState(null);
  const [loading, setLoading]       = useState(false);
  const [loadErr, setLoadErr]       = useState('');
  const [refreshing, setRefreshing] = useState(false);

  const load = (isRefresh = false) => {
    if (isRefresh) setRefreshing(true);
    else { setLoading(true); setMatrix(null); }
    setLoadErr('');
    CIE_API.getMods()
      .then(data => setMatrix(data))
      .catch(err => setLoadErr(err.message))
      .finally(() => { setLoading(false); setRefreshing(false); });
  };

  useEffect(() => { load(); }, []);

  const serverList = useMemo(
    () => rawServers ? rawServers.map(s => s.id || s) : (matrix?.servers || []),
    [rawServers, matrix]
  );

  const diffCount = matrix ? matrix.mods.filter(m => !m.in_sync).length : 0;

  return (
    <div style={{height:'100%', display:'flex', flexDirection:'column', overflow:'hidden', padding:'18px 22px 0'}}>

      {/* Header */}
      <div className="view-head" style={{flexShrink:0}}>
        <div>
          <h1>{t('mods.title')}</h1>
          <div className="sub">
            {matrix
              ? t('mods.stats', { total: matrix.mods.length, diff: diffCount })
              : t('mods.subtitle')}
          </div>
        </div>
        <div className="actions">
          {tab === 'matrix' && (
            <button className="btn ghost sm" onClick={() => load(true)} disabled={refreshing || loading}>
              {refreshing ? t('mods.refreshing') : t('mods.refresh')}
            </button>
          )}
        </div>
      </div>

      {/* Таби */}
      <div className="editor-tabs" style={{flexShrink:0}}>
        <div className={`tab ${tab==='matrix'?'on':''}`} onClick={() => setTab('matrix')}>{t('mods.tab_matrix')}</div>
        <div className={`tab ${tab==='server'?'on':''}`} onClick={() => setTab('server')}>{t('mods.tab_server')}</div>
        <div className="spacer" />
      </div>

      {/* Loading / Error для матриці */}
      {tab === 'matrix' && loading && (
        <div className="empty" style={{paddingTop:80}}>{t('mods.loading')}</div>
      )}
      {tab === 'matrix' && loadErr && (
        <div className="empty" style={{paddingTop:80, color:'var(--err)'}}>✗ {loadErr}</div>
      )}

      {/* Таб: Матриця */}
      {tab === 'matrix' && !loading && !loadErr && matrix && (
        <MatrixTab matrix={matrix} onMatrixUpdate={setMatrix} />
      )}

      {/* Таб: По серверу */}
      {tab === 'server' && (
        <ServerTab servers={serverList} />
      )}
    </div>
  );
}

window.ModsMatrix = ModsMatrix;
