'use strict';

const PS = {
  status: null,
  config: null,
};

async function api(method, path, body) {
  const opts = {
    method,
    headers: body ? (body instanceof FormData ? {} : { 'Content-Type': 'application/json' }) : {},
  };
  if (body && !(body instanceof FormData)) {
    opts.body = JSON.stringify(body);
  } else if (body instanceof FormData) {
    opts.body = body;
  }
  const res = await fetch(path, opts);
  const json = await res.json();
  if (!res.ok || !json.success) {
    throw new Error(json.error || `HTTP ${res.status}`);
  }
  return json.data;
}

const get = (path) => api('GET', path);
const post = (path, body) => api('POST', path, body);
const put = (path, body) => api('PUT', path, body);

function showToast(msg, type = 'info') {
  const t = document.getElementById('toast');
  t.textContent = msg;
  t.className = `toast ${type}`;
  t.classList.remove('hidden');
  clearTimeout(t._timer);
  t._timer = setTimeout(() => t.classList.add('hidden'), 3500);
}

function typeLabel(t) {
  const m = { ss: 'Shadowsocks', vmess: 'VMess', vless: 'VLESS', trojan: 'Trojan', hysteria2: 'Hysteria2', http: 'HTTP', socks5: 'SOCKS5', direct: 'DIRECT' };
  return m[t] || t;
}

function groupTypeLabel(t) {
  const m = { select: 'Выбор', 'url-test': 'Автотест', fallback: 'Резерв', 'load-balance': 'Балансировка' };
  return m[t] || t;
}

function getProxies() { return PS.config && Array.isArray(PS.config.proxies) ? PS.config.proxies : []; }
function getGroups() { return PS.config && Array.isArray(PS.config['proxy-groups']) ? PS.config['proxy-groups'] : []; }
function getRules() { return PS.config && Array.isArray(PS.config.rules) ? PS.config.rules : []; }
function getGeneral() { return PS.config && PS.config.general ? PS.config.general : {}; }
function getTProxy() { return PS.config && PS.config.tproxy ? PS.config.tproxy : {}; }
function getDNS() { return PS.config && PS.config.dns ? PS.config.dns : {}; }

async function loadStatus() {
  try {
    PS.status = await get('/api/mihomo/status');
    renderStatus();
  } catch (e) {
    console.error('load status', e);
  }
}

async function loadConfig() {
  try {
    PS.config = await get('/api/mihomo/config');
    renderAll();
  } catch (e) {
    console.error('load config', e);
    PS.config = {};
    renderAll();
    showToast('Ошибка загрузки конфига: ' + e.message, 'error');
  }
  document.getElementById('loading').classList.add('hidden');
}

async function saveFullConfig(restart) {
  try {
    await put('/api/mihomo/config', PS.config);
    showToast('Конфиг сохранён', 'success');
    refreshYAMLPreview();
    if (restart) {
      try {
        await post('/api/mihomo/restart', null);
        showToast('Mihomo перезапущен', 'success');
      } catch (e) {
        showToast('Перезапуск не удался: ' + e.message, 'error');
      }
      await loadStatus();
    }
  } catch (e) {
    showToast('Ошибка сохранения: ' + e.message, 'error');
  }
}

async function refreshYAMLPreview() {
  try {
    const res = await fetch('/api/mihomo/config.yaml');
    if (res.ok) {
      document.getElementById('yamlPreview').value = await res.text();
    }
  } catch (e) { /* ignore */ }
}

function renderStatus() {
  const s = PS.status;
  if (!s) return;
  const text = document.getElementById('statusText');
  const headerBadge = document.getElementById('statusBadge');
  const startBtn = document.getElementById('startBtn');
  const stopBtn = document.getElementById('stopBtn');
  const restartBtn = document.getElementById('restartBtn');
  const coreInfo = document.getElementById('coreInfo');

  if (s.running) {
    text.className = 'svc-badge running';
    text.textContent = 'Запущен (PID ' + (s.pid || '?') + ')';
    headerBadge.className = 'svc-badge running';
    headerBadge.textContent = 'Запущен';
    startBtn.disabled = true;
    stopBtn.disabled = false;
    restartBtn.disabled = false;
  } else {
    text.className = 'svc-badge stopped';
    text.textContent = 'Остановлен';
    headerBadge.className = 'svc-badge stopped';
    headerBadge.textContent = 'Остановлен';
    startBtn.disabled = false;
    stopBtn.disabled = true;
    restartBtn.disabled = true;
  }

  document.getElementById('corePath').textContent = s.core_path || '—';
  document.getElementById('coreExists').textContent = s.core_exists ? 'Да' : 'Нет';
  document.getElementById('corePid').textContent = s.running && s.pid ? s.pid : '—';

  if (!s.core_exists) {
    coreInfo.classList.remove('hidden');
    document.getElementById('coreInfoMsg').textContent = 'Ядро Mihomo не найдено. Загрузите бинарный файл в раздел «Ядро».';
  } else {
    coreInfo.classList.add('hidden');
  }

  document.getElementById('statusBar').classList.remove('hidden');
  document.getElementById('loading').classList.add('hidden');
}

