'use strict';

// ── State ────────────────────────────────────────────────────────────────────

const state = {
  interfaces: [],    // latest data from /api/interfaces
  pending: [],       // interface names with pending config
  configModal: null, // name of interface being configured (null = new VLAN)
  configModalParent: null, // parent interface when creating a new VLAN
  nat: null,         // {installed, interfaces} from /api/nat
};

// ── API helpers ──────────────────────────────────────────────────────────────

async function api(method, path, body) {
  const opts = {
    method,
    headers: body ? { 'Content-Type': 'application/json' } : {},
    body: body ? JSON.stringify(body) : undefined,
  };
  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 del  = (path)        => api('DELETE', path);

// ── VLAN helpers ─────────────────────────────────────────────────────────────

function isVLAN(name) {
  return /\.\d+$/.test(name);
}
function vlanParent(name) {
  return name.replace(/\.\d+$/, '');
}
function vlanId(name) {
  const m = name.match(/\.(\d+)$/);
  return m ? parseInt(m[1]) : 0;
}

// ── Format helpers ───────────────────────────────────────────────────────────

function fmtBytes(n) {
  if (n === undefined || n === null) return '—';
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
  let i = 0;
  let v = Number(n);
  while (v >= 1024 && i < units.length - 1) { v /= 1024; i++; }
  return `${v.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
}

function stateClass(s) {
  if (s === 'up') return 'up';
  if (s === 'down') return 'down';
  return 'unknown';
}

function modeLabel(m) {
  return { dhcp: 'DHCP', static: 'Static', loopback: 'Loopback', manual: 'Manual' }[m] || (m || '?');
}

// ── SVG icons ────────────────────────────────────────────────────────────────

const ICON = {
  pencil: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
    <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
    <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
  </svg>`,
  restart: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
    <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
    <path d="M3 3v5h5"/>
  </svg>`,
  trash: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
    <path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6"/>
  </svg>`,
  plus: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13">
    <path d="M12 5v14M5 12h14"/>
  </svg>`,
};

// ── Render ───────────────────────────────────────────────────────────────────

function renderAll() {
  const grid = document.getElementById('ifaceGrid');
  grid.innerHTML = '';

  // Group VLANs by parent
  const vlansByParent = {};
  const physicals = [];

  for (const iface of state.interfaces) {
    if (isVLAN(iface.name)) {
      const p = vlanParent(iface.name);
      if (!vlansByParent[p]) vlansByParent[p] = [];
      vlansByParent[p].push(iface);
    } else {
      physicals.push(iface);
    }
  }

  for (const iface of physicals) {
    const vlans = vlansByParent[iface.name] || [];
    grid.appendChild(buildCard(iface, vlans));
  }

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

  renderPendingBanner();
}

function buildCard(iface, vlans) {
  const hasPending = state.pending.includes(iface.name);
  const sc = stateClass(iface.state);
  const isLo = iface.name === 'lo' || iface.mode === 'loopback';
  const isUp = iface.state === 'up';
  const label = iface.label || '';

  const card = document.createElement('div');
  card.className = 'iface-card' + (hasPending ? ' has-pending' : '');
  card.dataset.name = iface.name;

  const ipv6lines = (iface.ipv6 || []).map(a =>
    `<div class="info-row"><span class="info-label">IPv6</span><span class="info-val">${a}</span></div>`
  ).join('');

  const nameBlock = label
    ? `<div class="card-name-stack">
        <span class="card-label-text">${label}</span>
        <span class="card-iface-sub">${iface.name}</span>
       </div>`
    : `<span class="card-iface-name">${iface.name}</span>`;

  card.innerHTML = `
    <div class="card-header">
      <div class="card-name">
        <span class="state-dot ${sc}"></span>
        ${nameBlock}
        ${hasPending ? '<span class="pending-badge">несохранено</span>' : ''}
      </div>
      <span class="mode-badge ${iface.mode || 'unknown'}">${modeLabel(iface.mode)}</span>
    </div>

    <div class="card-info">
      <div class="info-row">
        <span class="info-label">IPv4</span>
        <span class="info-val">${iface.ipv4
          ? iface.ipv4 + (iface.ipv4_mask ? ' / ' + iface.ipv4_mask : '')
          : '<span class="none">&mdash;</span>'}</span>
      </div>
      ${ipv6lines || `<div class="info-row"><span class="info-label">IPv6</span><span class="info-val none">&mdash;</span></div>`}
      <div class="info-row">
        <span class="info-label">Шлюз</span>
        <span class="info-val">${iface.gateway || '<span class="none">&mdash;</span>'}</span>
      </div>
      <div class="traffic-row">
        <div class="traffic-item">
          <span class="traffic-label">↓ RX</span>
          <span class="traffic-val">${fmtBytes(iface.rx_bytes)}</span>
        </div>
        <div class="traffic-item">
          <span class="traffic-label">↑ TX</span>
          <span class="traffic-val">${fmtBytes(iface.tx_bytes)}</span>
        </div>
        <div class="traffic-item">
          <span class="traffic-label">Пакеты</span>
          <span class="traffic-val">${iface.rx_packets ?? 0} / ${iface.tx_packets ?? 0}</span>
        </div>
      </div>
    </div>

    <div class="card-actions">
      ${!isLo ? `
      <label class="toggle-label iface-power-toggle" title="${isUp ? 'Выключить интерфейс' : 'Включить интерфейс'}">
        <input type="checkbox" ${isUp ? 'checked' : ''} data-action="toggle" data-iface="${iface.name}">
        <span class="toggle-slider"></span>
        <span class="iface-toggle-label">${isUp ? 'Вкл' : 'Выкл'}</span>
      </label>
      <button class="btn-icon" data-action="restart" data-iface="${iface.name}" title="Перезапустить">${ICON.restart}</button>
      ` : ''}
      <button class="btn-icon btn-icon-accent" data-action="config" data-iface="${iface.name}" title="Настроить" style="margin-left:auto">${ICON.pencil}</button>
    </div>

    ${!isLo ? buildVLANSection(iface.name, vlans) : ''}
  `;

  return card;
}

function buildVLANSection(parentName, vlans) {
  const rows = vlans.map(v => {
    const sc = stateClass(v.state);
    const hasPending = state.pending.includes(v.name);
    const isUp = v.state === 'up';
    const label = v.label || '';
    const ip = v.ipv4
      ? v.ipv4 + (v.ipv4_mask ? ' / ' + v.ipv4_mask : '')
      : '<span class="none">&mdash;</span>';
    return `
      <div class="vlan-row" data-name="${v.name}">
        <div class="vlan-row-left">
          <span class="state-dot ${sc}" style="width:8px;height:8px"></span>
          ${label
            ? `<div class="vlan-name-stack"><span class="vlan-label-text">${label}</span><span class="vlan-iface-name">${v.name}</span></div>`
            : `<span class="vlan-iface-name">${v.name}</span>`}
          <span class="vlan-id-tag">VLAN ${vlanId(v.name)}</span>
          <span class="mode-badge ${v.mode || 'unknown'}">${modeLabel(v.mode)}</span>
          ${hasPending ? '<span class="pending-badge">несохранено</span>' : ''}
        </div>
        <div class="vlan-row-info">${ip}</div>
        <div class="vlan-row-actions">
          <label class="toggle-label" title="${isUp ? 'Выключить' : 'Включить'}">
            <input type="checkbox" ${isUp ? 'checked' : ''} data-action="toggle" data-iface="${v.name}">
            <span class="toggle-slider toggle-sm"></span>
          </label>
          <button class="btn-icon" data-action="config" data-iface="${v.name}" title="Настроить">${ICON.pencil}</button>
          <button class="btn-icon btn-icon-danger" data-action="delete" data-iface="${v.name}" title="Удалить VLAN">${ICON.trash}</button>
        </div>
      </div>`;
  }).join('');

  const empty = vlans.length === 0
    ? `<div class="vlan-empty">Нет тегированных VLAN</div>`
    : '';

  return `
    <div class="vlan-section">
      <div class="vlan-header">
        <span class="vlan-title">VLAN</span>
        <button class="btn btn-ghost btn-xs" data-action="addvlan" data-iface="${parentName}">${ICON.plus} Добавить</button>
      </div>
      <div class="vlan-list">
        ${rows}
        ${empty}
      </div>
    </div>`;
}

function renderPendingBanner() {
  const banner = document.getElementById('pendingBanner');
  const list   = document.getElementById('pendingList');
  if (state.pending.length > 0) {
    list.textContent = state.pending.join(', ');
    banner.classList.remove('hidden');
  } else {
    banner.classList.add('hidden');
  }
}

// ── Data loading ─────────────────────────────────────────────────────────────

async function loadAll() {
  try {
    const [ifaces, pending] = await Promise.all([
      get('/api/interfaces'),
      get('/api/pending'),
    ]);
    state.interfaces = ifaces || [];
    state.pending    = pending || [];
    renderAll();
  } catch (e) {
    showToast('Ошибка загрузки: ' + e.message, 'error');
  }
}

// ── Interface actions ─────────────────────────────────────────────────────────

async function doAction(name, action) {
  if (action === 'delete') {
    if (!confirm(`Удалить VLAN ${name}?`)) return;
    try {
      await post(`/api/interfaces/${name}/delete`);
      showToast(`${name}: удалён`, 'success');
      await loadAll();
    } catch (e) {
      showToast(`${name} delete: ${e.message}`, 'error');
    }
    return;
  }

  try {
    await post(`/api/interfaces/${name}/${action}`);
    showToast(`${name}: ${action} выполнено`, 'success');
    await loadAll();
  } catch (e) {
    showToast(`${name} ${action}: ${e.message}`, 'error');
    await loadAll(); // refresh to restore correct toggle state
  }
}

// ── Config modal ──────────────────────────────────────────────────────────────

async function openConfig(name) {
  state.configModal = name;
  state.configModalParent = null;
  document.getElementById('modalTitle').textContent = `Настройка: ${name}`;

  // Show/hide VLAN ID field
  const vlanSection = document.getElementById('vlanIdSection');
  const vlanInput = document.getElementById('cfgVLANId');
  if (isVLAN(name)) {
    vlanSection.classList.remove('hidden');
    vlanInput.value = vlanId(name);
    vlanInput.readOnly = true;
  } else {
    vlanSection.classList.add('hidden');
    vlanInput.readOnly = false;
  }

  try {
    const [configData, natData] = await Promise.all([
      get(`/api/config/${name}`),
      get('/api/nat').catch(() => null),
    ]);
    if (natData) state.nat = natData;
    fillForm(configData.config, configData.pending, name, configData.label || '');
    document.getElementById('modal').classList.remove('hidden');
  } catch (e) {
    showToast('Ошибка загрузки конфига: ' + e.message, 'error');
  }
}

async function openNewVLAN(parentName) {
  state.configModal = null;
  state.configModalParent = parentName;
  document.getElementById('modalTitle').textContent = `Новый VLAN на ${parentName}`;

  const vlanSection = document.getElementById('vlanIdSection');
  const vlanInput = document.getElementById('cfgVLANId');
  vlanSection.classList.remove('hidden');
  vlanInput.readOnly = false;
  vlanInput.value = '';

  try {
    const natData = await get('/api/nat').catch(() => null);
    if (natData) state.nat = natData;
  } catch (_) {}

  fillForm({ auto: true, mode: 'static' }, false, '', '');
  document.getElementById('modal').classList.remove('hidden');
}

function fillForm(cfg, pending, name, label = '') {
  document.getElementById('cfgLabel').value   = label;
  document.getElementById('cfgAuto').checked    = !!cfg.auto;
  document.getElementById('cfgAddress').value   = cfg.address || '';
  document.getElementById('cfgNetmask').value   = cfg.netmask || '';
  document.getElementById('cfgGateway').value   = cfg.gateway || '';
  document.getElementById('cfgDNS').value       = (cfg.dns || []).join(' ');

  const mode = cfg.mode === 'static' ? 'static' : 'dhcp';
  setMode(mode);

  if (pending && name) {
    document.getElementById('modalTitle').textContent = `Настройка: ${name} (несохранённые изменения)`;
  }

  // NAT section — show for all non-loopback interfaces
  const natSection = document.getElementById('natSection');
  const natNotInstalled = document.getElementById('natNotInstalled');
  const cfgNAT = document.getElementById('cfgNAT');

  if (cfg.mode === 'loopback' || name === 'lo') {
    natSection.classList.add('hidden');
  } else {
    natSection.classList.remove('hidden');
    const natInstalled = state.nat?.installed !== false;
    cfgNAT.disabled = !natInstalled;
    natNotInstalled.classList.toggle('hidden', natInstalled);
    cfgNAT.checked = !!(state.nat?.interfaces || []).includes(name);
  }
}

function setMode(mode) {
  document.querySelectorAll('.seg-btn').forEach(b => {
    b.classList.toggle('active', b.dataset.mode === mode);
  });
  document.getElementById('staticFields').classList.toggle('hidden', mode !== 'static');
}

function currentMode() {
  return document.querySelector('.seg-btn.active')?.dataset.mode ?? 'dhcp';
}

function closeModal() {
  document.getElementById('modal').classList.add('hidden');
  document.getElementById('configForm').reset();
  state.configModal = null;
  state.configModalParent = null;
}

async function saveConfig() {
  let name = state.configModal;

  if (!name) {
    // New VLAN — build name from parent + VLAN ID
    const parent = state.configModalParent;
    const id = parseInt(document.getElementById('cfgVLANId').value);
    if (!parent) return;
    if (!id || id < 1 || id > 4094) {
      showToast('Укажите корректный VLAN ID (1–4094)', 'error');
      return;
    }
    name = `${parent}.${id}`;

    // Check for duplicate
    if (state.interfaces.find(i => i.name === name)) {
      showToast(`VLAN ${name} уже существует`, 'error');
      return;
    }
  }

  const mode = currentMode();
  const cfg = {
    name,
    label:   document.getElementById('cfgLabel').value.trim(),
    auto:    document.getElementById('cfgAuto').checked,
    mode,
    address: document.getElementById('cfgAddress').value.trim(),
    netmask: document.getElementById('cfgNetmask').value.trim(),
    gateway: document.getElementById('cfgGateway').value.trim(),
    dns:     document.getElementById('cfgDNS').value.trim().split(/\s+/).filter(Boolean),
    extra:   {},
  };

  if (mode === 'static' && !cfg.address) {
    showToast('Укажите IP-адрес', 'error');
    return;
  }

  try {
    await post(`/api/config/${name}`, cfg);

    // Save NAT setting if section is visible
    const natSection = document.getElementById('natSection');
    if (!natSection.classList.contains('hidden') && state.nat?.installed !== false) {
      const natEnabled = document.getElementById('cfgNAT').checked;
      const current = state.nat?.interfaces || [];
      const updated = natEnabled
        ? [...new Set([...current, name])]
        : current.filter(i => i !== name);
      await post('/api/nat', { interfaces: updated });
      if (state.nat) state.nat.interfaces = updated;
    }

    showToast(`${name}: настройки сохранены (ожидают применения)`, 'info');
    closeModal();
    await loadAll();
  } catch (e) {
    showToast('Ошибка сохранения: ' + e.message, 'error');
  }
}

// ── Apply / discard ───────────────────────────────────────────────────────────

async function applyAll() {
  const btn = document.getElementById('applyBtn');
  btn.disabled = true;
  btn.textContent = 'Применяю...';
  try {
    await post('/api/apply');
    showToast('Настройки применены', 'success');
    await loadAll();
  } catch (e) {
    showToast('Ошибка применения: ' + e.message, 'error');
  } finally {
    btn.disabled = false;
    btn.textContent = 'Применить';
  }
}

async function discardAll() {
  for (const name of [...state.pending]) {
    try { await del(`/api/config/${name}`); } catch (_) {}
  }
  state.pending = [];
  renderPendingBanner();
  showToast('Изменения отменены', 'info');
  await loadAll();
}

// ── Toast ─────────────────────────────────────────────────────────────────────

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

// ── Event wiring ──────────────────────────────────────────────────────────────

document.getElementById('refreshBtn').addEventListener('click', loadAll);
document.getElementById('applyBtn').addEventListener('click', applyAll);
document.getElementById('discardAllBtn').addEventListener('click', discardAll);

// Card button clicks (delegated)
document.getElementById('ifaceGrid').addEventListener('click', e => {
  const btn = e.target.closest('[data-action]');
  if (!btn) return;
  // Don't handle toggle inputs here (handled by 'change' below)
  if (btn.tagName === 'INPUT' && btn.type === 'checkbox') return;
  const { action, iface } = btn.dataset;
  if (!action || !iface) return;
  if (action === 'config') {
    openConfig(iface);
  } else if (action === 'addvlan') {
    openNewVLAN(iface);
  } else {
    doAction(iface, action);
  }
});

// Toggle switch (on/off) — delegated change event
document.getElementById('ifaceGrid').addEventListener('change', e => {
  const input = e.target.closest('input[data-action="toggle"]');
  if (!input) return;
  doAction(input.dataset.iface, input.checked ? 'up' : 'down');
});

// Modal close
document.getElementById('closeModal').addEventListener('click', closeModal);
document.getElementById('cancelConfigBtn').addEventListener('click', closeModal);
document.getElementById('modalBackdrop').addEventListener('click', closeModal);
document.addEventListener('keydown', e => {
  if (e.key === 'Escape') closeModal();
});

// Mode switcher
document.getElementById('modeSwitch').addEventListener('click', e => {
  const btn = e.target.closest('.seg-btn');
  if (btn) setMode(btn.dataset.mode);
});

// Save config
document.getElementById('saveConfigBtn').addEventListener('click', saveConfig);
document.getElementById('configForm').addEventListener('submit', e => {
  e.preventDefault();
  saveConfig();
});

// Auto-refresh every 10 seconds
setInterval(loadAll, 10000);

// ── Init ──────────────────────────────────────────────────────────────────────

(async () => {
  await loadAll();
})();
