'use strict';

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

const state = {
  rules: [],        // current rule list (order matters)
  interfaces: [],   // available interface names for autocomplete
  editIdx: -1,      // index in state.rules being edited (-1 = new)
};

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

async function api(method, path, body) {
  const res = await fetch(path, {
    method,
    headers: body ? { 'Content-Type': 'application/json' } : {},
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok || !json.success) throw new Error(json.error || `HTTP ${res.status}`);
  return json.data;
}
const get  = p      => api('GET',  p);
const post = (p, b) => api('POST', p, b);

// ── Load ─────────────────────────────────────────────────────────────────────

async function loadAll() {
  try {
    const data = await get('/api/firewall');
    state.rules      = data.rules || [];
    state.interfaces = data.interfaces || [];
    document.getElementById('vlanIsolation').checked = !!data.vlan_isolation;

    const notInstalled = document.getElementById('notInstalledBanner');
    if (!data.installed) {
      notInstalled.classList.remove('hidden');
    } else {
      notInstalled.classList.add('hidden');
    }

    // Populate datalist for interface autocomplete.
    const dl = document.getElementById('ifaceList');
    dl.innerHTML = state.interfaces.map(n => `<option value="${n}">`).join('');

    renderRules();
  } catch (e) {
    showToast('Ошибка загрузки: ' + e.message, 'error');
  }
}

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

const ACTION_LABELS = { accept: 'Разрешить', drop: 'Запретить', reject: 'Отклонить' };
const ACTION_CLASS  = { accept: 'fw-accept', drop: 'fw-drop', reject: 'fw-reject' };
const PROTO_LABELS  = { all: 'Любой', tcp: 'TCP', udp: 'UDP', icmp: 'ICMP' };

function addrPort(addr, port) {
  if (!addr && !port) return '<span class="none">любой</span>';
  const a = addr || '<span class="none">*</span>';
  const p = port ? `:<b>${port}</b>` : '';
  return a + p;
}

function renderRules() {
  const tbody = document.getElementById('rulesTbody');
  const emptyRow = document.getElementById('emptyRow');

  // Remove all rows except the empty placeholder.
  [...tbody.querySelectorAll('.fw-row')].forEach(r => r.remove());

  if (state.rules.length === 0) {
    emptyRow.classList.remove('hidden');
    return;
  }
  emptyRow.classList.add('hidden');

  state.rules.forEach((rule, idx) => {
    const tr = document.createElement('tr');
    tr.className = 'fw-row' + (rule.enabled ? '' : ' fw-row-disabled');
    tr.draggable = true;
    tr.dataset.ruleId = rule.id || String(idx);

    const inIface  = rule.in_iface  || '<span class="none">—</span>';
    const outIface = rule.out_iface || '<span class="none">—</span>';

    tr.innerHTML = `
      <td class="col-drag"><span class="drag-handle" title="Перетащить">⠿</span></td>
      <td class="col-num">${idx + 1}</td>
      <td class="col-en">
        <label class="mini-toggle" title="${rule.enabled ? 'Отключить' : 'Включить'}">
          <input type="checkbox" data-idx="${idx}" class="rule-toggle" ${rule.enabled ? 'checked' : ''}>
          <span class="mini-slider"></span>
        </label>
      </td>
      <td class="col-action"><span class="action-badge ${ACTION_CLASS[rule.action] || 'fw-drop'}">${ACTION_LABELS[rule.action] || rule.action}</span></td>
      <td class="col-proto">${PROTO_LABELS[rule.protocol] || rule.protocol || 'Любой'}</td>
      <td class="col-iface">${inIface}</td>
      <td class="col-iface">${outIface}</td>
      <td class="col-addr">${addrPort(rule.src_addr, rule.src_port)}</td>
      <td class="col-addr">${addrPort(rule.dst_addr, rule.dst_port)}</td>
      <td class="col-comment">${rule.comment ? `<span class="fw-comment">${esc(rule.comment)}</span>` : '<span class="none">—</span>'}</td>
      <td class="col-btns">
        <button class="btn btn-ghost btn-xs rule-edit" data-idx="${idx}" title="Редактировать">✎</button>
        <button class="btn btn-danger btn-xs rule-del" data-idx="${idx}" title="Удалить">✕</button>
      </td>
    `;

    addDragHandlers(tr);
    tbody.appendChild(tr);
  });
}

function esc(s) {
  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

// ── Drag-to-reorder ───────────────────────────────────────────────────────────

let dragSrc = null;

function addDragHandlers(row) {
  row.addEventListener('dragstart', e => {
    dragSrc = row;
    e.dataTransfer.effectAllowed = 'move';
    setTimeout(() => row.classList.add('dragging'), 0);
  });
  row.addEventListener('dragend', () => {
    row.classList.remove('dragging');
    document.querySelectorAll('.fw-row').forEach(r => r.classList.remove('drag-over'));
    commitDragOrder();
    dragSrc = null;
  });
  row.addEventListener('dragover', e => {
    e.preventDefault();
    if (!dragSrc || dragSrc === row) return;
    document.querySelectorAll('.fw-row').forEach(r => r.classList.remove('drag-over'));
    row.classList.add('drag-over');
  });
  row.addEventListener('drop', e => {
    e.preventDefault();
    if (!dragSrc || dragSrc === row) return;
    const tbody = row.parentNode;
    const rows  = [...tbody.querySelectorAll('.fw-row')];
    const si    = rows.indexOf(dragSrc);
    const ti    = rows.indexOf(row);
    if (si < ti) {
      tbody.insertBefore(dragSrc, row.nextSibling);
    } else {
      tbody.insertBefore(dragSrc, row);
    }
  });
}

function commitDragOrder() {
  const rows  = [...document.querySelectorAll('.fw-row')];
  const byId  = {};
  state.rules.forEach(r => { byId[r.id] = r; });

  const newRules = [];
  rows.forEach((row, i) => {
    const id = row.dataset.ruleId;
    const r  = byId[id] || state.rules[parseInt(id)];
    if (r) newRules.push(r);
  });
  state.rules = newRules;
  renderRules();
}

// ── Modal ─────────────────────────────────────────────────────────────────────

function openModal(idx) {
  state.editIdx = idx;
  const isNew = idx === -1;
  document.getElementById('ruleModalTitle').textContent = isNew ? 'Добавить правило' : 'Редактировать правило';

  const rule = isNew ? { enabled: true, action: 'accept', protocol: 'all' } : state.rules[idx];

  document.getElementById('rEnabled').checked   = rule.enabled !== false;
  document.getElementById('rComment').value     = rule.comment  || '';
  document.getElementById('rSrcAddr').value     = rule.src_addr || '';
  document.getElementById('rSrcPort').value     = rule.src_port || '';
  document.getElementById('rDstAddr').value     = rule.dst_addr || '';
  document.getElementById('rDstPort').value     = rule.dst_port || '';
  document.getElementById('rInIface').value     = rule.in_iface  || '';
  document.getElementById('rOutIface').value    = rule.out_iface || '';

  setSegmented('actionSwitch', rule.action    || 'accept');
  setSegmented('protoSwitch',  rule.protocol  || 'all');
  updatePortFields(rule.protocol || 'all');

  document.getElementById('ruleModal').classList.remove('hidden');
}

function closeModal() {
  document.getElementById('ruleModal').classList.add('hidden');
  document.getElementById('ruleForm').reset();
}

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

function getSegmented(id) {
  return document.querySelector(`#${id} .seg-btn.active`)?.dataset.val ?? '';
}

function updatePortFields(proto) {
  const show = proto === 'tcp' || proto === 'udp';
  document.getElementById('portFields').classList.toggle('hidden', !show);
}

function saveRule() {
  const rule = {
    id:        state.editIdx === -1 ? genId() : (state.rules[state.editIdx]?.id || genId()),
    enabled:   document.getElementById('rEnabled').checked,
    action:    getSegmented('actionSwitch'),
    protocol:  getSegmented('protoSwitch'),
    src_addr:  document.getElementById('rSrcAddr').value.trim(),
    src_port:  document.getElementById('rSrcPort').value.trim(),
    dst_addr:  document.getElementById('rDstAddr').value.trim(),
    dst_port:  document.getElementById('rDstPort').value.trim(),
    in_iface:  document.getElementById('rInIface').value.trim(),
    out_iface: document.getElementById('rOutIface').value.trim(),
    comment:   document.getElementById('rComment').value.trim(),
  };

  if (!rule.action) { showToast('Выберите действие', 'error'); return; }

  if (state.editIdx === -1) {
    state.rules.push(rule);
  } else {
    state.rules[state.editIdx] = rule;
  }

  closeModal();
  renderRules();
}

function genId() {
  return Math.random().toString(36).slice(2, 10);
}

// ── Save & Apply ──────────────────────────────────────────────────────────────

async function saveAndApply() {
  const btn = document.getElementById('applyBtn');
  btn.disabled = true;
  btn.textContent = 'Применяю...';

  try {
    await post('/api/firewall', {
      rules:          state.rules,
      vlan_isolation: document.getElementById('vlanIsolation').checked,
    });
    await post('/api/firewall/apply');
    showToast('Правила файрвола применены', 'success');
  } catch (e) {
    showToast('Ошибка: ' + e.message, 'error');
  } finally {
    btn.disabled = false;
    btn.textContent = 'Сохранить и применить';
  }
}

// ── 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', saveAndApply);
document.getElementById('addRuleBtn').addEventListener('click', () => openModal(-1));

// Table events (delegated)
document.getElementById('rulesTbody').addEventListener('click', e => {
  const editBtn = e.target.closest('.rule-edit');
  const delBtn  = e.target.closest('.rule-del');
  if (editBtn) { openModal(parseInt(editBtn.dataset.idx)); return; }
  if (delBtn)  {
    const idx = parseInt(delBtn.dataset.idx);
    if (confirm(`Удалить правило ${idx + 1}?`)) {
      state.rules.splice(idx, 1);
      renderRules();
    }
    return;
  }
});

document.getElementById('rulesTbody').addEventListener('change', e => {
  const toggle = e.target.closest('.rule-toggle');
  if (toggle) {
    const idx = parseInt(toggle.dataset.idx);
    state.rules[idx].enabled = toggle.checked;
    renderRules();
  }
});

// Modal
document.getElementById('closeRuleModal').addEventListener('click', closeModal);
document.getElementById('cancelRuleBtn').addEventListener('click', closeModal);
document.getElementById('ruleModalBackdrop').addEventListener('click', closeModal);
document.getElementById('saveRuleBtn').addEventListener('click', saveRule);
document.getElementById('ruleForm').addEventListener('submit', e => { e.preventDefault(); saveRule(); });
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); });

// Segmented switchers
document.getElementById('actionSwitch').addEventListener('click', e => {
  const b = e.target.closest('.seg-btn');
  if (b) setSegmented('actionSwitch', b.dataset.val);
});
document.getElementById('protoSwitch').addEventListener('click', e => {
  const b = e.target.closest('.seg-btn');
  if (b) { setSegmented('protoSwitch', b.dataset.val); updatePortFields(b.dataset.val); }
});

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

loadAll();