function renderAll() {
  renderProxies();
  renderGroups();
  renderRules();
  fillSettings();
  refreshYAMLPreview();
}

function renderProxies() {
  const list = document.getElementById('proxyList');
  const empty = document.getElementById('proxyEmpty');
  const proxies = getProxies();
  list.innerHTML = '';
  if (proxies.length === 0) {
    empty.classList.remove('hidden');
    return;
  }
  empty.classList.add('hidden');
  proxies.forEach(p => {
    const card = document.createElement('div');
    card.className = 'proxy-card';
    card.innerHTML = `
      <div class="proxy-card-header">
        <div style="display:flex;align-items:center;gap:8px">
          <span class="proxy-name">${esc(p.name)}</span>
          <span class="tag-active">${esc(typeLabel(p.type))}</span>
        </div>
        <div style="display:flex;gap:6px">
          <button class="btn btn-ghost btn-sm" data-edit-proxy="${esc(p.name)}">Изменить</button>
          <button class="btn btn-danger btn-sm" data-delete-proxy="${esc(p.name)}">Удалить</button>
        </div>
      </div>
      <div class="proxy-card-info">
        ${p.type !== 'direct' ? `<div class="info-row"><span class="info-label">Сервер</span><span class="info-val mono">${esc(p.server || '—')}</span></div>
        <div class="info-row"><span class="info-label">Порт</span><span class="info-val mono">${p.port || '—'}</span></div>` : ''}
        ${p.udp ? '<span class="tag-active" style="margin-left:0">UDP</span>' : ''}
        ${p.tls ? '<span class="tag-active" style="margin-left:4px">TLS</span>' : ''}
      </div>
    `;
    list.appendChild(card);
  });
}

function renderGroups() {
  const list = document.getElementById('groupList');
  const empty = document.getElementById('groupEmpty');
  const groups = getGroups();
  list.innerHTML = '';
  if (groups.length === 0) {
    empty.classList.remove('hidden');
    return;
  }
  empty.classList.add('hidden');
  groups.forEach(g => {
    const card = document.createElement('div');
    card.className = 'proxy-card';
    const proxyList = (g.proxies || []).slice(0, 5).map(p => esc(p)).join(', ');
    const more = (g.proxies || []).length > 5 ? ` +${(g.proxies || []).length - 5}` : '';
    card.innerHTML = `
      <div class="proxy-card-header">
        <div style="display:flex;align-items:center;gap:8px">
          <span class="proxy-name">${esc(g.name)}</span>
          <span class="tag-gw">${esc(groupTypeLabel(g.type))}</span>
          ${g['include-all'] ? '<span class="tag-active">Все прокси</span>' : ''}
        </div>
        <div style="display:flex;gap:6px">
          <button class="btn btn-ghost btn-sm" data-edit-group="${esc(g.name)}">Изменить</button>
          <button class="btn btn-danger btn-sm" data-delete-group="${esc(g.name)}">Удалить</button>
        </div>
      </div>
      <div class="proxy-card-info">
        <div class="info-row"><span class="info-label">Узлы</span><span class="info-val">${proxyList}${more || '—'}</span></div>
        ${g.url ? `<div class="info-row"><span class="info-label">URL</span><span class="info-val mono" style="font-size:.75rem">${esc(g.url)}</span></div>` : ''}
        ${g.interval ? `<div class="info-row"><span class="info-label">Интервал</span><span class="info-val">${g.interval}с</span></div>` : ''}
      </div>
    `;
    list.appendChild(card);
  });
}

function renderRules() {
  const list = document.getElementById('rulesList');
  const rules = getRules();
  list.innerHTML = '';
  if (rules.length === 0) {
    list.innerHTML = '<div class="empty-state">Нет правил маршрутизации</div>';
    return;
  }
  rules.forEach((rule, i) => {
    const parts = rule.split(',');
    const type = parts[0] || '';
    const value = parts[1] || '';
    const target = parts[2] || '';
    const flags = parts.slice(3).join(',');

    const el = document.createElement('div');
    el.className = 'rule-item';
    el.innerHTML = `
      <div class="rule-info">
        <span class="rule-type">${esc(type)}</span>
        <span class="rule-value">${esc(value || (type === 'MATCH' ? '*' : ''))}</span>
        <span class="rule-target">${esc(target)}</span>
        ${flags ? `<span class="rule-flags">${esc(flags)}</span>` : ''}
      </div>
      <div style="display:flex;gap:4px">
        <button class="btn btn-danger btn-sm" data-delete-rule="${i}">✕</button>
      </div>
    `;
    list.appendChild(el);
  });
}

function fillSettings() {
  if (!PS.config) {
    document.getElementById('loading').classList.add('hidden');
    return;
  }
  const g = getGeneral();
  const tp = getTProxy();
  const dns = getDNS();

  document.getElementById('mixedPort').value = g['mixed-port'] || 7890;
  document.getElementById('allowLan').checked = g['allow-lan'] !== false;
  document.getElementById('ipv6').checked = g.ipv6 !== false;
  document.getElementById('tcpConcurrent').checked = g['tcp-concurrent'] !== false;
  document.getElementById('externalController').value = g['external-controller'] || '0.0.0.0:9090';
  document.getElementById('secret').value = g.secret || '';

  setSegBtn('modeSwitch', g.mode || 'rule');

  document.getElementById('tproxyEnabled').checked = tp.enabled || false;
  document.getElementById('tproxyPort').value = tp.port || 7894;

  document.getElementById('dnsEnabled').checked = dns.enable !== false;
  document.getElementById('dnsListen').value = dns.listen || '0.0.0.0:1053';
  setSegBtn('dnsModeSwitch', dns['enhanced-mode'] || 'redir-host');
  document.getElementById('dnsNameserver').value = (dns.nameserver || []).join('\n');
  document.getElementById('dnsFallback').value = (dns.fallback || []).join('\n');
}

function setSegBtn(id, mode) {
  document.querySelectorAll(`#${id} .seg-btn`).forEach(b => {
    b.classList.toggle('active', b.dataset.mode === mode);
  });
}

function getSegBtn(id) {
  const active = document.querySelector(`#${id} .seg-btn.active`);
  return active ? active.dataset.mode : '';
}

function esc(s) {
  if (!s) return '';
  const d = document.createElement('div');
  d.textContent = s;
  return d.innerHTML;
}

// ─── Tab switching ───
document.querySelectorAll('.ptab').forEach(btn => {
  btn.addEventListener('click', () => {
    document.querySelectorAll('.ptab').forEach(b => b.classList.remove('active'));
    document.querySelectorAll('.ptab-content').forEach(c => c.classList.add('hidden'));
    btn.classList.add('active');
    document.getElementById('tab-' + btn.dataset.tab).classList.remove('hidden');
  });
});

// ─── Core control ───
document.getElementById('startBtn').addEventListener('click', async () => {
  try {
    await post('/api/mihomo/start', null);
    showToast('Mihomo запущен', 'success');
    await loadStatus();
  } catch (e) {
    showToast('Ошибка: ' + e.message, 'error');
  }
});

document.getElementById('stopBtn').addEventListener('click', async () => {
  try {
    await post('/api/mihomo/stop', null);
    showToast('Mihomo остановлен', 'success');
    await loadStatus();
  } catch (e) {
    showToast('Ошибка: ' + e.message, 'error');
  }
});

document.getElementById('restartBtn').addEventListener('click', async () => {
  try {
    await post('/api/mihomo/restart', null);
    showToast('Mihomo перезапущен', 'success');
    await loadStatus();
  } catch (e) {
    showToast('Ошибка: ' + e.message, 'error');
  }
});

// ─── Config mode switches ───
document.getElementById('modeSwitch').addEventListener('click', e => {
  const btn = e.target.closest('.seg-btn');
  if (btn) setSegBtn('modeSwitch', btn.dataset.mode);
});
document.getElementById('dnsModeSwitch').addEventListener('click', e => {
  const btn = e.target.closest('.seg-btn');
  if (btn) setSegBtn('dnsModeSwitch', btn.dataset.mode);
});

// ─── Settings save ───
function applySettingsToConfig() {
  if (!PS.config) PS.config = {};
  PS.config['mixed-port'] = parseInt(document.getElementById('mixedPort').value) || 7890;
  PS.config['allow-lan'] = document.getElementById('allowLan').checked;
  PS.config['bind-address'] = '*';
  PS.config.mode = getSegBtn('modeSwitch');
  PS.config['log-level'] = 'info';
  PS.config.ipv6 = document.getElementById('ipv6').checked;
  PS.config['external-controller'] = document.getElementById('externalController').value;
  PS.config.secret = document.getElementById('secret').value || '';
  PS.config['tcp-concurrent'] = document.getElementById('tcpConcurrent').checked;
  PS.config['find-process-mode'] = 'off';

  const tpEnabled = document.getElementById('tproxyEnabled').checked;
  if (tpEnabled) {
    PS.config['tproxy-port'] = parseInt(document.getElementById('tproxyPort').value) || 7894;
  } else {
    delete PS.config['tproxy-port'];
  }

  PS.config.dns = {
    enable: document.getElementById('dnsEnabled').checked,
    ipv6: document.getElementById('ipv6').checked,
    listen: document.getElementById('dnsListen').value,
    'enhanced-mode': getSegBtn('dnsModeSwitch'),
    'fake-ip-range': '198.18.0.1/16',
    'fake-ip-filter': ['*.lan', '*.local', '+.market.xiaomi.com'],
    'default-nameserver': ['223.5.5.5', '119.29.29.29'],
    nameserver: document.getElementById('dnsNameserver').value.split('\n').map(s => s.trim()).filter(Boolean),
    fallback: document.getElementById('dnsFallback').value.split('\n').map(s => s.trim()).filter(Boolean),
  };

  PS.config.profile = { 'store-selected': true, 'store-fake-ip': true };
}

document.getElementById('settingsForm').addEventListener('submit', e => {
  e.preventDefault();
  applySettingsToConfig();
  saveFullConfig(false);
});
document.getElementById('saveAndRestartBtn').addEventListener('click', () => {
  applySettingsToConfig();
  saveFullConfig(true);
});

// ─── Proxy Modal ───
function updateProxyFields() {
  const type = document.getElementById('proxyType').value;
  const serverFields = document.getElementById('proxyServerFields');
  const authFields = document.getElementById('proxyAuthFields');
  const cipherField = document.getElementById('proxyCipherField');
  const uuidField = document.getElementById('proxyUUIDField');
  const trojanPassField = document.getElementById('proxyTrojanPassField');
  const hysteria2Fields = document.getElementById('proxyHysteria2Fields');
  const tlsField = document.getElementById('proxyTLSField');
  const vlessFlowField = document.getElementById('proxyVlessFlowField');
  const networkField = document.getElementById('proxyNetworkField');

  serverFields.classList.remove('hidden');
  authFields.classList.add('hidden');
  cipherField.classList.add('hidden');
  uuidField.classList.add('hidden');
  trojanPassField.classList.add('hidden');
  hysteria2Fields.classList.add('hidden');
  tlsField.classList.add('hidden');
  vlessFlowField.classList.add('hidden');
  networkField.classList.add('hidden');

  switch (type) {
    case 'ss':
      cipherField.classList.remove('hidden');
      tlsField.classList.remove('hidden');
      document.querySelector('#proxyPassword').closest('.form-row').classList.remove('hidden');
      break;
    case 'vmess':
      uuidField.classList.remove('hidden');
      cipherField.classList.remove('hidden');
      tlsField.classList.remove('hidden');
      networkField.classList.remove('hidden');
      document.getElementById('proxyCipher').innerHTML = '<option value="auto">auto</option><option value="none">none</option><option value="zero">zero</option><option value="aes-128-gcm">aes-128-gcm</option><option value="chacha20-poly1305">chacha20-poly1305</option>';
      break;
    case 'vless':
      uuidField.classList.remove('hidden');
      tlsField.classList.remove('hidden');
      vlessFlowField.classList.remove('hidden');
      networkField.classList.remove('hidden');
      break;
    case 'trojan':
      trojanPassField.classList.remove('hidden');
      tlsField.classList.remove('hidden');
      networkField.classList.remove('hidden');
      break;
    case 'hysteria2':
      hysteria2Fields.classList.remove('hidden');
      tlsField.classList.add('hidden');
      document.querySelector('#proxyPassword').closest('.form-row').classList.remove('hidden');
      serverFields.classList.remove('hidden');
      break;
    case 'http':
      authFields.classList.remove('hidden');
      tlsField.classList.remove('hidden');
      break;
    case 'socks5':
      authFields.classList.remove('hidden');
      tlsField.classList.remove('hidden');
      break;
    case 'direct':
      serverFields.classList.add('hidden');
      break;
  }
}

document.getElementById('proxyType').addEventListener('change', updateProxyFields);

let editProxyName = null;

function openProxyModal(proxy) {
  editProxyName = proxy ? proxy.name : null;
  document.getElementById('proxyModalTitle').textContent = proxy ? 'Редактировать прокси' : 'Добавить прокси';
  document.getElementById('proxyEditName').value = proxy ? proxy.name : '';
  document.getElementById('proxyName').value = proxy ? proxy.name : '';
  document.getElementById('proxyType').value = proxy ? proxy.type : 'ss';
  document.getElementById('proxyServer').value = proxy ? (proxy.server || '') : '';
  document.getElementById('proxyPort').value = proxy ? (proxy.port || '') : '';
  document.getElementById('proxyUDP').checked = proxy ? (proxy.udp !== false) : true;
  document.getElementById('proxyUsername').value = proxy ? (proxy.username || '') : '';
  document.getElementById('proxyPassword').value = proxy ? (proxy.password || '') : '';
  document.getElementById('proxyTLS').checked = proxy ? (proxy.tls || false) : false;
  document.getElementById('proxySNI').value = proxy ? (proxy.servername || '') : '';
  document.getElementById('proxySkipCertVerify').checked = proxy ? (proxy['skip-cert-verify'] || false) : false;
  document.getElementById('proxyUUID').value = proxy ? (proxy.uuid || '') : '';
  document.getElementById('proxyCipher').value = proxy ? (proxy.cipher || 'auto') : 'auto';
  document.getElementById('proxyFlow').value = proxy ? (proxy.flow || '') : '';
  document.getElementById('proxyNetwork').value = proxy ? (proxy.network || '') : '';
  document.getElementById('proxyTrojanPass').value = proxy ? (proxy.password || '') : '';
  document.getElementById('proxyObfs').value = proxy ? (proxy.obfs || '') : '';
  document.getElementById('proxyObfsPass').value = proxy ? (proxy['obfs-password'] || '') : '';

  updateProxyFields();
  document.getElementById('proxyModal').classList.remove('hidden');
}

function closeProxyModal() {
  document.getElementById('proxyModal').classList.add('hidden');
}

document.getElementById('addProxyBtn').addEventListener('click', () => openProxyModal(null));
document.getElementById('closeProxyModal').addEventListener('click', closeProxyModal);
document.getElementById('cancelProxyBtn').addEventListener('click', closeProxyModal);
document.getElementById('proxyModalBackdrop').addEventListener('click', closeProxyModal);

document.getElementById('saveProxyBtn').addEventListener('click', () => {
  const type = document.getElementById('proxyType').value;
  const proxy = {
    name: document.getElementById('proxyName').value.trim(),
    type: type,
    server: document.getElementById('proxyServer').value.trim(),
    port: parseInt(document.getElementById('proxyPort').value) || 443,
    udp: document.getElementById('proxyUDP').checked,
    tls: document.getElementById('proxyTLS').checked,
    servername: document.getElementById('proxySNI').value.trim(),
    'skip-cert-verify': document.getElementById('proxySkipCertVerify').checked,
    network: document.getElementById('proxyNetwork').value || '',
  };

  if (type === 'ss') {
    proxy.cipher = document.getElementById('proxyCipher').value;
    proxy.password = document.getElementById('proxyPassword').value;
  } else if (type === 'vmess' || type === 'vless') {
    proxy.uuid = document.getElementById('proxyUUID').value.trim();
    if (type === 'vless') proxy.flow = document.getElementById('proxyFlow').value.trim();
    proxy.cipher = document.getElementById('proxyCipher').value;
  } else if (type === 'trojan') {
    proxy.password = document.getElementById('proxyTrojanPass').value;
  } else if (type === 'hysteria2') {
    proxy.password = document.getElementById('proxyPassword').value;
    proxy.obfs = document.getElementById('proxyObfs').value.trim();
    proxy['obfs-password'] = document.getElementById('proxyObfsPass').value.trim();
  } else if (type === 'http' || type === 'socks5') {
    proxy.username = document.getElementById('proxyUsername').value.trim();
    proxy.password = document.getElementById('proxyPassword').value;
  } else if (type === 'direct') {
    delete proxy.server;
    delete proxy.port;
  }

  if (!proxy.name) {
    showToast('Имя прокси обязательно', 'error');
    return;
  }

  const proxies = getProxies();
  if (editProxyName) {
    const idx = proxies.findIndex(p => p.name === editProxyName);
    if (idx >= 0) {
      const oldName = proxies[idx].name;
      proxies[idx] = proxy;
      if (proxy.name !== oldName) {
        getGroups().forEach(g => {
          if (g.proxies) {
            g.proxies = g.proxies.map(pn => pn === oldName ? proxy.name : pn);
          }
        });
      }
    }
  } else {
    if (proxies.some(p => p.name === proxy.name)) {
      showToast('Прокси с таким именем уже существует', 'error');
      return;
    }
    proxies.push(proxy);
  }
  PS.config.proxies = proxies;

  closeProxyModal();
  renderAll();
  saveFullConfig(false);
});

// Proxy list delegated events
document.getElementById('proxyList').addEventListener('click', e => {
  const editBtn = e.target.closest('[data-edit-proxy]');
  const delBtn = e.target.closest('[data-delete-proxy]');
  if (editBtn) {
    const name = editBtn.dataset.editProxy;
    const proxy = getProxies().find(p => p.name === name);
    if (proxy) openProxyModal(proxy);
  } else if (delBtn) {
    const name = delBtn.dataset.deleteProxy;
    if (confirm(`Удалить прокси "${name}"?`)) {
      const proxies = getProxies();
      const idx = proxies.findIndex(p => p.name === name);
      if (idx >= 0) {
        proxies.splice(idx, 1);
        PS.config.proxies = proxies;
        getGroups().forEach(g => {
          if (g.proxies) {
            g.proxies = g.proxies.filter(pn => pn !== name);
          }
        });
        renderAll();
        saveFullConfig(false);
      }
    }
  }
});

// ─── Group Modal ───
function updateGroupFields() {
  const type = document.getElementById('groupType').value;
  const urlField = document.getElementById('groupURLField');
  const lbField = document.getElementById('groupLBStrategy');
  if (type === 'url-test' || type === 'fallback' || type === 'load-balance') {
    urlField.classList.remove('hidden');
  } else {
    urlField.classList.add('hidden');
  }
  if (type === 'load-balance') {
    lbField.classList.remove('hidden');
  } else {
    lbField.classList.add('hidden');
  }
}

document.getElementById('groupType').addEventListener('change', updateGroupFields);

let editGroupName = null;

function openGroupModal(group) {
  editGroupName = group ? group.name : null;
  document.getElementById('groupEditName').value = group ? group.name : '';
  document.getElementById('groupModalTitle').textContent = group ? 'Редактировать группу' : 'Добавить группу';
  document.getElementById('groupName').value = group ? group.name : '';
  document.getElementById('groupType').value = group ? group.type : 'select';
  document.getElementById('groupURL').value = group ? (group.url || 'https://www.gstatic.com/generate_204') : 'https://www.gstatic.com/generate_204';
  document.getElementById('groupInterval').value = group ? (group.interval || 300) : 300;
  document.getElementById('groupTolerance').value = group ? (group.tolerance || 50) : 50;
  document.getElementById('groupIncludeAll').checked = group ? (group['include-all'] || false) : false;
  document.getElementById('groupFilter').value = group ? (group.filter || '') : '';
  document.getElementById('groupLBStrategy').value = group ? (group.strategy || 'round-robin') : 'round-robin';

  const checkboxes = document.getElementById('groupProxyCheckboxes');
  checkboxes.innerHTML = '';
  const builtin = ['DIRECT'];
  const allProxies = [...builtin, ...getProxies().map(p => p.name)];
  const selected = group ? (group.proxies || []) : [];
  allProxies.forEach(name => {
    const label = document.createElement('label');
    label.className = 'checkbox-label';
    label.style.cssText = 'font-size:.85rem;padding:4px 0;display:flex;align-items:center;gap:6px';
    const cb = document.createElement('input');
    cb.type = 'checkbox';
    cb.value = name;
    if (selected.includes(name)) cb.checked = true;
    label.appendChild(cb);
    label.appendChild(document.createTextNode(name));
    checkboxes.appendChild(label);
  });

  updateGroupFields();
  document.getElementById('groupModal').classList.remove('hidden');
}

function closeGroupModal() {
  document.getElementById('groupModal').classList.add('hidden');
}

document.getElementById('addGroupBtn').addEventListener('click', () => openGroupModal(null));
document.getElementById('closeGroupModal').addEventListener('click', closeGroupModal);
document.getElementById('cancelGroupBtn').addEventListener('click', closeGroupModal);
document.getElementById('groupModalBackdrop').addEventListener('click', closeGroupModal);

document.getElementById('saveGroupBtn').addEventListener('click', () => {
  const selectedProxies = [];
  document.querySelectorAll('#groupProxyCheckboxes input[type="checkbox"]:checked').forEach(cb => {
    selectedProxies.push(cb.value);
  });

  const group = {
    name: document.getElementById('groupName').value.trim(),
    type: document.getElementById('groupType').value,
    proxies: selectedProxies,
    url: document.getElementById('groupURLField').classList.contains('hidden') ? '' : document.getElementById('groupURL').value.trim(),
    interval: parseInt(document.getElementById('groupInterval').value) || 300,
    tolerance: parseInt(document.getElementById('groupTolerance').value) || 0,
    'include-all': document.getElementById('groupIncludeAll').checked,
    filter: document.getElementById('groupFilter').value.trim(),
  };

  if (group.type === 'load-balance') {
    group.strategy = document.getElementById('groupLBStrategy').value;
  }

  if (!group.name) {
    showToast('Имя группы обязательно', 'error');
    return;
  }

  const groups = getGroups();
  if (editGroupName) {
    const idx = groups.findIndex(g => g.name === editGroupName);
    if (idx >= 0) {
      groups[idx] = group;
    }
  } else {
    if (groups.some(g => g.name === group.name)) {
      showToast('Группа с таким именем уже существует', 'error');
      return;
    }
    groups.push(group);
  }
  PS.config['proxy-groups'] = groups;

  closeGroupModal();
  renderAll();
  saveFullConfig(false);
});

// Group list events
document.getElementById('groupList').addEventListener('click', e => {
  const editBtn = e.target.closest('[data-edit-group]');
  const delBtn = e.target.closest('[data-delete-group]');
  if (editBtn) {
    const name = editBtn.dataset.editGroup;
    const group = getGroups().find(g => g.name === name);
    if (group) openGroupModal(group);
  } else if (delBtn) {
    const name = delBtn.dataset.deleteGroup;
    if (confirm(`Удалить группу "${name}"?`)) {
      const groups = getGroups();
      const idx = groups.findIndex(g => g.name === name);
      if (idx >= 0) {
        groups.splice(idx, 1);
        PS.config['proxy-groups'] = groups;
        const rules = getRules();
        PS.config.rules = rules.map(r => {
          if (r.endsWith(',' + name)) {
            return r.replace(',' + name, ',DIRECT');
          }
          return r;
        });
        renderAll();
        saveFullConfig(false);
      }
    }
  }
});

// ─── Rule Modal ───
function updateRuleFields() {
  const type = document.getElementById('ruleType').value;
  const valueDiv = document.getElementById('ruleValueField');
  const noResolveDiv = document.getElementById('ruleNoResolveDiv');
  valueDiv.classList.remove('hidden');
  noResolveDiv.classList.add('hidden');

  if (type === 'MATCH') {
    valueDiv.classList.add('hidden');
  } else if (type === 'IP-CIDR' || type === 'IP-CIDR6' || type === 'SRC-IP-CIDR' || type === 'GEOIP') {
    noResolveDiv.classList.remove('hidden');
  }

  const sel = document.getElementById('ruleProxy');
  sel.innerHTML = '<option value="DIRECT">DIRECT</option><option value="REJECT">REJECT</option>';
  getGroups().forEach(g => {
    const opt = document.createElement('option');
    opt.value = g.name;
    opt.textContent = '📋 ' + g.name;
    sel.appendChild(opt);
  });
  getProxies().forEach(p => {
    if (p.type !== 'direct') {
      const opt = document.createElement('option');
      opt.value = p.name;
      opt.textContent = '🔗 ' + p.name;
      sel.appendChild(opt);
    }
  });
}

document.getElementById('ruleType').addEventListener('change', updateRuleFields);

function openRuleModal() {
  document.getElementById('ruleModalTitle').textContent = 'Добавить правило';
  document.getElementById('ruleType').value = 'DOMAIN-SUFFIX';
  document.getElementById('ruleValue').value = '';
  document.getElementById('ruleProxy').value = 'DIRECT';
  document.getElementById('ruleNoResolve').checked = true;
  updateRuleFields();
  document.getElementById('ruleModal').classList.remove('hidden');
}

function closeRuleModal() {
  document.getElementById('ruleModal').classList.add('hidden');
}

document.getElementById('addRuleBtn').addEventListener('click', openRuleModal);
document.getElementById('addBlockBtn').addEventListener('click', () => {
  openRuleModal();
  document.getElementById('ruleType').value = 'DOMAIN-KEYWORD';
  document.getElementById('ruleProxy').value = 'REJECT';
  updateRuleFields();
});
document.getElementById('closeRuleModal').addEventListener('click', closeRuleModal);
document.getElementById('cancelRuleBtn').addEventListener('click', closeRuleModal);
document.getElementById('ruleModalBackdrop').addEventListener('click', closeRuleModal);

document.getElementById('saveRuleBtn').addEventListener('click', () => {
  const type = document.getElementById('ruleType').value;
  const value = document.getElementById('ruleValue').value.trim();
  const proxy = document.getElementById('ruleProxy').value;
  const noResolve = document.getElementById('ruleNoResolve').checked;

  if (type !== 'MATCH' && !value) {
    showToast('Введите значение правила', 'error');
    return;
  }

  let rule;
  if (type === 'MATCH') {
    rule = `MATCH,${proxy}`;
  } else {
    rule = `${type},${value},${proxy}`;
    const needsNoResolve = ['IP-CIDR', 'IP-CIDR6', 'SRC-IP-CIDR', 'GEOIP'].includes(type);
    if (needsNoResolve && noResolve) {
      rule += ',no-resolve';
    }
  }

  const rules = getRules();
  rules.push(rule);
  PS.config.rules = rules;

  closeRuleModal();
  renderAll();
  saveFullConfig(false);
});

// Rule delete delegated events
document.getElementById('rulesList').addEventListener('click', e => {
  const delBtn = e.target.closest('[data-delete-rule]');
  if (delBtn) {
    const idx = parseInt(delBtn.dataset.deleteRule);
    if (isNaN(idx)) return;
    const rules = getRules();
    rules.splice(idx, 1);
    PS.config.rules = rules;
    renderAll();
    saveFullConfig(false);
  }
});

// ─── Core upload ───
document.getElementById('uploadCoreForm').addEventListener('submit', async e => {
  e.preventDefault();
  const fileInput = document.getElementById('coreFile');
  if (!fileInput.files[0]) {
    showToast('Выберите файл', 'error');
    return;
  }
  const fd = new FormData();
  fd.append('core', fileInput.files[0]);
  try {
    const result = await fetch('/api/mihomo/upload-core', { method: 'POST', body: fd });
    const json = await result.json();
    if (!json.success) {
      throw new Error(json.error || 'Upload failed');
    }
    showToast('Ядро загружено: ' + (json.data.arch || json.data.path), 'success');
    await loadStatus();
  } catch (e) {
    showToast('Ошибка: ' + e.message, 'error');
  }
});

// ─── YAML editor ───
document.getElementById('yamlLoadBtn').addEventListener('click', async () => {
  try {
    const res = await fetch('/api/mihomo/config.yaml');
    if (res.status === 404) {
      document.getElementById('yamlEditor').value = '# Config not found.';
      return;
    }
    const text = await res.text();
    document.getElementById('yamlEditor').value = text;
    showToast('Конфиг загружен', 'info');
  } catch (e) {
    showToast('Ошибка: ' + e.message, 'error');
  }
});

document.getElementById('yamlSaveBtn').addEventListener('click', async () => {
  const content = document.getElementById('yamlEditor').value;
  try {
    const res = await fetch('/api/mihomo/config.yaml', {
      method: 'PUT',
      headers: { 'Content-Type': 'text/yaml' },
      body: content,
    });
    const json = await res.json();
    if (!json.success) throw new Error(json.error || 'Save failed');
    showToast('Конфиг сохранён', 'success');
    await loadConfig();
  } catch (e) {
    showToast('Ошибка: ' + e.message, 'error');
  }
});

// ─── Logs polling ───
let logPollTimer = null;
let lastLogCount = 0;

async function fetchLogs() {
  try {
    const lines = await get('/api/mihomo/logs');
    const el = document.getElementById('logOutput');
    if (lines.length !== lastLogCount) {
      lastLogCount = lines.length;
      el.textContent = lines.join('\n');
      el.scrollTop = el.scrollHeight;
    }
  } catch (e) { /* ignore */ }
}

function startLogPoll() {
  if (logPollTimer) return;
  fetchLogs();
  logPollTimer = setInterval(fetchLogs, 500);
}

function stopLogPoll() {
  if (logPollTimer) {
    clearInterval(logPollTimer);
    logPollTimer = null;
  }
}

document.querySelectorAll('.ptab').forEach(btn => {
  btn.addEventListener('click', () => {
    if (btn.dataset.tab === 'logs') {
      lastLogCount = 0;
      startLogPoll();
    } else {
      stopLogPoll();
    }
  });
});

document.getElementById('clearLogsBtn').addEventListener('click', () => {
  document.getElementById('logOutput').textContent = '';
  lastLogCount = 0;
});

// ─── Init ───
(async () => {
  try { await loadStatus(); } catch(e) { console.error('status', e); }
  try { await loadConfig(); } catch(e) { console.error('config', e); }
  document.getElementById('loading').classList.add('hidden');
})();